mirror of
https://github.com/OCA/bank-statement-import.git
synced 2025-01-20 12:37:43 +02:00
[IMP] account_bank_statement_import_online_transferwise: support SCA
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
# 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).
|
||||
|
||||
{
|
||||
'name': 'Online Bank Statements: TransferWise.com',
|
||||
'name': 'Online Bank Statements: Wise.com (TransferWise.com)',
|
||||
'version': '12.0.1.0.3',
|
||||
'author':
|
||||
'CorporateHub, '
|
||||
@@ -12,11 +12,14 @@
|
||||
'website': 'https://github.com/OCA/bank-statement-import/',
|
||||
'license': 'AGPL-3',
|
||||
'category': 'Accounting',
|
||||
'summary': 'Online bank statements for TransferWise.com',
|
||||
'summary': 'Online bank statements for Wise.com (TransferWise.com)',
|
||||
'depends': [
|
||||
'account_bank_statement_import_online',
|
||||
'web_widget_dropdown_dynamic',
|
||||
],
|
||||
'external_dependencies': {
|
||||
'python': ['cryptography'],
|
||||
},
|
||||
'data': [
|
||||
'views/online_bank_statement_provider.xml',
|
||||
],
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
# 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).
|
||||
|
||||
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
|
||||
import dateutil.parser
|
||||
from decimal import Decimal
|
||||
@@ -10,8 +14,9 @@ import json
|
||||
import pytz
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
from urllib.error import HTTPError
|
||||
|
||||
from odoo import models, api, _
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
import logging
|
||||
@@ -24,6 +29,14 @@ TRANSFERWISE_API_BASE = 'https://api.transferwise.com'
|
||||
class OnlineBankStatementProviderTransferwise(models.Model):
|
||||
_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
|
||||
def values_transferwise_profile(self):
|
||||
api_base = self.env.context.get('api_base') or TRANSFERWISE_API_BASE
|
||||
@@ -52,7 +65,7 @@ class OnlineBankStatementProviderTransferwise(models.Model):
|
||||
@api.model
|
||||
def _get_available_services(self):
|
||||
return super()._get_available_services() + [
|
||||
('transferwise', 'TransferWise.com'),
|
||||
('transferwise', 'Wise.com (TransferWise.com)'),
|
||||
]
|
||||
|
||||
@api.multi
|
||||
@@ -66,6 +79,13 @@ class OnlineBankStatementProviderTransferwise(models.Model):
|
||||
|
||||
api_base = self.api_base or TRANSFERWISE_API_BASE
|
||||
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 = (
|
||||
self.currency_id or self.company_id.currency_id
|
||||
).name
|
||||
@@ -79,7 +99,9 @@ class OnlineBankStatementProviderTransferwise(models.Model):
|
||||
url = api_base + '/v1/borderless-accounts?profileId=%s' % (
|
||||
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']
|
||||
balance = list(filter(
|
||||
lambda balance: balance['currency'] == currency,
|
||||
@@ -94,15 +116,16 @@ class OnlineBankStatementProviderTransferwise(models.Model):
|
||||
# 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'
|
||||
'/v3/profiles/%s/borderless-accounts/%s/statement.json' +
|
||||
'?currency=%s&intervalStart=%s&intervalEnd=%s&type=COMPACT'
|
||||
) % (
|
||||
self.origin,
|
||||
borderless_account,
|
||||
currency,
|
||||
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']
|
||||
|
||||
# Get statements, using 469 days (around 1 year 3 month) as step.
|
||||
@@ -113,9 +136,10 @@ class OnlineBankStatementProviderTransferwise(models.Model):
|
||||
balance_end = None
|
||||
while interval_start < interval_end:
|
||||
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,
|
||||
currency,
|
||||
interval_start.isoformat() + 'Z',
|
||||
@@ -123,7 +147,7 @@ class OnlineBankStatementProviderTransferwise(models.Model):
|
||||
interval_start + interval_step, interval_end
|
||||
).isoformat() + 'Z',
|
||||
)
|
||||
data = self._transferwise_retrieve(url, api_key)
|
||||
data = self._transferwise_retrieve(url, api_key, private_key)
|
||||
transactions += data['transactions']
|
||||
balance_end = data['endOfStatementBalance']['value']
|
||||
interval_start += interval_step
|
||||
@@ -250,7 +274,7 @@ class OnlineBankStatementProviderTransferwise(models.Model):
|
||||
'name': _('Fee for %s') % reference_number,
|
||||
'amount': str(fees_value),
|
||||
'date': date,
|
||||
'partner_name': 'TransferWise',
|
||||
'partner_name': 'Wise (former TransferWise)',
|
||||
'unique_import_id': '%s-FEE' % unique_import_id,
|
||||
'note': _('Transaction fee for %s') % reference_number,
|
||||
}]
|
||||
@@ -268,20 +292,94 @@ class OnlineBankStatementProviderTransferwise(models.Model):
|
||||
return content
|
||||
|
||||
@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:
|
||||
content = response.read().decode(
|
||||
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)
|
||||
|
||||
@api.model
|
||||
def _transferwise_urlopen(self, url, api_key):
|
||||
def _transferwise_urlopen(self, url, api_key, ott=None, signature=None):
|
||||
if not api_key:
|
||||
raise UserError(_('No API key specified!'))
|
||||
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)
|
||||
|
||||
@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()
|
||||
|
||||
@@ -3,7 +3,7 @@ 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
|
||||
#. Select *Wise.com (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.
|
||||
@@ -14,7 +14,7 @@ or, alternatively:
|
||||
#. 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
|
||||
#. Select *Wise.com (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.
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
This module provides online bank statements from
|
||||
`TransferWise.com <https://transferwise.com/>`__.
|
||||
`Wise.com <https://wise.com/>`__.
|
||||
(formely `TransferWise.com <https://transferwise.com/>`__).
|
||||
|
||||
@@ -4,3 +4,9 @@ To pull historical bank statements:
|
||||
#. Select specific bank accounts
|
||||
#. Launch *Actions > Online Bank Statements Pull Wizard*
|
||||
#. 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.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# 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).
|
||||
|
||||
from datetime import datetime
|
||||
@@ -7,6 +7,7 @@ from dateutil.relativedelta import relativedelta
|
||||
from decimal import Decimal
|
||||
import json
|
||||
from unittest import mock
|
||||
from urllib.error import HTTPError
|
||||
|
||||
from odoo.tests import common
|
||||
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(
|
||||
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):
|
||||
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):
|
||||
journal = self.AccountJournal.create({
|
||||
'name': 'Bank',
|
||||
@@ -99,7 +172,7 @@ class TestAccountBankAccountStatementImportOnlineTransferwise(
|
||||
provider = journal.online_bank_statement_provider_id
|
||||
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:
|
||||
payload = """[
|
||||
{
|
||||
@@ -133,6 +206,112 @@ class TestAccountBankAccountStatementImportOnlineTransferwise(
|
||||
self.assertEqual(data[1]['balance_start'], 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):
|
||||
lines = self.transferwise_parse_transaction("""{
|
||||
"type": "CREDIT",
|
||||
@@ -216,7 +395,7 @@ class TestAccountBankAccountStatementImportOnlineTransferwise(
|
||||
'amount': '-0.60',
|
||||
'name': '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',
|
||||
})
|
||||
|
||||
@@ -342,7 +521,7 @@ class TestAccountBankAccountStatementImportOnlineTransferwise(
|
||||
'amount': '-1.23',
|
||||
'name': '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',
|
||||
})
|
||||
|
||||
@@ -400,7 +579,7 @@ class TestAccountBankAccountStatementImportOnlineTransferwise(
|
||||
'date': datetime(2000, 1, 1),
|
||||
'name': 'Fee for TRANSFER-123456789',
|
||||
'note': 'Transaction fee for TRANSFER-123456789',
|
||||
'partner_name': 'TransferWise',
|
||||
'partner_name': 'Wise (former TransferWise)',
|
||||
'amount': '-5.21',
|
||||
'unique_import_id': 'DEBIT-TRANSFER-123456789-946684800-FEE',
|
||||
})
|
||||
@@ -547,7 +726,7 @@ class TestAccountBankAccountStatementImportOnlineTransferwise(
|
||||
'name': 'Fee for BALANCE-123456789',
|
||||
'note': 'Transaction fee for BALANCE-123456789',
|
||||
'amount': '-0.05',
|
||||
'partner_name': 'TransferWise',
|
||||
'partner_name': 'Wise (former TransferWise)',
|
||||
'unique_import_id': 'DEBIT-BALANCE-123456789-946684800-FEE',
|
||||
})
|
||||
|
||||
@@ -587,7 +766,7 @@ class TestAccountBankAccountStatementImportOnlineTransferwise(
|
||||
'name': 'Fee for TRANSFER-123456789',
|
||||
'note': 'Transaction fee for TRANSFER-123456789',
|
||||
'amount': '-0.68',
|
||||
'partner_name': 'TransferWise',
|
||||
'partner_name': 'Wise (former TransferWise)',
|
||||
'unique_import_id': 'CREDIT-TRANSFER-123456789-946684800-FEE',
|
||||
})
|
||||
|
||||
@@ -631,6 +810,6 @@ class TestAccountBankAccountStatementImportOnlineTransferwise(
|
||||
'name': 'Fee for TRANSFER-123456789',
|
||||
'note': 'Transaction fee for TRANSFER-123456789',
|
||||
'amount': '4.33',
|
||||
'partner_name': 'TransferWise',
|
||||
'partner_name': 'Wise (former TransferWise)',
|
||||
'unique_import_id': 'CREDIT-TRANSFER-123456789-946684800-FEE',
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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).
|
||||
-->
|
||||
<odoo>
|
||||
@@ -27,7 +28,7 @@
|
||||
</group>
|
||||
<group>
|
||||
<field
|
||||
name="origin"
|
||||
name="transferwise_profile"
|
||||
string="Profile"
|
||||
attrs="{'required': [('service', '=', 'transferwise')]}"
|
||||
widget="dynamic_dropdown"
|
||||
@@ -35,6 +36,25 @@
|
||||
context="{'api_key': password, 'api_base': api_base}"
|
||||
/>
|
||||
</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>
|
||||
</xpath>
|
||||
</field>
|
||||
|
||||
Reference in New Issue
Block a user