diff --git a/addons/payment/tests/common.py b/addons/payment/tests/common.py index f7b6437bf52..7c6c424543e 100644 --- a/addons/payment/tests/common.py +++ b/addons/payment/tests/common.py @@ -99,6 +99,27 @@ class PaymentCommon(PaymentTestUtils): cls.currency = cls.currency_euro cls.partner = cls.default_partner cls.reference = "Test Transaction" + cls.account = cls.company.account_journal_payment_credit_account_id + cls.invoice = cls.env['account.move'].create({ + 'move_type': 'entry', + 'date': '2019-01-01', + 'line_ids': [ + (0, 0, { + 'account_id': cls.account.id, + 'currency_id': cls.currency_euro.id, + 'debit': 100.0, + 'credit': 0.0, + 'amount_currency': 200.0, + }), + (0, 0, { + 'account_id': cls.account.id, + 'currency_id': cls.currency_euro.id, + 'debit': 0.0, + 'credit': 100.0, + 'amount_currency': -200.0, + }), + ], + }) #=== Utils ===# diff --git a/addons/payment/tests/http_common.py b/addons/payment/tests/http_common.py index 5df1b59bf18..31a5d5a9d0e 100644 --- a/addons/payment/tests/http_common.py +++ b/addons/payment/tests/http_common.py @@ -198,8 +198,7 @@ class PaymentHttpCommon(PaymentTestUtils, HttpCase): def portal_transaction(self, **route_kwargs): """/payment/transaction feedback - :returns: processing values for given route_kwargs - :rtype: dict + :return: The response to the json request """ uri = '/payment/transaction' url = self._build_url(uri) diff --git a/addons/payment/tests/test_flows.py b/addons/payment/tests/test_flows.py index 5c6c5de6fcb..e67ed60df50 100644 --- a/addons/payment/tests/test_flows.py +++ b/addons/payment/tests/test_flows.py @@ -1,6 +1,7 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from freezegun import freeze_time +from unittest.mock import patch from odoo.tests import tagged from odoo.tools import mute_logger @@ -223,7 +224,7 @@ class TestFlows(PaymentCommon, PaymentHttpCommon): response = self.portal_pay(**route_values) self.assertTrue(response.url.startswith(self._build_url('/web/login?redirect='))) - # Pay without a partner specified (but loggged) --> pay with the partner of current user. + # Pay without a partner specified (but logged) --> pay with the partner of current user. self.authenticate(self.portal_user.login, self.portal_user.login) tx_context = self.get_tx_checkout_context(**route_values) self.assertEqual(tx_context['partner_id'], self.portal_partner.id) @@ -237,7 +238,7 @@ class TestFlows(PaymentCommon, PaymentHttpCommon): response = self.portal_pay(**route_values) self.assertTrue(response.url.startswith(self._build_url('/web/login?redirect='))) - # Pay without a partner specified (but loggged) --> pay with the partner of current user. + # Pay without a partner specified (but logged) --> pay with the partner of current user. self.authenticate(self.portal_user.login, self.portal_user.login) tx_context = self.get_tx_checkout_context(**route_values) self.assertEqual(tx_context['partner_id'], self.portal_partner.id) @@ -265,34 +266,12 @@ class TestFlows(PaymentCommon, PaymentHttpCommon): def test_invoice_payment_flow(self): """Test the payment of an invoice through the payment/pay route""" - # Create a dummy invoice - account = self.env['account.account'].search([('company_id', '=', self.env.company.id)], limit=1) - invoice = self.env['account.move'].create({ - 'move_type': 'entry', - 'date': '2019-01-01', - 'line_ids': [ - (0, 0, { - 'account_id': account.id, - 'currency_id': self.currency_euro.id, - 'debit': 100.0, - 'credit': 0.0, - 'amount_currency': 200.0, - }), - (0, 0, { - 'account_id': account.id, - 'currency_id': self.currency_euro.id, - 'debit': 0.0, - 'credit': 100.0, - 'amount_currency': -200.0, - }), - ], - }) # Pay for this invoice (no impact even if amounts do not match) route_values = self._prepare_pay_values() - route_values['invoice_id'] = invoice.id + route_values['invoice_id'] = self.invoice.id tx_context = self.get_tx_checkout_context(**route_values) - self.assertEqual(tx_context['invoice_id'], invoice.id) + self.assertEqual(tx_context['invoice_id'], self.invoice.id) # payment/transaction route_values = { @@ -318,8 +297,8 @@ class TestFlows(PaymentCommon, PaymentHttpCommon): # Note: strangely, the check # self.assertEqual(tx_sudo.invoice_ids, invoice) # doesn't work, and cache invalidation doesn't work either. - invoice.invalidate_cache(['transaction_ids']) - self.assertEqual(invoice.transaction_ids, tx_sudo) + self.invoice.invalidate_cache(['transaction_ids']) + self.assertEqual(self.invoice.transaction_ids, tx_sudo) def test_transaction_wrong_flow(self): transaction_values = self._prepare_pay_values() @@ -380,3 +359,42 @@ class TestFlows(PaymentCommon, PaymentHttpCommon): self.assertEqual(manage_context['partner_id'], self.partner.id) self.assertEqual(manage_context['acquirer_ids'], [acquirer_b.id]) self.assertEqual(manage_context['token_ids'], []) + + def test_number_of_payment_request_from_portal(self): + self.authenticate(self.portal_user.login, self.portal_user.login) + self.partner = self.portal_partner + self.user = self.portal_user + self._test_number_of_payment_request_from_portal('direct', 0) + self._test_number_of_payment_request_from_portal('redirect', 0) + self._test_number_of_payment_request_from_portal('token', 1) + + @mute_logger('odoo.addons.payment.models.payment_transaction') + def _test_number_of_payment_request_from_portal(self, flow, calls): + """ Test the number of payment requests made from the portal for a given online payment flow + + :param str flow: The online payment flow to test ('direct', 'redirect', or 'token') + :param int calls: The expected number of call to `_send_payment_request` + """ + payment_option_id = self.create_token().id if flow == 'token' else self.acquirer.id + data = { + 'amount': self.amount, + 'currency_id': self.currency.id, + 'partner_id': self.partner.id, + 'access_token': self._generate_test_access_token( + self.partner.id, self.amount, self.currency.id + ), + 'payment_option_id': payment_option_id, + 'reference_prefix': 'test', + 'tokenization_requested': True, + 'landing_route': 'Test', + 'is_validation': False, + 'invoice_id': self.invoice.id, + 'flow': flow, + } + + with patch( + 'odoo.addons.payment.models.payment_transaction.PaymentTransaction' + '._send_payment_request' + ) as patched: + self.get_processing_values(**data) + self.assertEqual(patched.call_count, calls) diff --git a/addons/payment/tests/test_payments.py b/addons/payment/tests/test_payments.py index 737db7c4e85..9fac2a87ac5 100644 --- a/addons/payment/tests/test_payments.py +++ b/addons/payment/tests/test_payments.py @@ -1,6 +1,7 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from odoo.tests import tagged +from unittest.mock import patch from odoo.addons.payment.tests.common import PaymentCommon @@ -115,3 +116,27 @@ class TestPayments(PaymentCommon): 1, msg="The refunds count should only consider transactions with operation 'refund'." ) + + def test_action_post_call_send_payment_request_only_once(self): + payment_token = self.create_token() + payment_without_token = self.env['account.payment'].create({ + 'payment_type': 'inbound', + 'partner_type': 'customer', + 'amount': 2000.0, + 'date': '2019-01-01', + 'currency_id': self.currency.id, + 'partner_id': self.partner.id, + 'journal_id': self.acquirer.journal_id.id, + 'payment_method_line_id': self.inbound_payment_method_line.id, + }) + payment_with_token = payment_without_token.copy() + payment_with_token.payment_token_id = payment_token.id + + with patch( + 'odoo.addons.payment.models.payment_transaction.PaymentTransaction' + '._send_payment_request' + ) as patched: + payment_without_token.action_post() + patched.assert_not_called() + payment_with_token.action_post() + patched.assert_called_once()