diff --git a/addons/payment/models/payment_provider.py b/addons/payment/models/payment_provider.py index abec35a96a8..1b6aa7595a3 100644 --- a/addons/payment/models/payment_provider.py +++ b/addons/payment/models/payment_provider.py @@ -111,10 +111,14 @@ class PaymentProvider(models.Model): # Fees fields fees_active = fields.Boolean(string="Add Extra Fees") - fees_dom_fixed = fields.Float(string="Fixed domestic fees") - fees_dom_var = fields.Float(string="Variable domestic fees (in percents)") - fees_int_fixed = fields.Float(string="Fixed international fees") - fees_int_var = fields.Float(string="Variable international fees (in percents)") + fees_dom_fixed = fields.Monetary( + string="Fixed domestic fees", currency_field='main_currency_id' + ) + fees_dom_var = fields.Float(string="Variable domestic fees") + fees_int_fixed = fields.Monetary( + string="Fixed international fees", currency_field='main_currency_id' + ) + fees_int_var = fields.Float(string="Variable international fees") # Message fields display_as = fields.Char( @@ -315,7 +319,7 @@ class PaymentProvider(models.Model): :return None """ for provider in self: - if any(not 0 <= fee < 100 for fee in (provider.fees_dom_var, provider.fees_int_var)): + if any(not 0 <= fee < 1 for fee in (provider.fees_dom_var, provider.fees_int_var)): raise ValidationError(_("Variable fees must always be positive and below 100%.")) #=== CRUD METHODS ===# @@ -499,9 +503,9 @@ class PaymentProvider(models.Model): The computation is based on the fields `fees_dom_fixed`, `fees_dom_var`, `fees_int_fixed` and `fees_int_var`, and is performed with the formula - :code:`fees = (amount * variable / 100.0 + fixed) / (1 - variable / 100.0)` where the values - of `fixed` and `variable` are taken from either the domestic (`dom`) or international - (`int`) fields, depending on whether the country matches the company's country. + :code:`fees = (amount * variable + fixed) / (1 - variable)` where the values of `fixed` and + `variable` are taken from either the domestic (`dom`) or international (`int`) fields, + depending on whether the country matches the company's country. For a provider to base the computation on different variables, or to use a different formula, it must override this method and return the resulting fees. @@ -522,7 +526,10 @@ class PaymentProvider(models.Model): else: fixed = self.fees_int_fixed variable = self.fees_int_var - fees = (amount * variable / 100.0 + fixed) / (1 - variable / 100.0) + fixed_converted = self.main_currency_id._convert( + fixed, currency, self.company_id, fields.Date.context_today(self) + ) + fees = (amount * variable + fixed_converted) / (1 - variable) return fees def _get_validation_amount(self): diff --git a/addons/payment/tests/common.py b/addons/payment/tests/common.py index e298dca688b..44e3deaa405 100644 --- a/addons/payment/tests/common.py +++ b/addons/payment/tests/common.py @@ -6,13 +6,13 @@ from unittest.mock import patch from lxml import objectify from odoo.fields import Command -from odoo.tests.common import TransactionCase +from odoo.addons.base.tests.common import BaseCommon from odoo.tools.misc import hmac as hmac_tool _logger = logging.getLogger(__name__) -class PaymentCommon(TransactionCase): +class PaymentCommon(BaseCommon): @classmethod def setUpClass(cls): diff --git a/addons/payment/tests/test_payment_provider.py b/addons/payment/tests/test_payment_provider.py index aa4ebb9561b..ccce69d3dcd 100644 --- a/addons/payment/tests/test_payment_provider.py +++ b/addons/payment/tests/test_payment_provider.py @@ -1,12 +1,14 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. +from unittest.mock import patch + from odoo.tests import tagged from odoo.addons.payment.tests.common import PaymentCommon @tagged('-at_install', 'post_install') -class TestPaymentprovider(PaymentCommon): +class TestPaymentProvider(PaymentCommon): def test_published_provider_compatible_with_all_users(self): """ Test that a published provider is always available to all users. """ @@ -67,3 +69,52 @@ class TestPaymentprovider(PaymentCommon): self.company.id, self.partner.id, self.amount, currency_id=self.env.company.currency_id.id, ) self.assertNotIn(self.provider, compatible_providers) + + def test_fees_null_when_deactivated(self): + """ Test that extra fees of a provider are null if these fees are deactivated. """ + self.provider.write({ + 'fees_active': False, + 'fees_dom_fixed': 1.0, + 'fees_dom_var': 0.35, + 'fees_int_fixed': 0.1, + 'fees_int_var': 0.2, + }) + transaction_fees = self.provider._compute_fees( + self.amount, self.currency, self.partner.country_id + ) + self.assertEqual(transaction_fees, 0) + + def test_fees_formula(self): + """ Test that the extra fees of a provider are correctly computed in a domestic case. """ + self.provider.write({ + 'fees_active': True, + 'fees_dom_fixed': 1.0, + 'fees_dom_var': 0.0035, + 'fees_int_fixed': 0., + 'fees_int_var': 0., + }) + transaction_fees = self.currency.round( + self.provider._compute_fees( + self.amount, self.currency_usd, self.provider.company_id.country_id + ) + ) + self.assertEqual(transaction_fees, 4.91) + + def test_fees_with_currency_conversion(self): + """ Test that the conversion of the fees is correctly computed. """ + self.provider.write({ + 'fees_active': True, + 'fees_dom_fixed': 0., + 'fees_dom_var': 0., + 'fees_int_fixed': 1., + 'fees_int_var': 0., + }) + + with patch( + 'odoo.addons.base.models.res_currency.Currency._get_rates', + return_value={self.currency_euro.id: 2., self.currency_usd.id: 1.}, + ): + transaction_fees = self.currency.round( + self.provider._compute_fees(self.amount, self.currency_euro, self.country_france) + ) + self.assertEqual(transaction_fees, 2.) diff --git a/addons/payment/views/payment_provider_views.xml b/addons/payment/views/payment_provider_views.xml index 7896f9f1dd1..25a1668d094 100644 --- a/addons/payment/views/payment_provider_views.xml +++ b/addons/payment/views/payment_provider_views.xml @@ -94,11 +94,19 @@ - - - - - + + + + + + +