[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:
Ronald Portier (Therp BV)
2022-10-02 13:15:38 +02:00
parent fa59c44b22
commit c32a65fe3c
11 changed files with 433 additions and 507 deletions

View File

@@ -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

View File

@@ -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)

View File

@@ -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