mirror of
https://github.com/OCA/bank-statement-import.git
synced 2025-01-20 12:37:43 +02:00
[IMP] *_online_ponto: forward port 12.0 improvements
Separate retrieval of data from ponto (buffer data) and creation of statements.
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from . import test_ponto_interface
|
||||
from . import test_account_statement_import_online_ponto
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# Copyright 2020 Florent de Labarre
|
||||
# Copyright 2022 Therp BV <https://therp.nl>.
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from datetime import date, datetime
|
||||
@@ -8,14 +9,89 @@ from odoo import fields
|
||||
from odoo.tests import Form, common
|
||||
|
||||
_module_ns = "odoo.addons.account_statement_import_online_ponto"
|
||||
_provider_class = (
|
||||
_module_ns
|
||||
+ ".models.online_bank_statement_provider_ponto"
|
||||
+ ".OnlineBankStatementProviderPonto"
|
||||
)
|
||||
_interface_class = _module_ns + ".models.ponto_interface" + ".PontoInterface"
|
||||
|
||||
THREE_TRANSACTIONS = [
|
||||
{
|
||||
"type": "transaction",
|
||||
"relationships": {
|
||||
"account": {
|
||||
"links": {"related": "https://api.myponto.com/accounts/"},
|
||||
"data": {
|
||||
"type": "account",
|
||||
"id": "fd3d5b1d-fca9-4310-a5c8-76f2a9dc7c75",
|
||||
},
|
||||
}
|
||||
},
|
||||
"id": "701ab965-21c4-46ca-b157-306c0646e0e2",
|
||||
"attributes": {
|
||||
"valueDate": "2019-11-18T00:00:00.000Z",
|
||||
"remittanceInformationType": "unstructured",
|
||||
"remittanceInformation": "Minima vitae totam!",
|
||||
"executionDate": "2019-11-20T00:00:00.000Z",
|
||||
"description": "Wire transfer",
|
||||
"currency": "EUR",
|
||||
"counterpartReference": "BE26089479973169",
|
||||
"counterpartName": "Osinski Group",
|
||||
"amount": 6.08,
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "transaction",
|
||||
"relationships": {
|
||||
"account": {
|
||||
"links": {"related": "https://api.myponto.com/accounts/"},
|
||||
"data": {
|
||||
"type": "account",
|
||||
"id": "fd3d5b1d-fca9-4310-a5c8-76f2a9dc7c75",
|
||||
},
|
||||
}
|
||||
},
|
||||
"id": "9ac50483-16dc-4a82-aa60-df56077405cd",
|
||||
"attributes": {
|
||||
"valueDate": "2019-11-04T00:00:00.000Z",
|
||||
"remittanceInformationType": "unstructured",
|
||||
"remittanceInformation": "Quia voluptatem blanditiis.",
|
||||
"executionDate": "2019-11-06T00:00:00.000Z",
|
||||
"description": "Wire transfer",
|
||||
"currency": "EUR",
|
||||
"counterpartReference": "BE97201830401438",
|
||||
"counterpartName": "Stokes-Miller",
|
||||
"amount": 5.48,
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "transaction",
|
||||
"relationships": {
|
||||
"account": {
|
||||
"links": {"related": "https://api.myponto.com/accounts/"},
|
||||
"data": {
|
||||
"type": "account",
|
||||
"id": "fd3d5b1d-fca9-4310-a5c8-76f2a9dc7c75",
|
||||
},
|
||||
}
|
||||
},
|
||||
"id": "b21a6c65-1c52-4ba6-8cbc-127d2b2d85ff",
|
||||
"attributes": {
|
||||
"valueDate": "2019-11-04T00:00:00.000Z",
|
||||
"remittanceInformationType": "unstructured",
|
||||
"remittanceInformation": "Laboriosam repelo?",
|
||||
"executionDate": "2019-11-04T00:00:00.000Z",
|
||||
"description": "Wire transfer",
|
||||
"currency": "EUR",
|
||||
"counterpartReference": "BE10325927501996",
|
||||
"counterpartName": "Strosin-Veum",
|
||||
"amount": 5.83,
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
EMPTY_TRANSACTIONS = []
|
||||
|
||||
|
||||
class TestAccountBankAccountStatementImportOnlineQonto(common.TransactionCase):
|
||||
class TestAccountStatementImportOnlinePonto(common.TransactionCase):
|
||||
post_install = True
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
@@ -27,7 +103,7 @@ class TestAccountBankAccountStatementImportOnlineQonto(common.TransactionCase):
|
||||
self.OnlineBankStatementProvider = self.env["online.bank.statement.provider"]
|
||||
self.AccountBankStatement = self.env["account.bank.statement"]
|
||||
self.AccountBankStatementLine = self.env["account.bank.statement.line"]
|
||||
self.AccStatemenPull = self.env["online.bank.statement.pull.wizard"]
|
||||
self.AccountStatementPull = self.env["online.bank.statement.pull.wizard"]
|
||||
|
||||
self.bank_account = self.ResPartnerBank.create(
|
||||
{
|
||||
@@ -48,98 +124,25 @@ class TestAccountBankAccountStatementImportOnlineQonto(common.TransactionCase):
|
||||
)
|
||||
self.provider = self.journal.online_bank_statement_provider_id
|
||||
|
||||
self.mock_header = lambda: mock.patch(
|
||||
_provider_class + "._ponto_header",
|
||||
self.mock_login = lambda: mock.patch(
|
||||
_interface_class + "._login",
|
||||
return_value={
|
||||
"Accept": "application/json",
|
||||
"Authorization": "Bearer --TOKEN--",
|
||||
"username": "test_user",
|
||||
"password": "very_secret",
|
||||
"access_token": "abcd1234",
|
||||
"token_expiration": datetime(2099, 12, 31, 23, 59, 59),
|
||||
},
|
||||
)
|
||||
|
||||
self.mock_account_ids = lambda: mock.patch(
|
||||
_provider_class + "._ponto_get_account_ids",
|
||||
return_value={"FR0214508000302245362775K46": "id"},
|
||||
)
|
||||
self.mock_synchronisation = lambda: mock.patch(
|
||||
_provider_class + "._ponto_synchronisation",
|
||||
self.mock_set_access_account = lambda: mock.patch(
|
||||
_interface_class + "._set_access_account",
|
||||
return_value=None,
|
||||
)
|
||||
|
||||
self.mock_transaction = lambda: mock.patch(
|
||||
_provider_class + "._ponto_get_transaction",
|
||||
return_value=[
|
||||
{
|
||||
"type": "transaction",
|
||||
"relationships": {
|
||||
"account": {
|
||||
"links": {"related": "https://api.myponto.com/accounts/"},
|
||||
"data": {
|
||||
"type": "account",
|
||||
"id": "fd3d5b1d-fca9-4310-a5c8-76f2a9dc7c75",
|
||||
},
|
||||
}
|
||||
},
|
||||
"id": "701ab965-21c4-46ca-b157-306c0646e0e2",
|
||||
"attributes": {
|
||||
"valueDate": "2019-11-18T00:00:00.000Z",
|
||||
"remittanceInformationType": "unstructured",
|
||||
"remittanceInformation": "Minima vitae totam!",
|
||||
"executionDate": "2019-11-20T00:00:00.000Z",
|
||||
"description": "Wire transfer",
|
||||
"currency": "EUR",
|
||||
"counterpartReference": "BE26089479973169",
|
||||
"counterpartName": "Osinski Group",
|
||||
"amount": 6.08,
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "transaction",
|
||||
"relationships": {
|
||||
"account": {
|
||||
"links": {"related": "https://api.myponto.com/accounts/"},
|
||||
"data": {
|
||||
"type": "account",
|
||||
"id": "fd3d5b1d-fca9-4310-a5c8-76f2a9dc7c75",
|
||||
},
|
||||
}
|
||||
},
|
||||
"id": "9ac50483-16dc-4a82-aa60-df56077405cd",
|
||||
"attributes": {
|
||||
"valueDate": "2019-11-04T00:00:00.000Z",
|
||||
"remittanceInformationType": "unstructured",
|
||||
"remittanceInformation": "Quia voluptatem blanditiis.",
|
||||
"executionDate": "2019-11-06T00:00:00.000Z",
|
||||
"description": "Wire transfer",
|
||||
"currency": "EUR",
|
||||
"counterpartReference": "BE97201830401438",
|
||||
"counterpartName": "Stokes-Miller",
|
||||
"amount": 5.48,
|
||||
},
|
||||
},
|
||||
{
|
||||
"type": "transaction",
|
||||
"relationships": {
|
||||
"account": {
|
||||
"links": {"related": "https://api.myponto.com/accounts/"},
|
||||
"data": {
|
||||
"type": "account",
|
||||
"id": "fd3d5b1d-fca9-4310-a5c8-76f2a9dc7c75",
|
||||
},
|
||||
}
|
||||
},
|
||||
"id": "b21a6c65-1c52-4ba6-8cbc-127d2b2d85ff",
|
||||
"attributes": {
|
||||
"valueDate": "2019-11-04T00:00:00.000Z",
|
||||
"remittanceInformationType": "unstructured",
|
||||
"remittanceInformation": "Laboriosam repelo?",
|
||||
"executionDate": "2019-11-04T00:00:00.000Z",
|
||||
"description": "Wire transfer",
|
||||
"currency": "EUR",
|
||||
"counterpartReference": "BE10325927501996",
|
||||
"counterpartName": "Strosin-Veum",
|
||||
"amount": 5.83,
|
||||
},
|
||||
},
|
||||
# return list of transactions on first call, empty list on second call.
|
||||
self.mock_get_transactions = lambda: mock.patch(
|
||||
_interface_class + "._get_transactions",
|
||||
side_effect=[
|
||||
THREE_TRANSACTIONS,
|
||||
EMPTY_TRANSACTIONS,
|
||||
],
|
||||
)
|
||||
|
||||
@@ -153,16 +156,18 @@ class TestAccountBankAccountStatementImportOnlineQonto(common.TransactionCase):
|
||||
line_form.amount = 100
|
||||
initial_statement = st_form.save()
|
||||
initial_statement.button_post()
|
||||
with self.mock_transaction(), self.mock_header(), self.mock_synchronisation(), self.mock_account_ids(): # noqa: B950
|
||||
with self.mock_login(), self.mock_set_access_account(), self.mock_get_transactions(): # noqa: B950
|
||||
vals = {
|
||||
"provider_ids": self.provider.ids,
|
||||
"provider_ids": [(4, self.provider.id)],
|
||||
"date_since": datetime(2019, 11, 4),
|
||||
"date_until": datetime(2019, 11, 5),
|
||||
}
|
||||
wizard = self.AccStatemenPull.with_context(
|
||||
wizard = self.AccountStatementPull.with_context(
|
||||
active_model="account.journal",
|
||||
active_id=self.journal.id,
|
||||
).create(vals)
|
||||
# For some reason the provider is not set in the create.
|
||||
wizard.provider_ids = self.provider
|
||||
wizard.action_pull()
|
||||
statements = self.AccountBankStatement.search(
|
||||
[("journal_id", "=", self.journal.id)]
|
||||
@@ -170,26 +175,33 @@ class TestAccountBankAccountStatementImportOnlineQonto(common.TransactionCase):
|
||||
new_statement = statements - initial_statement
|
||||
self.assertEqual(len(new_statement.line_ids), 1)
|
||||
self.assertEqual(new_statement.balance_start, 100)
|
||||
self.assertEqual(new_statement.balance_end_real, 105.83)
|
||||
self.assertEqual(new_statement.balance_end, 105.83)
|
||||
# Ponto does not give balance info in transactions.
|
||||
# self.assertEqual(new_statement.balance_end_real, 105.83)
|
||||
|
||||
def test_ponto(self):
|
||||
with self.mock_transaction(), self.mock_header(), self.mock_synchronisation(), self.mock_account_ids(): # noqa: B950
|
||||
with self.mock_login(), self.mock_set_access_account(), self.mock_get_transactions(): # noqa: B950
|
||||
vals = {
|
||||
"provider_ids": self.provider.ids,
|
||||
"provider_ids": [(4, self.provider.id)],
|
||||
"date_since": datetime(2019, 11, 3),
|
||||
"date_until": datetime(2019, 11, 17),
|
||||
}
|
||||
wizard = self.AccStatemenPull.with_context(
|
||||
wizard = self.AccountStatementPull.with_context(
|
||||
active_model="account.journal",
|
||||
active_id=self.journal.id,
|
||||
).create(vals)
|
||||
# To get all the moves at once
|
||||
self.provider.statement_creation_mode = "monthly"
|
||||
# For some reason the provider is not set in the create.
|
||||
wizard.provider_ids = self.provider
|
||||
wizard.action_pull()
|
||||
statement = self.AccountBankStatement.search(
|
||||
[("journal_id", "=", self.journal.id)]
|
||||
)
|
||||
self.assertEqual(len(statement), 1)
|
||||
self.assertEqual(len(statement.line_ids), 3)
|
||||
self.assertEqual(statement.line_ids.mapped("amount"), [6.08, 5.48, 5.83])
|
||||
self.assertEqual(statement.balance_end_real, 17.39)
|
||||
sorted_amounts = sorted(statement.line_ids.mapped("amount"))
|
||||
self.assertEqual(sorted_amounts, [5.48, 5.83, 6.08])
|
||||
self.assertEqual(statement.balance_end, 17.39)
|
||||
# Ponto does not give balance info in transactions.
|
||||
# self.assertEqual(statement.balance_end_real, 17.39)
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
# Copyright 2022 Therp BV <https://therp.nl>.
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
import json
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from odoo import fields
|
||||
from odoo.tests import common
|
||||
|
||||
from .test_account_statement_import_online_ponto import THREE_TRANSACTIONS
|
||||
|
||||
|
||||
class TestPontoInterface(common.TransactionCase):
|
||||
post_install = True
|
||||
|
||||
@patch("requests.post")
|
||||
def test_login(self, requests_post):
|
||||
"""Check Ponto API login."""
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.text = json.dumps(
|
||||
{
|
||||
"access_token": "live_the_token",
|
||||
"expires_in": 1799,
|
||||
"scope": "ai",
|
||||
"token_type": "bearer",
|
||||
}
|
||||
)
|
||||
requests_post.return_value = mock_response
|
||||
interface_model = self.env["ponto.interface"]
|
||||
access_data = interface_model._login("uncle_john", "secret")
|
||||
self.assertEqual(access_data["access_token"], "live_the_token")
|
||||
self.assertIn("token_expiration", access_data)
|
||||
|
||||
@patch("requests.get")
|
||||
def test_set_access_account(self, requests_get):
|
||||
"""Test getting account data for Ponto access."""
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.text = json.dumps(
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "wrong_id",
|
||||
"attributes": {
|
||||
"reference": "NL66ABNA123456789",
|
||||
},
|
||||
},
|
||||
{
|
||||
"id": "2ad3df83-be01-47cf-a6be-cf0de5cb4c99",
|
||||
"attributes": {
|
||||
"reference": "NL66RABO123456789",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
)
|
||||
requests_get.return_value = mock_response
|
||||
# Start of actual test.
|
||||
access_data = self._get_access_dict(include_account=False)
|
||||
interface_model = self.env["ponto.interface"]
|
||||
interface_model._set_access_account(access_data, "NL66RABO123456789")
|
||||
self.assertIn("ponto_account", access_data)
|
||||
self.assertEqual(
|
||||
access_data["ponto_account"], "2ad3df83-be01-47cf-a6be-cf0de5cb4c99"
|
||||
)
|
||||
|
||||
@patch("requests.get")
|
||||
def test_get_transactions(self, requests_get):
|
||||
"""Test getting transactions from Ponto."""
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 200
|
||||
# Key "data" will contain a list of transactions.
|
||||
mock_response.text = json.dumps({"data": THREE_TRANSACTIONS})
|
||||
requests_get.return_value = mock_response
|
||||
# Start of actual test.
|
||||
access_data = self._get_access_dict()
|
||||
interface_model = self.env["ponto.interface"]
|
||||
transactions = interface_model._get_transactions(access_data, False)
|
||||
self.assertEqual(len(transactions), 3)
|
||||
self.assertEqual(transactions[2]["id"], "b21a6c65-1c52-4ba6-8cbc-127d2b2d85ff")
|
||||
self.assertEqual(
|
||||
transactions[2]["attributes"]["counterpartReference"], "BE10325927501996"
|
||||
)
|
||||
|
||||
def _get_access_dict(self, include_account=True):
|
||||
"""Get access dict that caches login/account information."""
|
||||
token_expiration = fields.Datetime.now() + relativedelta(seconds=1800)
|
||||
access_data = {
|
||||
"username": "uncle_john",
|
||||
"password": "secret",
|
||||
"access_token": "live_the_token",
|
||||
"token_expiration": token_expiration,
|
||||
}
|
||||
if include_account:
|
||||
access_data["ponto_account"] = "2ad3df83-be01-47cf-a6be-cf0de5cb4c99"
|
||||
return access_data
|
||||
Reference in New Issue
Block a user