diff --git a/account_bank_statement_import_online_transferwise/README.rst b/account_bank_statement_import_online_transferwise/README.rst
new file mode 100644
index 0000000..f2e61be
--- /dev/null
+++ b/account_bank_statement_import_online_transferwise/README.rst
@@ -0,0 +1,109 @@
+========================================
+Online Bank Statements: TransferWise.com
+========================================
+
+.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! This file is generated by oca-gen-addon-readme !!
+ !! changes will be overwritten. !!
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
+ :target: https://odoo-community.org/page/development-status
+ :alt: Beta
+.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
+ :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
+ :alt: License: AGPL-3
+.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fbank--statement--import-lightgray.png?logo=github
+ :target: https://github.com/OCA/bank-statement-import/tree/12.0/account_bank_statement_import_online_transferwise
+ :alt: OCA/bank-statement-import
+.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
+ :target: https://translation.odoo-community.org/projects/bank-statement-import-12-0/bank-statement-import-12-0-account_bank_statement_import_online_transferwise
+ :alt: Translate me on Weblate
+.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
+ :target: https://runbot.odoo-community.org/runbot/174/12.0
+ :alt: Try me on Runbot
+
+|badge1| |badge2| |badge3| |badge4| |badge5|
+
+This module provides online bank statements from
+`TransferWise.com `_.
+
+**Table of contents**
+
+.. contents::
+ :local:
+
+Configuration
+=============
+
+To configure online bank statements provider:
+
+#. Go to *Invoicing > Configuration > Bank Accounts*
+#. Open bank account to configure and edit it
+#. Set *Bank Feeds* to *Online*
+#. Select *TransferWise.com* as online bank statements provider in
+ *Online Bank Statements (OCA)* section
+#. Save the bank account
+#. Click on provider and configure provider-specific settings.
+
+or, alternatively:
+
+#. Go to *Invoicing > Overview*
+#. Open settings of the corresponding journal account
+#. Switch to *Bank Account* tab
+#. Set *Bank Feeds* to *Online*
+#. Select *TransferWise.com* as online bank statements provider in
+ *Online Bank Statements (OCA)* section
+#. Save the bank account
+#. Click on provider and configure provider-specific settings.
+
+Usage
+=====
+
+To pull historical bank statements:
+
+#. Go to *Invoicing > Configuration > Bank Accounts*
+#. Select specific bank accounts
+#. Launch *Actions > Online Bank Statements Pull Wizard*
+#. Configure date interval and click *Pull*
+
+Bug Tracker
+===========
+
+Bugs are tracked on `GitHub Issues `_.
+In case of trouble, please check there if your issue has already been reported.
+If you spotted it first, help us smashing it by providing a detailed and welcomed
+`feedback `_.
+
+Do not contact contributors directly about support or help with technical issues.
+
+Credits
+=======
+
+Authors
+~~~~~~~
+
+* Brainbean Apps
+* Dataplug
+
+Contributors
+~~~~~~~~~~~~
+
+* Alexey Pelykh
+
+Maintainers
+~~~~~~~~~~~
+
+This module is maintained by the OCA.
+
+.. image:: https://odoo-community.org/logo.png
+ :alt: Odoo Community Association
+ :target: https://odoo-community.org
+
+OCA, or the Odoo Community Association, is a nonprofit organization whose
+mission is to support the collaborative development of Odoo features and
+promote its widespread use.
+
+This module is part of the `OCA/bank-statement-import `_ project on GitHub.
+
+You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/account_bank_statement_import_online_transferwise/__init__.py b/account_bank_statement_import_online_transferwise/__init__.py
new file mode 100644
index 0000000..31660d6
--- /dev/null
+++ b/account_bank_statement_import_online_transferwise/__init__.py
@@ -0,0 +1,3 @@
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+
+from . import models
diff --git a/account_bank_statement_import_online_transferwise/__manifest__.py b/account_bank_statement_import_online_transferwise/__manifest__.py
new file mode 100644
index 0000000..9b7b66f
--- /dev/null
+++ b/account_bank_statement_import_online_transferwise/__manifest__.py
@@ -0,0 +1,24 @@
+# Copyright 2019 Brainbean Apps (https://brainbeanapps.com)
+# Copyright 2019 Dataplug (https://dataplug.io)
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+
+{
+ 'name': 'Online Bank Statements: TransferWise.com',
+ 'version': '12.0.1.0.0',
+ 'author':
+ 'Brainbean Apps, '
+ 'Dataplug, '
+ 'Odoo Community Association (OCA)',
+ 'website': 'https://github.com/OCA/bank-statement-import/',
+ 'license': 'AGPL-3',
+ 'category': 'Accounting',
+ 'summary': 'Online bank statements for TransferWise.com',
+ 'depends': [
+ 'account_bank_statement_import_online',
+ 'web_widget_dropdown_dynamic',
+ ],
+ 'data': [
+ 'views/online_bank_statement_provider.xml',
+ ],
+ 'installable': True,
+}
diff --git a/account_bank_statement_import_online_transferwise/models/__init__.py b/account_bank_statement_import_online_transferwise/models/__init__.py
new file mode 100644
index 0000000..3a96b70
--- /dev/null
+++ b/account_bank_statement_import_online_transferwise/models/__init__.py
@@ -0,0 +1,3 @@
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+
+from . import online_bank_statement_provider_transferwise
diff --git a/account_bank_statement_import_online_transferwise/models/online_bank_statement_provider_transferwise.py b/account_bank_statement_import_online_transferwise/models/online_bank_statement_provider_transferwise.py
new file mode 100644
index 0000000..f3e53a0
--- /dev/null
+++ b/account_bank_statement_import_online_transferwise/models/online_bank_statement_provider_transferwise.py
@@ -0,0 +1,278 @@
+# Copyright 2019 Brainbean Apps (https://brainbeanapps.com)
+# Copyright 2019 Dataplug (https://dataplug.io)
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
+
+from dateutil.relativedelta import relativedelta
+import dateutil.parser
+from decimal import Decimal
+import itertools
+import json
+import pytz
+import urllib.parse
+import urllib.request
+
+from odoo import models, api, _
+from odoo.exceptions import UserError
+
+TRANSFERWISE_API_BASE = 'https://api.transferwise.com'
+
+
+class OnlineBankStatementProviderTransferwise(models.Model):
+ _inherit = 'online.bank.statement.provider'
+
+ @api.model
+ def values_transferwise_profile(self):
+ api_base = self.env.context.get('api_base') or TRANSFERWISE_API_BASE
+ api_key = self.env.context.get('api_key')
+ if not api_key:
+ return []
+ try:
+ url = api_base + '/v1/profiles'
+ data = self._transferwise_retrieve(url, api_key)
+ except:
+ return []
+ return list(map(
+ lambda entry: (
+ str(entry['id']),
+ '%s %s (personal)' % (
+ entry['details']['firstName'],
+ entry['details']['lastName'],
+ )
+ if entry['type'] == 'personal'
+ else entry['details']['name']
+ ),
+ data
+ ))
+
+ @api.model
+ def _get_available_services(self):
+ return super()._get_available_services() + [
+ ('transferwise', 'TransferWise.com'),
+ ]
+
+ @api.multi
+ def _obtain_statement_data(self, date_since, date_until):
+ self.ensure_one()
+ if self.service != 'transferwise':
+ return super()._obtain_statement_data(
+ date_since,
+ date_until,
+ ) # pragma: no cover
+
+ api_base = self.api_base or TRANSFERWISE_API_BASE
+ api_key = self.password
+ currency = (
+ self.currency_id or self.company_id.currency_id
+ ).name
+
+ if date_since.tzinfo:
+ date_since = date_since.astimezone(pytz.utc).replace(tzinfo=None)
+ if date_until.tzinfo:
+ date_until = date_until.astimezone(pytz.utc).replace(tzinfo=None)
+
+ # Get corresponding balance by currency
+ url = api_base + '/v1/borderless-accounts?profileId=%s' % (
+ self.origin,
+ )
+ data = self._transferwise_retrieve(url, api_key)
+ borderless_account = data[0]['id']
+ balance = list(filter(
+ lambda balance: balance['currency'] == currency,
+ data[0]['balances']
+ ))
+ if not balance:
+ return None
+
+ # Notes on /statement endpoint:
+ # - intervalStart <= date < intervalEnd
+
+ # Get starting balance
+ starting_balance_timestamp = date_since.isoformat() + 'Z'
+ url = api_base + (
+ '/v1/borderless-accounts/%s/statement.json' +
+ '?currency=%s&intervalStart=%s&intervalEnd=%s'
+ ) % (
+ borderless_account,
+ currency,
+ starting_balance_timestamp,
+ starting_balance_timestamp,
+ )
+ data = self._transferwise_retrieve(url, api_key)
+ balance_start = data['endOfStatementBalance']['value']
+
+ # Get statements, using 469 days (around 1 year 3 month) as step.
+ interval_step = relativedelta(days=469)
+ interval_start = date_since
+ interval_end = date_until
+ transactions = []
+ balance_end = None
+ while interval_start < interval_end:
+ url = api_base + (
+ '/v1/borderless-accounts/%s/statement.json' +
+ '?currency=%s&intervalStart=%s&intervalEnd=%s'
+ ) % (
+ borderless_account,
+ currency,
+ interval_start.isoformat() + 'Z',
+ min(
+ interval_start + interval_step, interval_end
+ ).isoformat() + 'Z',
+ )
+ data = self._transferwise_retrieve(url, api_key)
+ transactions += data['transactions']
+ balance_end = data['endOfStatementBalance']['value']
+ interval_start += interval_step
+ if balance_end is None:
+ raise UserError(_('Ending balance unavailable'))
+
+ # Normalize transactions' date, sort by it, and get lines
+ transactions = map(
+ lambda transaction: self._transferwise_preparse_transaction(
+ transaction
+ ),
+ transactions
+ )
+ lines = list(itertools.chain.from_iterable(map(
+ lambda x: self._transferwise_transaction_to_lines(x),
+ sorted(
+ transactions,
+ key=lambda transaction: transaction['date']
+ )
+ )))
+
+ return lines, {
+ 'balance_start': balance_start,
+ 'balance_end_real': balance_end,
+ }
+
+ @api.model
+ def _transferwise_preparse_transaction(self, transaction):
+ transaction['date'] = dateutil.parser.parse(
+ transaction['date']
+ ).replace(tzinfo=None)
+ return transaction
+
+ @api.model
+ def _transferwise_transaction_to_lines(self, transaction):
+ reference_number = transaction['referenceNumber']
+ details = transaction.get('details', {})
+ exchange_details = transaction.get('exchangeDetails')
+ recipient = details.get('recipient')
+ total_fees = transaction.get('totalFees')
+ date = transaction['date']
+ payment_reference = details.get('paymentReference')
+ description = details.get('description')
+ note = reference_number
+ if description:
+ note = '%s: %s' % (
+ note,
+ description
+ )
+ amount = transaction['amount']
+ amount_value = amount.get('value', 0)
+ fees_value = total_fees.get('value', Decimal()).copy_abs()
+ if amount_value.is_signed():
+ fees_value = fees_value.copy_negate()
+ amount_value -= fees_value
+ unique_import_id = '%s-%s-%s' % (
+ transaction['type'],
+ reference_number,
+ int(date.timestamp()),
+ )
+ line = {
+ 'name': payment_reference or description or '',
+ 'amount': str(amount_value),
+ 'date': date,
+ 'note': note,
+ 'unique_import_id': unique_import_id,
+ }
+ if recipient:
+ if 'name' in recipient:
+ line.update({
+ 'partner_name': recipient['name'],
+ })
+ if 'bankAccount' in recipient:
+ line.update({
+ 'account_number': recipient['bankAccount'],
+ })
+ elif 'merchant' in details:
+ merchant = details['merchant']
+ if 'name' in merchant:
+ line.update({
+ 'partner_name': merchant['name'],
+ })
+ else:
+ if 'senderName' in details:
+ line.update({
+ 'partner_name': details['senderName'],
+ })
+ if 'senderAccount' in details:
+ line.update({
+ 'account_number': details['senderAccount'],
+ })
+ if exchange_details:
+ to_amount = exchange_details['toAmount']
+ from_amount = exchange_details['fromAmount']
+ other_amount_value = (
+ to_amount['value']
+ if to_amount['currency'] != amount['currency']
+ else from_amount['value']
+ )
+ other_currency_name = (
+ to_amount['currency']
+ if to_amount['currency'] != amount['currency']
+ else from_amount['currency']
+ )
+ other_amount_value = other_amount_value.copy_abs()
+ if amount_value.is_signed():
+ other_amount_value = other_amount_value.copy_negate()
+ other_currency = self.env['res.currency'].search(
+ [('name', '=', other_currency_name)],
+ limit=1
+ )
+ if other_amount_value and other_currency:
+ line.update({
+ 'amount_currency': str(other_amount_value),
+ 'currency_id': other_currency.id,
+ })
+ lines = [line]
+ if fees_value:
+ lines += [{
+ 'name': _('Fee for %s') % reference_number,
+ 'amount': str(fees_value),
+ 'date': date,
+ 'partner_name': 'TransferWise',
+ 'unique_import_id': '%s-FEE' % unique_import_id,
+ 'note': _('Transaction fee for %s') % reference_number,
+ }]
+ return lines
+
+ @api.model
+ def _transferwise_validate(self, content):
+ content = json.loads(content, parse_float=Decimal)
+ if 'error' in content and content['error']:
+ raise UserError(
+ content['error_description']
+ if 'error_description' in content
+ else 'Unknown error'
+ )
+ return content
+
+ @api.model
+ def _transferwise_retrieve(self, url, api_key):
+ with self._transferwise_urlopen(url, api_key) as response:
+ content = response.read().decode(
+ response.headers.get_content_charset()
+ )
+ return self._transferwise_validate(content)
+
+ @api.model
+ def _transferwise_urlopen(self, url, api_key):
+ if not api_key:
+ raise UserError(_('No API key specified!'))
+ request = urllib.request.Request(url)
+ request.add_header(
+ 'Authorization',
+ 'Bearer %s' % api_key
+ )
+ return urllib.request.urlopen(request)
diff --git a/account_bank_statement_import_online_transferwise/readme/CONFIGURE.rst b/account_bank_statement_import_online_transferwise/readme/CONFIGURE.rst
new file mode 100644
index 0000000..0a35ffe
--- /dev/null
+++ b/account_bank_statement_import_online_transferwise/readme/CONFIGURE.rst
@@ -0,0 +1,20 @@
+To configure online bank statements provider:
+
+#. Go to *Invoicing > Configuration > Bank Accounts*
+#. Open bank account to configure and edit it
+#. Set *Bank Feeds* to *Online*
+#. Select *TransferWise.com* as online bank statements provider in
+ *Online Bank Statements (OCA)* section
+#. Save the bank account
+#. Click on provider and configure provider-specific settings.
+
+or, alternatively:
+
+#. Go to *Invoicing > Overview*
+#. Open settings of the corresponding journal account
+#. Switch to *Bank Account* tab
+#. Set *Bank Feeds* to *Online*
+#. Select *TransferWise.com* as online bank statements provider in
+ *Online Bank Statements (OCA)* section
+#. Save the bank account
+#. Click on provider and configure provider-specific settings.
diff --git a/account_bank_statement_import_online_transferwise/readme/CONTRIBUTORS.rst b/account_bank_statement_import_online_transferwise/readme/CONTRIBUTORS.rst
new file mode 100644
index 0000000..1c6a35a
--- /dev/null
+++ b/account_bank_statement_import_online_transferwise/readme/CONTRIBUTORS.rst
@@ -0,0 +1 @@
+* Alexey Pelykh
diff --git a/account_bank_statement_import_online_transferwise/readme/DESCRIPTION.rst b/account_bank_statement_import_online_transferwise/readme/DESCRIPTION.rst
new file mode 100644
index 0000000..1f37905
--- /dev/null
+++ b/account_bank_statement_import_online_transferwise/readme/DESCRIPTION.rst
@@ -0,0 +1,2 @@
+This module provides online bank statements from
+`TransferWise.com `_.
diff --git a/account_bank_statement_import_online_transferwise/readme/USAGE.rst b/account_bank_statement_import_online_transferwise/readme/USAGE.rst
new file mode 100644
index 0000000..03845f1
--- /dev/null
+++ b/account_bank_statement_import_online_transferwise/readme/USAGE.rst
@@ -0,0 +1,6 @@
+To pull historical bank statements:
+
+#. Go to *Invoicing > Configuration > Bank Accounts*
+#. Select specific bank accounts
+#. Launch *Actions > Online Bank Statements Pull Wizard*
+#. Configure date interval and click *Pull*
diff --git a/account_bank_statement_import_online_transferwise/tests/__init__.py b/account_bank_statement_import_online_transferwise/tests/__init__.py
new file mode 100644
index 0000000..130e465
--- /dev/null
+++ b/account_bank_statement_import_online_transferwise/tests/__init__.py
@@ -0,0 +1,3 @@
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+
+from . import test_account_bank_statement_import_online_transferwise
diff --git a/account_bank_statement_import_online_transferwise/tests/test_account_bank_statement_import_online_transferwise.py b/account_bank_statement_import_online_transferwise/tests/test_account_bank_statement_import_online_transferwise.py
new file mode 100644
index 0000000..1e65cf5
--- /dev/null
+++ b/account_bank_statement_import_online_transferwise/tests/test_account_bank_statement_import_online_transferwise.py
@@ -0,0 +1,552 @@
+# Copyright 2019 Brainbean Apps (https://brainbeanapps.com)
+# Copyright 2019 Dataplug (https://dataplug.io)
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+
+from datetime import datetime
+from dateutil.relativedelta import relativedelta
+from decimal import Decimal
+import json
+from unittest import mock
+
+from odoo.tests import common
+from odoo import fields
+
+_module_ns = 'odoo.addons.account_bank_statement_import_online_transferwise'
+_provider_class = (
+ _module_ns
+ + '.models.online_bank_statement_provider_transferwise'
+ + '.OnlineBankStatementProviderTransferwise'
+)
+
+
+class TestAccountBankAccountStatementImportOnlineTransferwise(
+ common.TransactionCase
+):
+
+ def setUp(self):
+ super().setUp()
+
+ self.now = fields.Datetime.now()
+ self.currency_eur = self.env.ref('base.EUR')
+ self.currency_usd = self.env.ref('base.USD')
+ self.AccountJournal = self.env['account.journal']
+ self.OnlineBankStatementProvider = self.env[
+ 'online.bank.statement.provider'
+ ]
+ self.AccountBankStatement = self.env['account.bank.statement']
+ self.AccountBankStatementLine = self.env['account.bank.statement.line']
+
+ Provider = self.OnlineBankStatementProvider
+ self.transferwise_parse_transaction = lambda payload: (
+ Provider._transferwise_transaction_to_lines(
+ Provider._transferwise_preparse_transaction(
+ json.loads(
+ payload,
+ parse_float=Decimal,
+ )
+ )
+ )
+ )
+
+ def test_values_transferwise_profile(self):
+ mocked_response = json.loads(
+ """[
+ {
+ "id": 1234567890,
+ "type": "personal",
+ "details": {
+ "firstName": "Alexey",
+ "lastName": "Pelykh"
+ }
+ },
+ {
+ "id": 1234567891,
+ "type": "business",
+ "details": {
+ "name": "Brainbean Apps OÜ"
+ }
+ }
+]""", parse_float=Decimal)
+ values_transferwise_profile = []
+ with mock.patch(
+ _provider_class + '._transferwise_retrieve',
+ return_value=mocked_response,
+ ):
+ values_transferwise_profile = (
+ self.OnlineBankStatementProvider.with_context({
+ 'api_base': 'https://example.com',
+ 'api_key': 'dummy',
+ }).values_transferwise_profile()
+ )
+ self.assertEqual(
+ values_transferwise_profile,
+ [
+ ('1234567890', 'Alexey Pelykh (personal)'),
+ ('1234567891', 'Brainbean Apps OÜ'),
+ ]
+ )
+
+ def test_pull(self):
+ journal = self.AccountJournal.create({
+ 'name': 'Bank',
+ 'type': 'bank',
+ 'code': 'BANK',
+ 'currency_id': self.currency_eur.id,
+ 'bank_statements_source': 'online',
+ 'online_bank_statement_provider': 'transferwise',
+ })
+
+ provider = journal.online_bank_statement_provider_id
+ provider.origin = '1234567891'
+
+ def mock_response(url, api_key):
+ if '/borderless-accounts?profileId=1234567891' in url:
+ payload = """[
+ {
+ "id": 42,
+ "balances": [
+ {
+ "currency": "EUR"
+ }
+ ]
+ }
+]"""
+ elif '/borderless-accounts/42/statement.json' in url:
+ payload = """{
+ "transactions": [],
+ "endOfStatementBalance": {
+ "value": 42.00,
+ "currency": "EUR"
+ }
+}"""
+ return json.loads(payload, parse_float=Decimal)
+ with mock.patch(
+ _provider_class + '._transferwise_retrieve',
+ side_effect=mock_response,
+ ):
+ data = provider._obtain_statement_data(
+ self.now - relativedelta(hours=1),
+ self.now,
+ )
+
+ self.assertEqual(len(data[0]), 0)
+ self.assertEqual(data[1]['balance_start'], 42.0)
+ self.assertEqual(data[1]['balance_end_real'], 42.0)
+
+ def test_transaction_parse_1(self):
+ lines = self.transferwise_parse_transaction("""{
+ "type": "CREDIT",
+ "date": "2000-01-01T00:00:00.000Z",
+ "amount": {
+ "value": 0.42,
+ "currency": "EUR"
+ },
+ "totalFees": {
+ "value": 0.00,
+ "currency": "EUR"
+ },
+ "details": {
+ "type": "DEPOSIT",
+ "description": "Received money from SENDER with reference REF-XYZ",
+ "senderName": "SENDER",
+ "senderAccount": "XX00 0000 0000 0000",
+ "paymentReference": "REF-XYZ"
+ },
+ "exchangeDetails": null,
+ "runningBalance": {
+ "value": 0.42,
+ "currency": "EUR"
+ },
+ "referenceNumber": "TRANSFER-123456789"
+}""")
+ self.assertEqual(len(lines), 1)
+ self.assertEqual(lines[0], {
+ 'date': datetime(2000, 1, 1),
+ 'amount': '0.42',
+ 'name': 'REF-XYZ',
+ 'note': (
+ 'TRANSFER-123456789: Received money from SENDER with reference'
+ ' REF-XYZ'
+ ),
+ 'partner_name': 'SENDER',
+ 'account_number': 'XX00 0000 0000 0000',
+ 'unique_import_id': 'CREDIT-TRANSFER-123456789-946684800',
+ })
+
+ def test_transaction_parse_2(self):
+ lines = self.transferwise_parse_transaction("""{
+ "type": "DEBIT",
+ "date": "2000-01-01T00:00:00.000Z",
+ "amount": {
+ "value": -200.60,
+ "currency": "EUR"
+ },
+ "totalFees": {
+ "value": 0.60,
+ "currency": "EUR"
+ },
+ "details": {
+ "type": "TRANSFER",
+ "description": "Sent money to John Doe",
+ "recipient": {
+ "name": "John Doe",
+ "bankAccount": "XX00 0000 0000 0000"
+ },
+ "paymentReference": "INVOICE 42-01"
+ },
+ "exchangeDetails": null,
+ "runningBalance": {
+ "value": 100.42,
+ "currency": "EUR"
+ },
+ "referenceNumber": "TRANSFER-123456789"
+}""")
+ self.assertEqual(len(lines), 2)
+ self.assertEqual(lines[0], {
+ 'date': datetime(2000, 1, 1),
+ 'amount': '-200.00',
+ 'name': 'INVOICE 42-01',
+ 'note': 'TRANSFER-123456789: Sent money to John Doe',
+ 'partner_name': 'John Doe',
+ 'account_number': 'XX00 0000 0000 0000',
+ 'unique_import_id': 'DEBIT-TRANSFER-123456789-946684800',
+ })
+ self.assertEqual(lines[1], {
+ 'date': datetime(2000, 1, 1),
+ 'amount': '-0.60',
+ 'name': 'Fee for TRANSFER-123456789',
+ 'note': 'Transaction fee for TRANSFER-123456789',
+ 'partner_name': 'TransferWise',
+ 'unique_import_id': 'DEBIT-TRANSFER-123456789-946684800-FEE',
+ })
+
+ def test_transaction_parse_3(self):
+ lines = self.transferwise_parse_transaction("""{
+ "type": "DEBIT",
+ "date": "2000-01-01T00:00:00.000Z",
+ "amount": {
+ "value": -123.45,
+ "currency": "USD"
+ },
+ "totalFees": {
+ "value": 0.00,
+ "currency": "USD"
+ },
+ "details": {
+ "type": "CARD",
+ "description":
+ "Card transaction of 1234.56 USD issued by Paypal *XX CITY",
+ "amount": {
+ "value": 1234.56,
+ "currency": "USD"
+ },
+ "category": "Professional Services not elsewh",
+ "merchant": {
+ "name": "Paypal *XX",
+ "firstLine": null,
+ "postCode": "12345",
+ "city": "CITY",
+ "state": null,
+ "country": "GB",
+ "category": "Professional Services not elsewh"
+ }
+ },
+ "exchangeDetails": null,
+ "runningBalance": {
+ "value": 0.00,
+ "currency": "USD"
+ },
+ "referenceNumber": "CARD-123456789"
+}""")
+ self.assertEqual(len(lines), 1)
+ self.assertEqual(lines[0], {
+ 'date': datetime(2000, 1, 1),
+ 'amount': '-123.45',
+ 'name': (
+ 'Card transaction of 1234.56 USD issued by Paypal *XX CITY'
+ ),
+ 'note': (
+ 'CARD-123456789: Card transaction of 1234.56 USD issued by '
+ 'Paypal *XX CITY'
+ ),
+ 'partner_name': 'Paypal *XX',
+ 'unique_import_id': 'DEBIT-CARD-123456789-946684800',
+ })
+
+ def test_transaction_parse_4(self):
+ lines = self.transferwise_parse_transaction("""{
+ "type": "DEBIT",
+ "date": "2000-01-01T00:00:00.000Z",
+ "amount": {
+ "value": -456.78,
+ "currency": "EUR"
+ },
+ "totalFees": {
+ "value": 1.23,
+ "currency": "EUR"
+ },
+ "details": {
+ "type": "CARD",
+ "description":
+ "Card transaction of 1234.56 USD issued by Paypal *XX CITY",
+ "amount": {
+ "value": 1234.56,
+ "currency": "USD"
+ },
+ "category": "Professional Services not elsewh",
+ "merchant": {
+ "name": "Paypal *XX",
+ "firstLine": null,
+ "postCode": "12345",
+ "city": "CITY",
+ "state": null,
+ "country": "GB",
+ "category": "Professional Services not elsewh"
+ }
+ },
+ "exchangeDetails": {
+ "toAmount": {
+ "value": 567.89,
+ "currency": "USD"
+ },
+ "fromAmount": {
+ "value": 456.78,
+ "currency": "EUR"
+ },
+ "rate": 1.12260
+ },
+ "runningBalance": {
+ "value": 0.00,
+ "currency": "EUR"
+ },
+ "referenceNumber": "CARD-123456789"
+}""")
+ self.assertEqual(len(lines), 2)
+ self.assertEqual(lines[0], {
+ 'date': datetime(2000, 1, 1),
+ 'amount': '-455.55',
+ 'name': (
+ 'Card transaction of 1234.56 USD issued by Paypal *XX CITY'
+ ),
+ 'note': (
+ 'CARD-123456789: Card transaction of 1234.56 USD issued by'
+ ' Paypal *XX CITY'
+ ),
+ 'partner_name': 'Paypal *XX',
+ 'unique_import_id': 'DEBIT-CARD-123456789-946684800',
+ 'amount_currency': '-567.89',
+ 'currency_id': self.currency_usd.id,
+ })
+ self.assertEqual(lines[1], {
+ 'date': datetime(2000, 1, 1),
+ 'amount': '-1.23',
+ 'name': 'Fee for CARD-123456789',
+ 'note': 'Transaction fee for CARD-123456789',
+ 'partner_name': 'TransferWise',
+ 'unique_import_id': 'DEBIT-CARD-123456789-946684800-FEE',
+ })
+
+ def test_transaction_parse_5(self):
+ lines = self.transferwise_parse_transaction("""{
+ "type": "DEBIT",
+ "date": "2000-01-01T00:00:00.000Z",
+ "amount": {
+ "value": -270.55,
+ "currency": "EUR"
+ },
+ "totalFees": {
+ "value": 5.21,
+ "currency": "EUR"
+ },
+ "details": {
+ "type": "TRANSFER",
+ "description": "Sent money to Jane Doe",
+ "recipient": {
+ "name": "Jane Doe",
+ "bankAccount": "(ADBCDEF) 0000000000000000"
+ },
+ "paymentReference": "Invoice A from DD MMM YYYY"
+ },
+ "exchangeDetails": {
+ "toAmount": {
+ "value": 297.00,
+ "currency": "USD"
+ },
+ "fromAmount": {
+ "value": 265.34,
+ "currency": "EUR"
+ },
+ "rate": 1.11930
+ },
+ "runningBalance": {
+ "value": 2360.43,
+ "currency": "EUR"
+ },
+ "referenceNumber": "TRANSFER-123456789"
+}""")
+ self.assertEqual(len(lines), 2)
+ self.assertEqual(lines[0], {
+ 'date': datetime(2000, 1, 1),
+ 'name': 'Invoice A from DD MMM YYYY',
+ 'note': 'TRANSFER-123456789: Sent money to Jane Doe',
+ 'partner_name': 'Jane Doe',
+ 'account_number': '(ADBCDEF) 0000000000000000',
+ 'amount': '-265.34',
+ 'amount_currency': '-297.00',
+ 'currency_id': self.currency_usd.id,
+ 'unique_import_id': 'DEBIT-TRANSFER-123456789-946684800',
+ })
+ self.assertEqual(lines[1], {
+ 'date': datetime(2000, 1, 1),
+ 'name': 'Fee for TRANSFER-123456789',
+ 'note': 'Transaction fee for TRANSFER-123456789',
+ 'partner_name': 'TransferWise',
+ 'amount': '-5.21',
+ 'unique_import_id': 'DEBIT-TRANSFER-123456789-946684800-FEE',
+ })
+
+ def test_transaction_parse_6(self):
+ lines = self.transferwise_parse_transaction("""{
+ "type": "CREDIT",
+ "date": "2000-01-01T00:00:00.000Z",
+ "amount": {
+ "value": 5000.00,
+ "currency": "EUR"
+ },
+ "totalFees": {
+ "value": 0.00,
+ "currency": "EUR"
+ },
+ "details": {
+ "type": "MONEY_ADDED",
+ "description": "Topped up balance"
+ },
+ "exchangeDetails": null,
+ "runningBalance": {
+ "value": 7071.13,
+ "currency": "EUR"
+ },
+ "referenceNumber": "TRANSFER-123456789"
+}""")
+ self.assertEqual(len(lines), 1)
+ self.assertEqual(lines[0], {
+ 'date': datetime(2000, 1, 1),
+ 'name': 'Topped up balance',
+ 'note': 'TRANSFER-123456789: Topped up balance',
+ 'amount': '5000.00',
+ 'unique_import_id': 'CREDIT-TRANSFER-123456789-946684800',
+ })
+
+ def test_transaction_parse_7(self):
+ lines = self.transferwise_parse_transaction("""{
+ "type": "CREDIT",
+ "date": "2000-01-01T00:00:00.000Z",
+ "amount": {
+ "value": 6.93,
+ "currency": "EUR"
+ },
+ "totalFees": {
+ "value": 0.00,
+ "currency": "EUR"
+ },
+ "details": {
+ "type": "CONVERSION",
+ "description": "Converted 7.93 USD to 6.93 EUR",
+ "sourceAmount": {
+ "value": 7.93,
+ "currency": "USD"
+ },
+ "targetAmount": {
+ "value": 6.93,
+ "currency": "EUR"
+ },
+ "rate": 0.87944162
+ },
+ "exchangeDetails": {
+ "toAmount": {
+ "value": 6.93,
+ "currency": "EUR"
+ },
+ "fromAmount": {
+ "value": 7.93,
+ "currency": "USD"
+ },
+ "rate": 0.87944
+ },
+ "runningBalance": {
+ "value": 255.00,
+ "currency": "EUR"
+ },
+ "referenceNumber": "BALANCE-123456789"
+}""")
+ self.assertEqual(len(lines), 1)
+ self.assertEqual(lines[0], {
+ 'date': datetime(2000, 1, 1),
+ 'name': 'Converted 7.93 USD to 6.93 EUR',
+ 'note': 'BALANCE-123456789: Converted 7.93 USD to 6.93 EUR',
+ 'amount': '6.93',
+ 'amount_currency': '7.93',
+ 'currency_id': self.currency_usd.id,
+ 'unique_import_id': 'CREDIT-BALANCE-123456789-946684800',
+ })
+
+ def test_transaction_parse_8(self):
+ lines = self.transferwise_parse_transaction("""{
+ "type": "DEBIT",
+ "date": "2000-01-01T00:00:00.000Z",
+ "amount": {
+ "value": -7.93,
+ "currency": "USD"
+ },
+ "totalFees": {
+ "value": 0.05,
+ "currency": "USD"
+ },
+ "details": {
+ "type": "CONVERSION",
+ "description": "Converted 7.93 USD to 6.93 EUR",
+ "sourceAmount": {
+ "value": 7.93,
+ "currency": "USD"
+ },
+ "targetAmount": {
+ "value": 6.93,
+ "currency": "EUR"
+ },
+ "rate": 0.87944162
+ },
+ "exchangeDetails": {
+ "toAmount": {
+ "value": 6.93,
+ "currency": "EUR"
+ },
+ "fromAmount": {
+ "value": 7.93,
+ "currency": "USD"
+ },
+ "rate": 0.87944
+ },
+ "runningBalance": {
+ "value": 0.00,
+ "currency": "USD"
+ },
+ "referenceNumber": "BALANCE-123456789"
+}""")
+ self.assertEqual(len(lines), 2)
+ self.assertEqual(lines[0], {
+ 'date': datetime(2000, 1, 1),
+ 'name': 'Converted 7.93 USD to 6.93 EUR',
+ 'note': 'BALANCE-123456789: Converted 7.93 USD to 6.93 EUR',
+ 'amount': '-7.88',
+ 'amount_currency': '-6.93',
+ 'currency_id': self.currency_eur.id,
+ 'unique_import_id': 'DEBIT-BALANCE-123456789-946684800',
+ })
+ self.assertEqual(lines[1], {
+ 'date': datetime(2000, 1, 1),
+ 'name': 'Fee for BALANCE-123456789',
+ 'note': 'Transaction fee for BALANCE-123456789',
+ 'amount': '-0.05',
+ 'partner_name': 'TransferWise',
+ 'unique_import_id': 'DEBIT-BALANCE-123456789-946684800-FEE',
+ })
diff --git a/account_bank_statement_import_online_transferwise/views/online_bank_statement_provider.xml b/account_bank_statement_import_online_transferwise/views/online_bank_statement_provider.xml
new file mode 100644
index 0000000..2583a9f
--- /dev/null
+++ b/account_bank_statement_import_online_transferwise/views/online_bank_statement_provider.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+ online.bank.statement.provider.form
+ online.bank.statement.provider
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+