Browse Source

[IMP] account_bank_statement_import_online_transferwise: support SCA

12.0
Alexey Pelykh 3 years ago
parent
commit
f088b775b5
  1. 9
      account_bank_statement_import_online_transferwise/__manifest__.py
  2. 132
      account_bank_statement_import_online_transferwise/models/online_bank_statement_provider_transferwise.py
  3. 4
      account_bank_statement_import_online_transferwise/readme/CONFIGURE.rst
  4. 3
      account_bank_statement_import_online_transferwise/readme/DESCRIPTION.rst
  5. 6
      account_bank_statement_import_online_transferwise/readme/USAGE.rst
  6. 195
      account_bank_statement_import_online_transferwise/tests/test_account_bank_statement_import_online_transferwise.py
  7. 22
      account_bank_statement_import_online_transferwise/views/online_bank_statement_provider.xml

9
account_bank_statement_import_online_transferwise/__manifest__.py

@ -1,9 +1,9 @@
# Copyright 2019 Brainbean Apps (https://brainbeanapps.com) # Copyright 2019 Brainbean Apps (https://brainbeanapps.com)
# Copyright 2020 CorporateHub (https://corporatehub.eu)
# Copyright 2020-2021 CorporateHub (https://corporatehub.eu)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{ {
'name': 'Online Bank Statements: TransferWise.com',
'name': 'Online Bank Statements: Wise.com (TransferWise.com)',
'version': '12.0.1.0.3', 'version': '12.0.1.0.3',
'author': 'author':
'CorporateHub, ' 'CorporateHub, '
@ -12,11 +12,14 @@
'website': 'https://github.com/OCA/bank-statement-import/', 'website': 'https://github.com/OCA/bank-statement-import/',
'license': 'AGPL-3', 'license': 'AGPL-3',
'category': 'Accounting', 'category': 'Accounting',
'summary': 'Online bank statements for TransferWise.com',
'summary': 'Online bank statements for Wise.com (TransferWise.com)',
'depends': [ 'depends': [
'account_bank_statement_import_online', 'account_bank_statement_import_online',
'web_widget_dropdown_dynamic', 'web_widget_dropdown_dynamic',
], ],
'external_dependencies': {
'python': ['cryptography'],
},
'data': [ 'data': [
'views/online_bank_statement_provider.xml', 'views/online_bank_statement_provider.xml',
], ],

132
account_bank_statement_import_online_transferwise/models/online_bank_statement_provider_transferwise.py

@ -1,7 +1,11 @@
# Copyright 2019 Brainbean Apps (https://brainbeanapps.com) # Copyright 2019 Brainbean Apps (https://brainbeanapps.com)
# Copyright 2020 CorporateHub (https://corporatehub.eu)
# Copyright 2020-2021 CorporateHub (https://corporatehub.eu)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from base64 import b64encode
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
import dateutil.parser import dateutil.parser
from decimal import Decimal from decimal import Decimal
@ -10,8 +14,9 @@ import json
import pytz import pytz
import urllib.parse import urllib.parse
import urllib.request import urllib.request
from urllib.error import HTTPError
from odoo import models, api, _
from odoo import api, fields, models, _
from odoo.exceptions import UserError from odoo.exceptions import UserError
import logging import logging
@ -24,6 +29,14 @@ TRANSFERWISE_API_BASE = 'https://api.transferwise.com'
class OnlineBankStatementProviderTransferwise(models.Model): class OnlineBankStatementProviderTransferwise(models.Model):
_inherit = 'online.bank.statement.provider' _inherit = 'online.bank.statement.provider'
# NOTE: This is needed to workaround possible multiple 'origin' fields
# present in the same view, resulting in wrong field view configuraion
# if more than one is widget="dynamic_dropdown"
transferwise_profile = fields.Char(
related='origin',
readonly=False,
)
@api.model @api.model
def values_transferwise_profile(self): def values_transferwise_profile(self):
api_base = self.env.context.get('api_base') or TRANSFERWISE_API_BASE api_base = self.env.context.get('api_base') or TRANSFERWISE_API_BASE
@ -52,7 +65,7 @@ class OnlineBankStatementProviderTransferwise(models.Model):
@api.model @api.model
def _get_available_services(self): def _get_available_services(self):
return super()._get_available_services() + [ return super()._get_available_services() + [
('transferwise', 'TransferWise.com'),
('transferwise', 'Wise.com (TransferWise.com)'),
] ]
@api.multi @api.multi
@ -66,6 +79,13 @@ class OnlineBankStatementProviderTransferwise(models.Model):
api_base = self.api_base or TRANSFERWISE_API_BASE api_base = self.api_base or TRANSFERWISE_API_BASE
api_key = self.password api_key = self.password
private_key = self.certificate_private_key
if private_key:
private_key = serialization.load_pem_private_key(
private_key.encode(),
password=None,
backend=default_backend(),
)
currency = ( currency = (
self.currency_id or self.company_id.currency_id self.currency_id or self.company_id.currency_id
).name ).name
@ -79,7 +99,9 @@ class OnlineBankStatementProviderTransferwise(models.Model):
url = api_base + '/v1/borderless-accounts?profileId=%s' % ( url = api_base + '/v1/borderless-accounts?profileId=%s' % (
self.origin, self.origin,
) )
data = self._transferwise_retrieve(url, api_key)
data = self._transferwise_retrieve(url, api_key, private_key)
if not data:
return None
borderless_account = data[0]['id'] borderless_account = data[0]['id']
balance = list(filter( balance = list(filter(
lambda balance: balance['currency'] == currency, lambda balance: balance['currency'] == currency,
@ -94,15 +116,16 @@ class OnlineBankStatementProviderTransferwise(models.Model):
# Get starting balance # Get starting balance
starting_balance_timestamp = date_since.isoformat() + 'Z' starting_balance_timestamp = date_since.isoformat() + 'Z'
url = api_base + ( url = api_base + (
'/v1/borderless-accounts/%s/statement.json' +
'?currency=%s&intervalStart=%s&intervalEnd=%s'
'/v3/profiles/%s/borderless-accounts/%s/statement.json' +
'?currency=%s&intervalStart=%s&intervalEnd=%s&type=COMPACT'
) % ( ) % (
self.origin,
borderless_account, borderless_account,
currency, currency,
starting_balance_timestamp, starting_balance_timestamp,
starting_balance_timestamp, starting_balance_timestamp,
) )
data = self._transferwise_retrieve(url, api_key)
data = self._transferwise_retrieve(url, api_key, private_key)
balance_start = data['endOfStatementBalance']['value'] balance_start = data['endOfStatementBalance']['value']
# Get statements, using 469 days (around 1 year 3 month) as step. # Get statements, using 469 days (around 1 year 3 month) as step.
@ -113,9 +136,10 @@ class OnlineBankStatementProviderTransferwise(models.Model):
balance_end = None balance_end = None
while interval_start < interval_end: while interval_start < interval_end:
url = api_base + ( url = api_base + (
'/v1/borderless-accounts/%s/statement.json' +
'?currency=%s&intervalStart=%s&intervalEnd=%s'
'/v3/profiles/%s/borderless-accounts/%s/statement.json' +
'?currency=%s&intervalStart=%s&intervalEnd=%s&type=COMPACT'
) % ( ) % (
self.origin,
borderless_account, borderless_account,
currency, currency,
interval_start.isoformat() + 'Z', interval_start.isoformat() + 'Z',
@ -123,7 +147,7 @@ class OnlineBankStatementProviderTransferwise(models.Model):
interval_start + interval_step, interval_end interval_start + interval_step, interval_end
).isoformat() + 'Z', ).isoformat() + 'Z',
) )
data = self._transferwise_retrieve(url, api_key)
data = self._transferwise_retrieve(url, api_key, private_key)
transactions += data['transactions'] transactions += data['transactions']
balance_end = data['endOfStatementBalance']['value'] balance_end = data['endOfStatementBalance']['value']
interval_start += interval_step interval_start += interval_step
@ -250,7 +274,7 @@ class OnlineBankStatementProviderTransferwise(models.Model):
'name': _('Fee for %s') % reference_number, 'name': _('Fee for %s') % reference_number,
'amount': str(fees_value), 'amount': str(fees_value),
'date': date, 'date': date,
'partner_name': 'TransferWise',
'partner_name': 'Wise (former TransferWise)',
'unique_import_id': '%s-FEE' % unique_import_id, 'unique_import_id': '%s-FEE' % unique_import_id,
'note': _('Transaction fee for %s') % reference_number, 'note': _('Transaction fee for %s') % reference_number,
}] }]
@ -268,20 +292,94 @@ class OnlineBankStatementProviderTransferwise(models.Model):
return content return content
@api.model @api.model
def _transferwise_retrieve(self, url, api_key):
def _transferwise_retrieve(self, url, api_key, private_key=None):
try:
with self._transferwise_urlopen(url, api_key) as response: with self._transferwise_urlopen(url, api_key) as response:
content = response.read().decode( content = response.read().decode(
response.headers.get_content_charset() or 'utf-8' response.headers.get_content_charset() or 'utf-8'
) )
except HTTPError as e:
if e.code != 403 or \
e.headers.get('X-2FA-Approval-Result') != 'REJECTED':
raise e
if not private_key:
raise UserError(_(
'Strong Customer Authentication is not configured'
))
one_time_token = e.headers['X-2FA-Approval']
signature = private_key.sign(
one_time_token.encode(),
padding.PKCS1v15(),
hashes.SHA256(),
)
with self._transferwise_urlopen(
url,
api_key,
one_time_token,
b64encode(signature).decode(),
) as response:
content = response.read().decode(
response.headers.get_content_charset() or 'utf-8'
)
return self._transferwise_validate(content) return self._transferwise_validate(content)
@api.model @api.model
def _transferwise_urlopen(self, url, api_key):
def _transferwise_urlopen(self, url, api_key, ott=None, signature=None):
if not api_key: if not api_key:
raise UserError(_('No API key specified!')) raise UserError(_('No API key specified!'))
request = urllib.request.Request(url) request = urllib.request.Request(url)
request.add_header(
'Authorization',
'Bearer %s' % api_key
)
request.add_header('Authorization', 'Bearer %s' % api_key)
if ott and signature:
request.add_header('X-2FA-Approval', ott)
request.add_header('X-Signature', signature)
return urllib.request.urlopen(request) return urllib.request.urlopen(request)
@api.onchange('certificate_private_key', 'service')
def _onchange_transferwise_certificate_private_key(self):
if self.service != 'transferwise':
return
self.certificate_public_key = False
if not self.certificate_private_key:
return
try:
private_key = serialization.load_pem_private_key(
self.certificate_private_key.encode(),
password=None,
backend=default_backend(),
)
self.certificate_public_key = private_key.public_key().public_bytes(
serialization.Encoding.PEM,
serialization.PublicFormat.PKCS1,
).decode()
except:
_logger.warning('Unable to parse key', exc_info=True)
raise UserError(_('Unable to parse key'))
@api.multi
def _transferwise_generate_key(self):
self.ensure_one()
private_key = rsa.generate_private_key(
public_exponent=65537,
key_size=2048,
backend=default_backend(),
)
self.certificate_private_key = private_key.private_bytes(
serialization.Encoding.PEM,
serialization.PrivateFormat.TraditionalOpenSSL, # a.k.a. PKCS#1
serialization.NoEncryption(),
).decode()
self.certificate_public_key = private_key.public_key().public_bytes(
serialization.Encoding.PEM,
serialization.PublicFormat.PKCS1,
).decode()
@api.multi
def button_transferwise_generate_key(self):
for provider in self:
provider._transferwise_generate_key()

4
account_bank_statement_import_online_transferwise/readme/CONFIGURE.rst

@ -3,7 +3,7 @@ To configure online bank statements provider:
#. Go to *Invoicing > Configuration > Bank Accounts* #. Go to *Invoicing > Configuration > Bank Accounts*
#. Open bank account to configure and edit it #. Open bank account to configure and edit it
#. Set *Bank Feeds* to *Online* #. Set *Bank Feeds* to *Online*
#. Select *TransferWise.com* as online bank statements provider in
#. Select *Wise.com (TransferWise.com)* as online bank statements provider in
*Online Bank Statements (OCA)* section *Online Bank Statements (OCA)* section
#. Save the bank account #. Save the bank account
#. Click on provider and configure provider-specific settings. #. Click on provider and configure provider-specific settings.
@ -14,7 +14,7 @@ or, alternatively:
#. Open settings of the corresponding journal account #. Open settings of the corresponding journal account
#. Switch to *Bank Account* tab #. Switch to *Bank Account* tab
#. Set *Bank Feeds* to *Online* #. Set *Bank Feeds* to *Online*
#. Select *TransferWise.com* as online bank statements provider in
#. Select *Wise.com (TransferWise.com)* as online bank statements provider in
*Online Bank Statements (OCA)* section *Online Bank Statements (OCA)* section
#. Save the bank account #. Save the bank account
#. Click on provider and configure provider-specific settings. #. Click on provider and configure provider-specific settings.

3
account_bank_statement_import_online_transferwise/readme/DESCRIPTION.rst

@ -1,2 +1,3 @@
This module provides online bank statements from This module provides online bank statements from
`TransferWise.com <https://transferwise.com/>`__.
`Wise.com <https://wise.com/>`__.
(formely `TransferWise.com <https://transferwise.com/>`__).

6
account_bank_statement_import_online_transferwise/readme/USAGE.rst

@ -4,3 +4,9 @@ To pull historical bank statements:
#. Select specific bank accounts #. Select specific bank accounts
#. Launch *Actions > Online Bank Statements Pull Wizard* #. Launch *Actions > Online Bank Statements Pull Wizard*
#. Configure date interval and click *Pull* #. Configure date interval and click *Pull*
To configure Strong Customer Authentication:
#. Go to provider-specific settings and either press *Generate Key* or paste
manually-generate private and public keys
#. Navigate to `Wise.com <https://wise.com/public-keys/>`__ and register the public key.

195
account_bank_statement_import_online_transferwise/tests/test_account_bank_statement_import_online_transferwise.py

@ -1,5 +1,5 @@
# Copyright 2019 Brainbean Apps (https://brainbeanapps.com) # Copyright 2019 Brainbean Apps (https://brainbeanapps.com)
# Copyright 2020 CorporateHub (https://corporatehub.eu)
# Copyright 2020-2021 CorporateHub (https://corporatehub.eu)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from datetime import datetime from datetime import datetime
@ -7,6 +7,7 @@ from dateutil.relativedelta import relativedelta
from decimal import Decimal from decimal import Decimal
import json import json
from unittest import mock from unittest import mock
from urllib.error import HTTPError
from odoo.tests import common from odoo.tests import common
from odoo import fields from odoo import fields
@ -19,6 +20,29 @@ _provider_class = (
) )
class MockedResponse:
class Headers(dict):
def get_content_charset(self):
return None
def __init__(self, data=None, exception=None):
self.data = data
self.exception = exception
self.headers = MockedResponse.Headers()
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
def read(self):
if self.exception is not None:
raise self.exception()
return self.data
class TestAccountBankAccountStatementImportOnlineTransferwise( class TestAccountBankAccountStatementImportOnlineTransferwise(
common.TransactionCase common.TransactionCase
): ):
@ -47,6 +71,33 @@ class TestAccountBankAccountStatementImportOnlineTransferwise(
) )
) )
) )
self.response_balance = MockedResponse(data="""[
{
"id": 42,
"balances": [
{
"currency": "EUR"
}
]
}
]""".encode())
self.response_ott = MockedResponse(exception=lambda: HTTPError(
'https://wise.com/',
403,
'403',
{
'X-2FA-Approval-Result': 'REJECTED',
'X-2FA-Approval': '0123456789',
},
None,
))
self.response_transactions = MockedResponse(data="""{
"transactions": [],
"endOfStatementBalance": {
"value": 42.00,
"currency": "EUR"
}
}""".encode())
def test_values_transferwise_profile(self): def test_values_transferwise_profile(self):
mocked_response = json.loads( mocked_response = json.loads(
@ -86,6 +137,28 @@ class TestAccountBankAccountStatementImportOnlineTransferwise(
] ]
) )
def test_values_transferwise_profile_no_key(self):
values_transferwise_profile = (
self.OnlineBankStatementProvider.with_context({
'api_base': 'https://example.com',
}).values_transferwise_profile()
)
self.assertEqual(values_transferwise_profile, [])
def test_values_transferwise_profile_error(self):
values_transferwise_profile = []
with mock.patch(
_provider_class + '._transferwise_retrieve',
side_effect=lambda: Exception(),
):
values_transferwise_profile = (
self.OnlineBankStatementProvider.with_context({
'api_base': 'https://example.com',
'api_key': 'dummy',
}).values_transferwise_profile()
)
self.assertEqual(values_transferwise_profile, [])
def test_pull(self): def test_pull(self):
journal = self.AccountJournal.create({ journal = self.AccountJournal.create({
'name': 'Bank', 'name': 'Bank',
@ -99,7 +172,7 @@ class TestAccountBankAccountStatementImportOnlineTransferwise(
provider = journal.online_bank_statement_provider_id provider = journal.online_bank_statement_provider_id
provider.origin = '1234567891' provider.origin = '1234567891'
def mock_response(url, api_key):
def mock_response(url, api_key, private_key=None):
if '/borderless-accounts?profileId=1234567891' in url: if '/borderless-accounts?profileId=1234567891' in url:
payload = """[ payload = """[
{ {
@ -133,6 +206,112 @@ class TestAccountBankAccountStatementImportOnlineTransferwise(
self.assertEqual(data[1]['balance_start'], 42.0) self.assertEqual(data[1]['balance_start'], 42.0)
self.assertEqual(data[1]['balance_end_real'], 42.0) self.assertEqual(data[1]['balance_end_real'], 42.0)
def test_pull_no_data(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'
provider.password = 'API_KEY'
with mock.patch(
_provider_class + '._transferwise_retrieve',
return_value=[],
):
data = provider._obtain_statement_data(
self.now - relativedelta(hours=1),
self.now,
)
self.assertFalse(data)
def test_update_public_key(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'
provider.password = 'API_KEY'
with common.Form(provider) as provider_form:
provider_form.certificate_private_key = """
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAxC7aYWigCwPIB4mfyLpsALYPnqDm3/IC8I/3GdEwfK8eqXoF
sU1BHnVytFycBDEObmJ2Acpxe8Dk61FnbWPrrl6rXVnXfIRqfFl94TvgwFsuwG7u
8crncD6gPfe1QGkEykHcfBURr74OcSE8590ngNJcKMGvac0cuyZ2/NEszTw7EFJg
obpMWjp0m5ItgZ/UNsPLR/D4gFE9vZz7a4+FYQMa9Wbv+xaVxUS6z9rQCJfUQx7N
iih4etIvAafbfAnX6rFv8PwPzz+XvexPWWJxnbS4iV1LN2atrPDxqw73g5hc3W88
a0V2AVubtxhw9L2VK1VmRb/gnqsZpRXhDPSUIwIDAQABAoIBAQCMvnRLV80hudfC
mJh6YEvlgrfX/OVFmpFDVnVXHz2i5dugiHsXBS6HlIjzHlGLrEoHJTo19K/PscZJ
kEAcOYg2s5JLSY4PtcvTZDyr3tJSDdiPk8Z2zzOU0kkRy+lLyUv3cqKknlTu+PHR
daAFVCLoB4K4dqPKyq0nEuRgYgy7O42SPBY5DHgWYBKqkYGlTu+ImwpDD9unbv3e
mwvdcBCp9hAlYAArc1Ip/6aUkZdKxJYgVhovruaH309yuOmBfAEgguhsy3vR18t5
IZXbAF3C6iXCQXi8l+S1NUu8XWPLEavldb+ZA2hI2L+NPSBVIYqhI4jDiI7lfs1c
HE8BRsRpAoGBAO6BnK3qD8sRvg6JsrBhppoIGsudOdpZ/KVp9ZpYCBJNJmfrkqLR
bWx1KF2UjAoYUmaKDTS2GP8JQd7X2n4T5LX8q+7iG9/wzdSWZYZuBOnjvWlNyJu4
OiUKX4aEgdvZHiuEIin5xTP98/c5LTZXwM3bq8IrOXEz8LBLLPrTCGRvAoGBANKS
i3cn1jtVirJWbvhSIjjqhpfuZN0361FB6j1Aho+7z0WVd4NQjPQqA6cAqnWoa/kj
cX0X8Ncu5eHqf6CuW+HsQda3yp3bvCXi1Yc2nKBTHnWtMm721O4ZW6rbaALzBZYW
qeJr0m9pNlfCAL0INTcy7IVAtqcCJ/7CEN6Hjm2NAoGAIGSgKArDLFxziLvw9f29
R+xT31WyVtKj+r9iaR0Ns5ag4bpgBxcUmodq/RLA1lopTt3vHzqgOHtEZATDGx6O
kJ0JqP8ys/6bpgTrMw/cQPv6bMPwvB2QYBmBkd6LWJWrgFOI5FSVEROrv+cXGetf
N1ZfhJakTZi1VuxO5p4k5KcCgYAZS9OHR/jbfeZAkFOabzt/POVYYSIq1SnmxBVg
sFy57aTzxgXqd4XHWzi/GjxgEBCQiGp8zaB4KUEih6o3YlrVZC1wnvmvRxNuNbbT
HINqWzHgjyLs46gmxlMVzm/LUuiL5EMaWTuZeLk3h63RB6hk7jAtvd1zaLXnS+b8
5Kn+jQKBgQCDeMO6rvB2rbfqSbHvPPuTru1sPIsJBKm1YZpXTFI+VMjwtk7+meYb
UQnfZ1t5rjp9q4LEcRYuSa+PfifIkM6p+wMHVQhtltUCzXWWRYkLkmQrBWKu+qiP
edF6byMgXSzgOWYuRPXwmHpBQV0GiexQUAxVyUzaVWfil69LaFfXaw==
-----END RSA PRIVATE KEY-----
"""
self.assertTrue(provider.certificate_public_key)
def test_sca(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'
provider.password = 'API_KEY'
provider.button_transferwise_generate_key()
with mock.patch(
'urllib.request.urlopen',
side_effect=[
self.response_balance,
self.response_ott,
self.response_transactions,
self.response_transactions,
self.response_transactions,
],
):
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): def test_transaction_parse_1(self):
lines = self.transferwise_parse_transaction("""{ lines = self.transferwise_parse_transaction("""{
"type": "CREDIT", "type": "CREDIT",
@ -216,7 +395,7 @@ class TestAccountBankAccountStatementImportOnlineTransferwise(
'amount': '-0.60', 'amount': '-0.60',
'name': 'Fee for TRANSFER-123456789', 'name': 'Fee for TRANSFER-123456789',
'note': 'Transaction fee for TRANSFER-123456789', 'note': 'Transaction fee for TRANSFER-123456789',
'partner_name': 'TransferWise',
'partner_name': 'Wise (former TransferWise)',
'unique_import_id': 'DEBIT-TRANSFER-123456789-946684800-FEE', 'unique_import_id': 'DEBIT-TRANSFER-123456789-946684800-FEE',
}) })
@ -342,7 +521,7 @@ class TestAccountBankAccountStatementImportOnlineTransferwise(
'amount': '-1.23', 'amount': '-1.23',
'name': 'Fee for CARD-123456789', 'name': 'Fee for CARD-123456789',
'note': 'Transaction fee for CARD-123456789', 'note': 'Transaction fee for CARD-123456789',
'partner_name': 'TransferWise',
'partner_name': 'Wise (former TransferWise)',
'unique_import_id': 'DEBIT-CARD-123456789-946684800-FEE', 'unique_import_id': 'DEBIT-CARD-123456789-946684800-FEE',
}) })
@ -400,7 +579,7 @@ class TestAccountBankAccountStatementImportOnlineTransferwise(
'date': datetime(2000, 1, 1), 'date': datetime(2000, 1, 1),
'name': 'Fee for TRANSFER-123456789', 'name': 'Fee for TRANSFER-123456789',
'note': 'Transaction fee for TRANSFER-123456789', 'note': 'Transaction fee for TRANSFER-123456789',
'partner_name': 'TransferWise',
'partner_name': 'Wise (former TransferWise)',
'amount': '-5.21', 'amount': '-5.21',
'unique_import_id': 'DEBIT-TRANSFER-123456789-946684800-FEE', 'unique_import_id': 'DEBIT-TRANSFER-123456789-946684800-FEE',
}) })
@ -547,7 +726,7 @@ class TestAccountBankAccountStatementImportOnlineTransferwise(
'name': 'Fee for BALANCE-123456789', 'name': 'Fee for BALANCE-123456789',
'note': 'Transaction fee for BALANCE-123456789', 'note': 'Transaction fee for BALANCE-123456789',
'amount': '-0.05', 'amount': '-0.05',
'partner_name': 'TransferWise',
'partner_name': 'Wise (former TransferWise)',
'unique_import_id': 'DEBIT-BALANCE-123456789-946684800-FEE', 'unique_import_id': 'DEBIT-BALANCE-123456789-946684800-FEE',
}) })
@ -587,7 +766,7 @@ class TestAccountBankAccountStatementImportOnlineTransferwise(
'name': 'Fee for TRANSFER-123456789', 'name': 'Fee for TRANSFER-123456789',
'note': 'Transaction fee for TRANSFER-123456789', 'note': 'Transaction fee for TRANSFER-123456789',
'amount': '-0.68', 'amount': '-0.68',
'partner_name': 'TransferWise',
'partner_name': 'Wise (former TransferWise)',
'unique_import_id': 'CREDIT-TRANSFER-123456789-946684800-FEE', 'unique_import_id': 'CREDIT-TRANSFER-123456789-946684800-FEE',
}) })
@ -631,6 +810,6 @@ class TestAccountBankAccountStatementImportOnlineTransferwise(
'name': 'Fee for TRANSFER-123456789', 'name': 'Fee for TRANSFER-123456789',
'note': 'Transaction fee for TRANSFER-123456789', 'note': 'Transaction fee for TRANSFER-123456789',
'amount': '4.33', 'amount': '4.33',
'partner_name': 'TransferWise',
'partner_name': 'Wise (former TransferWise)',
'unique_import_id': 'CREDIT-TRANSFER-123456789-946684800-FEE', 'unique_import_id': 'CREDIT-TRANSFER-123456789-946684800-FEE',
}) })

22
account_bank_statement_import_online_transferwise/views/online_bank_statement_provider.xml

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- <!--
Copyright 2019 Brainbean Apps (https://brainbeanapps.com) Copyright 2019 Brainbean Apps (https://brainbeanapps.com)
Copyright 2021 CorporateHub (https://corporatehub.eu)
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
--> -->
<odoo> <odoo>
@ -27,7 +28,7 @@
</group> </group>
<group> <group>
<field <field
name="origin"
name="transferwise_profile"
string="Profile" string="Profile"
attrs="{'required': [('service', '=', 'transferwise')]}" attrs="{'required': [('service', '=', 'transferwise')]}"
widget="dynamic_dropdown" widget="dynamic_dropdown"
@ -35,6 +36,25 @@
context="{'api_key': password, 'api_base': api_base}" context="{'api_key': password, 'api_base': api_base}"
/> />
</group> </group>
<group string="Strong Customer Authentication" colspan="4">
<field
name="certificate_private_key"
string="Private key"
password="True"
/>
<field
name="certificate_public_key"
string="Public key"
/>
<div col="2" colspan="2">
<button
name="button_transferwise_generate_key"
string="Generate Key"
type="object"
class="oe_edit_only"
/>
</div>
</group>
</group> </group>
</xpath> </xpath>
</field> </field>

Loading…
Cancel
Save