From 90af85c2e4fdec0a6ac7d70663f90443fad120d2 Mon Sep 17 00:00:00 2001 From: "Valentin Vallaeys (vava)" Date: Fri, 23 Sep 2022 13:58:18 +0000 Subject: [PATCH] [IMP] payment(_*): show available currencies for payment providers Before this commit, the lists of supported currencies by payment provider were hard-coded in the Python scripts, which made them unavailable to the users. With this commit, the implemented initial lists of supported currencies are displayed on the form view and are editable, because Odoo lists may not be up-to-date. Empty lists do not trigger any filtering on the payment providers to access payment methods. For Authorize.net and Asiapay payment providers, the specific `(authorize,asiapay)_currency_id` are removed and the generic payment provider field `available_currency_ids` is restricted to a single-item list when one of those providers is enabled. task-2926016 closes odoo/odoo#101018 Related: odoo/enterprise#34158 Related: odoo/documentation#2788 Related: odoo/upgrade#4069 Signed-off-by: Antoine Vandevenne (anv) --- .../models/payment_provider.py | 2 +- addons/payment/models/payment_provider.py | 59 +++++++++- addons/payment/tests/common.py | 1 + addons/payment/tests/test_payment_provider.py | 25 +++++ .../payment/views/payment_provider_views.xml | 11 +- .../payment_alipay/models/payment_provider.py | 3 +- addons/payment_asiapay/data/neutralize.sql | 1 - .../models/payment_provider.py | 35 ++---- .../models/payment_transaction.py | 2 +- addons/payment_asiapay/tests/common.py | 4 +- .../views/payment_provider_views.xml | 6 +- .../models/payment_provider.py | 31 +++--- addons/payment_authorize/tests/common.py | 4 +- .../payment_authorize/tests/test_authorize.py | 4 +- .../views/payment_provider_views.xml | 17 ++- addons/payment_buckaroo/const.py | 14 +++ .../models/payment_provider.py | 11 ++ addons/payment_flutterwave/const.py | 2 +- .../models/payment_provider.py | 21 ++-- .../models/payment_provider.py | 22 ++-- .../tests/test_payment_transaction.py | 2 +- addons/payment_mollie/const.py | 38 +++++-- .../payment_mollie/models/payment_provider.py | 20 ++-- addons/payment_paypal/const.py | 2 + .../payment_paypal/models/payment_provider.py | 20 ++-- addons/payment_payulatam/const.py | 14 +++ .../models/payment_provider.py | 23 ++-- .../payment_payulatam/tests/test_payulatam.py | 10 -- .../models/payment_provider.py | 18 ++-- addons/payment_razorpay/const.py | 101 ++++++++++++++++-- .../models/payment_provider.py | 20 ++-- addons/payment_sips/{models => }/const.py | 7 +- .../payment_sips/models/payment_provider.py | 22 ++-- .../models/payment_transaction.py | 2 +- addons/payment_sips/tests/test_sips.py | 8 -- 35 files changed, 385 insertions(+), 197 deletions(-) create mode 100644 addons/payment_payulatam/const.py rename addons/payment_sips/{models => }/const.py (79%) diff --git a/addons/account_payment/models/payment_provider.py b/addons/account_payment/models/payment_provider.py index fad7ef33640..e218fa9990b 100644 --- a/addons/account_payment/models/payment_provider.py +++ b/addons/account_payment/models/payment_provider.py @@ -3,7 +3,7 @@ from odoo import api, fields, models -class Paymentprovider(models.Model): +class PaymentProvider(models.Model): _inherit = 'payment.provider' journal_id = fields.Many2one( diff --git a/addons/payment/models/payment_provider.py b/addons/payment/models/payment_provider.py index 1b6aa7595a3..e2128ca620c 100644 --- a/addons/payment/models/payment_provider.py +++ b/addons/payment/models/payment_provider.py @@ -102,6 +102,19 @@ class PaymentProvider(models.Model): column1='payment_id', column2='country_id', ) + available_currency_ids = fields.Many2many( + string="Currencies", + help="The currencies available with this payment provider. Leave empty not to restrict " + "any.", + comodel_name='res.currency', + relation='payment_currency_rel', + column1="payment_provider_id", + column2="currency_id", + compute='_compute_available_currency_ids', + store=True, + readonly=False, + context={'active_test': False}, + ) maximum_amount = fields.Monetary( string="Maximum Amount", help="The maximum payment amount that this payment provider is available for. Leave blank " @@ -190,6 +203,22 @@ class PaymentProvider(models.Model): #=== COMPUTE METHODS ===# + @api.depends('code') + def _compute_available_currency_ids(self): + """ Compute the available currencies based on their support by the providers. + + If the provider does not filter out any currency, the field is left empty for UX reasons. + + :return: None + """ + all_currencies = self.env['res.currency'].with_context(active_test=False).search([]) + for provider in self: + supported_currencies = provider._get_supported_currencies() + if supported_currencies < all_currencies: # Some currencies have been filtered out. + provider.available_currency_ids = supported_currencies + else: + provider.available_currency_ids = None + @api.depends('state', 'module_state') def _compute_color(self): """ Update the color of the kanban card based on the state of the provider. @@ -242,6 +271,7 @@ class PaymentProvider(models.Model): 'show_cancel_msg': True, }) + @api.depends('code') def _compute_feature_support_fields(self): """ Compute the feature support fields based on the provider. @@ -415,8 +445,8 @@ class PaymentProvider(models.Model): """ Select and return the providers matching the criteria. The criteria are that providers must not be disabled, be in the company that is provided, - and support the country of the partner if it exists. The criteria can be further refined - by providing the keyword arguments. + support the country of the partner if it exists, and be compatible with the currency if + provided. The criteria can be further refined by providing the keyword arguments. :param int company_id: The company to which providers must belong, as a `res.company` id. :param int partner_id: The partner making the payment, as a `res.partner` id. @@ -462,6 +492,16 @@ class PaymentProvider(models.Model): ] ]) + # Handle the available currencies (only if supported currencies list is not empty). + if currency: + domain = expression.AND([ + domain, [ + '|', + ('available_currency_ids', '=', False), + ('available_currency_ids', 'in', [currency.id]), + ] + ]) + # Handle tokenization support requirements. if force_tokenization or self._is_tokenization_required(**kwargs): domain = expression.AND([domain, [('allow_tokenization', '=', True)]]) @@ -473,6 +513,21 @@ class PaymentProvider(models.Model): compatible_providers = self.env['payment.provider'].search(domain) return compatible_providers + def _get_supported_currencies(self): + """ Return the supported currencies for the payment provider. + + By default, all currencies are considered supported, including the inactive ones. For a + provider to filter out specific currencies, it must override this method and return the + subset of supported currencies. + + Note: `self.ensure_one()` + + :return: The supported currencies. + :rtype: res.currency + """ + self.ensure_one() + return self.env['res.currency'].with_context(active_test=False).search([]) + def _is_tokenization_required(self, **kwargs): """ Return whether tokenizing the transaction is required given its context. diff --git a/addons/payment/tests/common.py b/addons/payment/tests/common.py index 44e3deaa405..6e92dc5ffc6 100644 --- a/addons/payment/tests/common.py +++ b/addons/payment/tests/common.py @@ -79,6 +79,7 @@ class PaymentCommon(BaseCommon): 'is_published': True, 'allow_tokenization': True, 'redirect_form_view_id': redirect_form.id, + 'available_currency_ids': [Command.set((cls.currency_euro + cls.currency_usd).ids)], }) cls.provider = cls.dummy_provider diff --git a/addons/payment/tests/test_payment_provider.py b/addons/payment/tests/test_payment_provider.py index cd0233ef68f..614ce799b25 100644 --- a/addons/payment/tests/test_payment_provider.py +++ b/addons/payment/tests/test_payment_provider.py @@ -2,6 +2,7 @@ from unittest.mock import patch +from odoo import Command from odoo.tests import tagged from odoo.addons.payment.tests.common import PaymentCommon @@ -70,6 +71,30 @@ class TestPaymentProvider(PaymentCommon): ) self.assertNotIn(self.provider, compatible_providers) + def test_provider_compatible_with_available_currencies(self): + """ Test that the provider is compatible with its available currencies. """ + compatible_providers = self.provider._get_compatible_providers( + self.company.id, self.partner.id, self.amount, currency_id=self.currency_euro.id + ) + self.assertIn(self.provider, compatible_providers) + + def test_provider_not_compatible_with_unavailable_currencies(self): + """ Test that the provider is not compatible with a currency that is not available. """ + # Make sure the list of available currencies is not empty. + self.provider.available_currency_ids = [Command.unlink(self.currency_usd.id)] + compatible_providers = self.provider._get_compatible_providers( + self.company.id, self.partner.id, self.amount, currency_id=self.currency_usd.id + ) + self.assertNotIn(self.provider, compatible_providers) + + def test_provider_compatible_when_no_available_currency_set(self): + """ Test that the provider is always compatible when no available currency is set. """ + self.provider.available_currency_ids = [Command.clear()] + compatible_providers = self.provider._get_compatible_providers( + self.company.id, self.partner.id, self.amount, currency_id=self.currency_euro.id + ) + self.assertIn(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({ diff --git a/addons/payment/views/payment_provider_views.xml b/addons/payment/views/payment_provider_views.xml index 25a1668d094..4adf8766460 100644 --- a/addons/payment/views/payment_provider_views.xml +++ b/addons/payment/views/payment_provider_views.xml @@ -84,10 +84,19 @@ + diff --git a/addons/payment_alipay/models/payment_provider.py b/addons/payment_alipay/models/payment_provider.py index 6af246c92b2..507c0e6ff08 100644 --- a/addons/payment_alipay/models/payment_provider.py +++ b/addons/payment_alipay/models/payment_provider.py @@ -41,7 +41,8 @@ class PaymentProvider(models.Model): @api.model def _get_compatible_providers(self, *args, currency_id=None, **kwargs): - """ Override of payment to unlist Alipay providers for unsupported currencies. """ + """ Override of payment to unlist Alipay providers when the currency is not CNY in case of + express checkout. """ providers = super()._get_compatible_providers(*args, currency_id=currency_id, **kwargs) currency = self.env['res.currency'].browse(currency_id).exists() diff --git a/addons/payment_asiapay/data/neutralize.sql b/addons/payment_asiapay/data/neutralize.sql index e9674ac07ec..8c5a2229a45 100644 --- a/addons/payment_asiapay/data/neutralize.sql +++ b/addons/payment_asiapay/data/neutralize.sql @@ -1,6 +1,5 @@ -- disable asiapay payment provider UPDATE payment_provider SET asiapay_merchant_id = NULL, - asiapay_currency_id = NULL, asiapay_secure_hash_secret = NULL, asiapay_secure_hash_function = NULL; diff --git a/addons/payment_asiapay/models/payment_provider.py b/addons/payment_asiapay/models/payment_provider.py index 8537df4cb1e..a4373be2584 100644 --- a/addons/payment_asiapay/models/payment_provider.py +++ b/addons/payment_asiapay/models/payment_provider.py @@ -2,7 +2,8 @@ from hashlib import new as hashnew -from odoo import api, fields, models +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError from odoo.addons.payment_asiapay import const @@ -10,10 +11,6 @@ from odoo.addons.payment_asiapay import const class PaymentProvider(models.Model): _inherit = 'payment.provider' - def _domain_asiapay_currency_id(self): - currency_xmlids = [f'base.{key}' for key in const.CURRENCY_MAPPING] - return [('id', 'in', [self.env.ref(xmlid).id for xmlid in currency_xmlids])] - code = fields.Selection( selection_add=[('asiapay', "AsiaPay")], ondelete={'asiapay': 'set default'} ) @@ -22,13 +19,6 @@ class PaymentProvider(models.Model): help="The Merchant ID solely used to identify your AsiaPay account.", required_if_provider='asiapay', ) - asiapay_currency_id = fields.Many2one( - string="AsiaPay Currency", - help="The currency associated to your AsiaPay account.", - comodel_name='res.currency', - domain=_domain_asiapay_currency_id, - required_if_provider='asiapay', - ) asiapay_secure_hash_secret = fields.Char( string="AsiaPay Secure Hash Secret", required_if_provider='asiapay', @@ -42,21 +32,16 @@ class PaymentProvider(models.Model): required_if_provider='asiapay', ) + # ==== CONSTRAINT METHODS ===# + + @api.constrains('available_currency_ids', 'state') + def _limit_available_currency_ids(self): + for provider in self.filtered(lambda p: p.code == 'asiapay'): + if len(provider.available_currency_ids) > 1 and provider.state != 'disabled': + raise ValidationError(_("Only one currency can be selected by AsiaPay account.")) + # === BUSINESS METHODS ===# - @api.model - def _get_compatible_providers(self, *args, currency_id=None, **kwargs): - """ Override of `payment` to filter out AsiaPay providers for unsupported currencies. """ - providers = super()._get_compatible_providers(*args, currency_id=currency_id, **kwargs) - - currency = self.env['res.currency'].browse(currency_id).exists() - if currency: - providers = providers.filtered( - lambda p: p.code != 'asiapay' or currency == p.asiapay_currency_id - ) - - return providers - def _asiapay_get_api_url(self): """ Return the URL of the API corresponding to the provider's state. diff --git a/addons/payment_asiapay/models/payment_transaction.py b/addons/payment_asiapay/models/payment_transaction.py index b5a2e1e6faa..7aa6728ff18 100644 --- a/addons/payment_asiapay/models/payment_transaction.py +++ b/addons/payment_asiapay/models/payment_transaction.py @@ -88,7 +88,7 @@ class PaymentTransaction(models.Model): 'merchant_id': self.provider_id.asiapay_merchant_id, 'amount': self.amount, 'reference': self.reference, - 'currency_code': const.CURRENCY_MAPPING[self.provider_id.asiapay_currency_id.name], + 'currency_code': const.CURRENCY_MAPPING[self.provider_id.available_currency_ids[0].name], 'mps_mode': 'SCP', 'return_url': urls.url_join(base_url, AsiaPayController._return_url), 'payment_type': 'N', diff --git a/addons/payment_asiapay/tests/common.py b/addons/payment_asiapay/tests/common.py index a0a3b777d6e..75e6582cc2f 100644 --- a/addons/payment_asiapay/tests/common.py +++ b/addons/payment_asiapay/tests/common.py @@ -1,5 +1,7 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. +from odoo import Command + from odoo.addons.payment.tests.common import PaymentCommon @@ -11,9 +13,9 @@ class AsiaPayCommon(PaymentCommon): cls.asiapay = cls._prepare_provider('asiapay', update_values={ 'asiapay_merchant_id': '123456789', - 'asiapay_currency_id': cls.currency_euro, 'asiapay_secure_hash_secret': 'coincoin_motherducker', 'asiapay_secure_hash_function': 'sha1', + 'available_currency_ids': [Command.set(cls.currency_euro.ids)], }) cls.provider = cls.asiapay diff --git a/addons/payment_asiapay/views/payment_provider_views.xml b/addons/payment_asiapay/views/payment_provider_views.xml index 4606b82b2d1..be784509ab0 100644 --- a/addons/payment_asiapay/views/payment_provider_views.xml +++ b/addons/payment_asiapay/views/payment_provider_views.xml @@ -11,9 +11,6 @@ - + + {'required': [('code', '=', 'asiapay'), ('state', '!=', 'disabled')]} + diff --git a/addons/payment_authorize/models/payment_provider.py b/addons/payment_authorize/models/payment_provider.py index 2b4e3363abc..d0934cf9f34 100644 --- a/addons/payment_authorize/models/payment_provider.py +++ b/addons/payment_authorize/models/payment_provider.py @@ -27,10 +27,6 @@ class PaymentProvider(models.Model): authorize_client_key = fields.Char( string="API Client Key", help="The public client key. To generate directly from Odoo or from Authorize.Net backend.") - # Authorize.Net supports only one currency: "One gateway account is required for each currency" - # See https://community.developer.authorize.net/t5/The-Authorize-Net-Developer-Blog/Authorize-Net-UK-Europe-Update/ba-p/35957 - authorize_currency_id = fields.Many2one( - string="Authorize Currency", comodel_name='res.currency') authorize_payment_method_type = fields.Selection( string="Allow Payments From", help="Determines with what payment method the customer can pay.", @@ -51,6 +47,16 @@ class PaymentProvider(models.Model): "method type on the duplicated provider." )) + # Authorize.Net supports only one currency: "One gateway account is required for each currency" + # See https://community.developer.authorize.net/t5/The-Authorize-Net-Developer-Blog/Authorize-Net-UK-Europe-Update/ba-p/35957 + @api.constrains('available_currency_ids', 'state') + def _limit_available_currency_ids(self): + for provider in self.filtered(lambda p: p.code == 'authorize'): + if len(provider.available_currency_ids) > 1 and provider.state != 'disabled': + raise ValidationError( + _("Only one currency can be selected by Authorize.Net account.") + ) + #=== COMPUTE METHODS ===# def _compute_feature_support_fields(self): @@ -104,24 +110,11 @@ class PaymentProvider(models.Model): raise UserError(_("Could not fetch merchant details:\n%s", res_content['err_msg'])) currency = self.env['res.currency'].search([('name', 'in', res_content.get('currencies'))]) - self.authorize_currency_id = currency + self.available_currency_ids = [Command.set(currency.ids)] self.authorize_client_key = res_content.get('publicClientKey') # === BUSINESS METHODS ===# - @api.model - def _get_compatible_providers(self, *args, currency_id=None, **kwargs): - """ Override of payment to unlist Authorize providers for unsupported currencies. """ - providers = super()._get_compatible_providers(*args, currency_id=currency_id, **kwargs) - - currency = self.env['res.currency'].browse(currency_id).exists() - if currency: - providers = providers.filtered( - lambda p: p.code != 'authorize' or currency == p.authorize_currency_id - ) - - return providers - def _get_validation_amount(self): """ Override of payment to return the amount for Authorize.Net validation operations. @@ -144,4 +137,4 @@ class PaymentProvider(models.Model): if self.code != 'authorize': return res - return self.authorize_currency_id + return self.available_currency_ids[0] diff --git a/addons/payment_authorize/tests/common.py b/addons/payment_authorize/tests/common.py index 152deef461e..db06872a653 100644 --- a/addons/payment_authorize/tests/common.py +++ b/addons/payment_authorize/tests/common.py @@ -1,5 +1,7 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. +from odoo.fields import Command + from odoo.addons.payment.tests.common import PaymentCommon @@ -13,7 +15,7 @@ class AuthorizeCommon(PaymentCommon): 'authorize_login': 'dummy', 'authorize_transaction_key': 'dummy', 'authorize_signature_key': '00000000', - 'authorize_currency_id': cls.currency_usd.id, + 'available_currency_ids': [Command.set(cls.currency_usd.ids)] }) cls.provider = cls.authorize diff --git a/addons/payment_authorize/tests/test_authorize.py b/addons/payment_authorize/tests/test_authorize.py index 54dc046daf0..e0d0fb382d0 100644 --- a/addons/payment_authorize/tests/test_authorize.py +++ b/addons/payment_authorize/tests/test_authorize.py @@ -14,7 +14,7 @@ from .common import AuthorizeCommon class AuthorizeTest(AuthorizeCommon): def test_compatible_providers(self): - # Note: in the test common, 'USD' is specified as authorize_currency_id + # Note: in the test common, 'USD' is specified as the currency linked to the user account. unsupported_currency = self._prepare_currency('CHF') providers = self.env['payment.provider']._get_compatible_providers( self.company.id, self.partner.id, self.amount, currency_id=unsupported_currency.id @@ -43,6 +43,6 @@ class AuthorizeTest(AuthorizeCommon): )) def test_validation(self): - self.assertEqual(self.authorize.authorize_currency_id, self.currency_usd) + self.assertEqual(self.authorize.available_currency_ids[0], self.currency_usd) self.assertEqual(self.authorize._get_validation_amount(), 0.01) self.assertEqual(self.authorize._get_validation_currency(), self.currency_usd) diff --git a/addons/payment_authorize/views/payment_provider_views.xml b/addons/payment_authorize/views/payment_provider_views.xml index e2ce31ae3dd..0e7f7ff0ab7 100644 --- a/addons/payment_authorize/views/payment_provider_views.xml +++ b/addons/payment_authorize/views/payment_provider_views.xml @@ -27,15 +27,14 @@ - - +
+
diff --git a/addons/payment_buckaroo/const.py b/addons/payment_buckaroo/const.py index e8da19d6da2..0521f5db781 100644 --- a/addons/payment_buckaroo/const.py +++ b/addons/payment_buckaroo/const.py @@ -10,3 +10,17 @@ STATUS_CODES_MAPPING = { 'refused': (690,), 'error': (490, 491, 492,), } + +# The currencies supported by Buckaroo, in ISO 4217 format. +# See https://support.buckaroo.eu/frequently-asked-questions +# Last seen online: 7 November 2022. +SUPPORTED_CURRENCIES = [ + 'EUR', + 'GBP', + 'PLN', + 'DKK', + 'NOK', + 'SEK', + 'CHF', + 'USD', +] diff --git a/addons/payment_buckaroo/models/payment_provider.py b/addons/payment_buckaroo/models/payment_provider.py index e26fdafc199..05744e1b1ad 100644 --- a/addons/payment_buckaroo/models/payment_provider.py +++ b/addons/payment_buckaroo/models/payment_provider.py @@ -6,6 +6,8 @@ from werkzeug import urls from odoo import fields, models +from odoo.addons.payment_buckaroo.const import SUPPORTED_CURRENCIES + class PaymentProvider(models.Model): _inherit = 'payment.provider' @@ -18,6 +20,15 @@ class PaymentProvider(models.Model): buckaroo_secret_key = fields.Char( string="Buckaroo Secret Key", required_if_provider='buckaroo', groups='base.group_system') + def _get_supported_currencies(self): + """ Override of `payment` to return the supported currencies. """ + supported_currencies = super()._get_supported_currencies() + if self.code == 'buckaroo': + supported_currencies = supported_currencies.filtered( + lambda c: c.name in SUPPORTED_CURRENCIES + ) + return supported_currencies + def _buckaroo_get_api_url(self): """ Return the API URL according to the state. diff --git a/addons/payment_flutterwave/const.py b/addons/payment_flutterwave/const.py index c38618a1d55..11eab44bcb1 100644 --- a/addons/payment_flutterwave/const.py +++ b/addons/payment_flutterwave/const.py @@ -7,6 +7,7 @@ SUPPORTED_CURRENCIES = [ 'GBP', 'CAD', + 'XAF', 'CLP', 'COP', 'EGP', @@ -24,7 +25,6 @@ SUPPORTED_CURRENCIES = [ 'TZS', 'UGX', 'USD', - 'XAF', 'XOF', 'ZMW', ] diff --git a/addons/payment_flutterwave/models/payment_provider.py b/addons/payment_flutterwave/models/payment_provider.py index 0d05e92c1eb..5735dbdc8b0 100644 --- a/addons/payment_flutterwave/models/payment_provider.py +++ b/addons/payment_flutterwave/models/payment_provider.py @@ -49,19 +49,24 @@ class PaymentProvider(models.Model): # === BUSINESS METHODS ===# @api.model - def _get_compatible_providers(self, *args, currency_id=None, is_validation=False, **kwargs): - """ Override of payment to filter out Flutterwave providers for unsupported currencies or - for validation operations. """ - providers = super()._get_compatible_providers( - *args, currency_id=currency_id, is_validation=is_validation, **kwargs - ) + def _get_compatible_providers(self, *args, is_validation=False, **kwargs): + """ Override of `payment` to filter out Flutterwave providers for validation operations. """ + providers = super()._get_compatible_providers(*args, is_validation=is_validation, **kwargs) - currency = self.env['res.currency'].browse(currency_id).exists() - if (currency and currency.name not in SUPPORTED_CURRENCIES) or is_validation: + if is_validation: providers = providers.filtered(lambda p: p.code != 'flutterwave') return providers + def _get_supported_currencies(self): + """ Override of `payment` to return the supported currencies. """ + supported_currencies = super()._get_supported_currencies() + if self.code == 'flutterwave': + supported_currencies = supported_currencies.filtered( + lambda c: c.name in SUPPORTED_CURRENCIES + ) + return supported_currencies + def _flutterwave_make_request(self, endpoint, payload=None, method='POST'): """ Make a request to Flutterwave API at the specified endpoint. diff --git a/addons/payment_mercado_pago/models/payment_provider.py b/addons/payment_mercado_pago/models/payment_provider.py index 1e963422560..a63267ed42b 100644 --- a/addons/payment_mercado_pago/models/payment_provider.py +++ b/addons/payment_mercado_pago/models/payment_provider.py @@ -6,7 +6,7 @@ import pprint import requests from werkzeug import urls -from odoo import _, api, fields, models +from odoo import _, fields, models from odoo.exceptions import ValidationError from odoo.addons.payment_mercado_pago.const import SUPPORTED_CURRENCIES @@ -15,7 +15,7 @@ from odoo.addons.payment_mercado_pago.const import SUPPORTED_CURRENCIES _logger = logging.getLogger(__name__) -class Paymentprovider(models.Model): +class PaymentProvider(models.Model): _inherit = 'payment.provider' code = fields.Selection( @@ -29,16 +29,14 @@ class Paymentprovider(models.Model): # === BUSINESS METHODS === # - @api.model - def _get_compatible_providers(self, *args, currency_id=None, **kwargs): - """ Override of `payment` to unlist Mercado Pago providers for unsupported currencies. """ - providers = super()._get_compatible_providers(*args, currency_id=currency_id, **kwargs) - - currency = self.env['res.currency'].browse(currency_id).exists() - if currency and currency.name not in SUPPORTED_CURRENCIES: - providers = providers.filtered(lambda p: p.code != 'mercado_pago') - - return providers + def _get_supported_currencies(self): + """ Override of `payment` to return the supported currencies. """ + supported_currencies = super()._get_supported_currencies() + if self.code == 'mercado_pago': + supported_currencies = supported_currencies.filtered( + lambda c: c.name in SUPPORTED_CURRENCIES + ) + return supported_currencies def _mercado_pago_make_request(self, endpoint, payload=None, method='POST'): """ Make a request to Mercado Pago API at the specified endpoint. diff --git a/addons/payment_mercado_pago/tests/test_payment_transaction.py b/addons/payment_mercado_pago/tests/test_payment_transaction.py index d94bd32d2c3..41f57cfb703 100644 --- a/addons/payment_mercado_pago/tests/test_payment_transaction.py +++ b/addons/payment_mercado_pago/tests/test_payment_transaction.py @@ -62,7 +62,7 @@ class TestPaymentTransaction(MercadoPagoCommon, PaymentHttpCommon): successful payment. """ tx = self._create_transaction(flow='redirect') with patch( - 'odoo.addons.payment_mercado_pago.models.payment_provider.Paymentprovider' + 'odoo.addons.payment_mercado_pago.models.payment_provider.PaymentProvider' '._mercado_pago_make_request', return_value=self.verification_data ): tx._process_notification_data(self.redirect_notification_data) diff --git a/addons/payment_mollie/const.py b/addons/payment_mollie/const.py index a0d68f8d92f..81748c88106 100644 --- a/addons/payment_mollie/const.py +++ b/addons/payment_mollie/const.py @@ -12,11 +12,37 @@ SUPPORTED_LOCALES = [ ] # Currency codes in ISO 4217 format supported by mollie. -# See https://docs.mollie.com/payments/multicurrency +# Note: support varies per payment method. +# See https://docs.mollie.com/payments/multicurrency. Last seen online: 22 September 2022. SUPPORTED_CURRENCIES = [ - 'AED', 'AUD', 'BGN', 'BRL', 'CAD', 'CHF', - 'CZK', 'DKK', 'EUR', 'GBP', 'HKD', 'HRK', - 'HUF', 'ILS', 'ISK', 'JPY', 'MXN', 'MYR', - 'NOK', 'NZD', 'PHP', 'PLN', 'RON', 'RUB', - 'SEK', 'SGD', 'THB', 'TWD', 'USD', 'ZAR' + 'AED', + 'AUD', + 'BGN', + 'BRL', + 'CAD', + 'CHF', + 'CZK', + 'DKK', + 'EUR', + 'GBP', + 'HKD', + 'HRK', + 'HUF', + 'ILS', + 'ISK', + 'JPY', + 'MXN', + 'MYR', + 'NOK', + 'NZD', + 'PHP', + 'PLN', + 'RON', + 'RUB', + 'SEK', + 'SGD', + 'THB', + 'TWD', + 'USD', + 'ZAR' ] diff --git a/addons/payment_mollie/models/payment_provider.py b/addons/payment_mollie/models/payment_provider.py index d58932df9aa..2b0b8389a79 100755 --- a/addons/payment_mollie/models/payment_provider.py +++ b/addons/payment_mollie/models/payment_provider.py @@ -5,7 +5,7 @@ import logging import requests from werkzeug import urls -from odoo import _, api, fields, models, service +from odoo import _, fields, models, service from odoo.exceptions import ValidationError from odoo.addons.payment_mollie.const import SUPPORTED_CURRENCIES @@ -27,16 +27,14 @@ class PaymentProvider(models.Model): #=== BUSINESS METHODS ===# - @api.model - def _get_compatible_providers(self, *args, currency_id=None, **kwargs): - """ Override of payment to unlist Mollie providers for unsupported currencies. """ - providers = super()._get_compatible_providers(*args, currency_id=currency_id, **kwargs) - - currency = self.env['res.currency'].browse(currency_id).exists() - if currency and currency.name not in SUPPORTED_CURRENCIES: - providers = providers.filtered(lambda p: p.code != 'mollie') - - return providers + def _get_supported_currencies(self): + """ Override of `payment` to return the supported currencies. """ + supported_currencies = super()._get_supported_currencies() + if self.code == 'mollie': + supported_currencies = supported_currencies.filtered( + lambda c: c.name in SUPPORTED_CURRENCIES + ) + return supported_currencies def _mollie_make_request(self, endpoint, data=None, method='POST'): """ Make a request at mollie endpoint. diff --git a/addons/payment_paypal/const.py b/addons/payment_paypal/const.py index 5ad95e6462d..1f02c1d0791 100644 --- a/addons/payment_paypal/const.py +++ b/addons/payment_paypal/const.py @@ -1,6 +1,8 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. # ISO 4217 codes of currencies supported by PayPal +# See https://developer.paypal.com/docs/reports/reference/paypal-supported-currencies/. +# Last seen on: 22 September 2022. SUPPORTED_CURRENCIES = ( 'AUD', 'BRL', diff --git a/addons/payment_paypal/models/payment_provider.py b/addons/payment_paypal/models/payment_provider.py index 3f48840256d..0aa4c998e1f 100644 --- a/addons/payment_paypal/models/payment_provider.py +++ b/addons/payment_paypal/models/payment_provider.py @@ -2,7 +2,7 @@ import logging -from odoo import _, api, fields, models +from odoo import _, fields, models from odoo.addons.payment_paypal.const import SUPPORTED_CURRENCIES @@ -35,16 +35,14 @@ class PaymentProvider(models.Model): #=== BUSINESS METHODS ===# - @api.model - def _get_compatible_providers(self, *args, currency_id=None, **kwargs): - """ Override of payment to unlist PayPal providers when the currency is not supported. """ - providers = super()._get_compatible_providers(*args, currency_id=currency_id, **kwargs) - - currency = self.env['res.currency'].browse(currency_id).exists() - if currency and currency.name not in SUPPORTED_CURRENCIES: - providers = providers.filtered(lambda p: p.code != 'paypal') - - return providers + def _get_supported_currencies(self): + """ Override of `payment` to return the supported currencies. """ + supported_currencies = super()._get_supported_currencies() + if self.code == 'paypal': + supported_currencies = supported_currencies.filtered( + lambda c: c.name in SUPPORTED_CURRENCIES + ) + return supported_currencies def _paypal_get_api_url(self): """ Return the API URL according to the provider state. diff --git a/addons/payment_payulatam/const.py b/addons/payment_payulatam/const.py new file mode 100644 index 00000000000..6738c357ebe --- /dev/null +++ b/addons/payment_payulatam/const.py @@ -0,0 +1,14 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +# Supported currencies of PayuLatam, in ISO 4217 currency codes. +# https://developers.payulatam.com/latam/en/docs/getting-started/response-codes-and-variables.html#accepted-currencies. +# Last seen online: 22 September 2022. +SUPPORTED_CURRENCIES = [ + 'ARS', + 'BRL', + 'CLP', + 'COP', + 'MXN', + 'PEN', + 'USD' +] diff --git a/addons/payment_payulatam/models/payment_provider.py b/addons/payment_payulatam/models/payment_provider.py index 27e58e6d342..630aebbfed4 100644 --- a/addons/payment_payulatam/models/payment_provider.py +++ b/addons/payment_payulatam/models/payment_provider.py @@ -2,11 +2,10 @@ from hashlib import md5 -from odoo import api, fields, models +from odoo import fields, models from odoo.tools.float_utils import float_repr -SUPPORTED_CURRENCIES = ('ARS', 'BRL', 'CLP', 'COP', 'MXN', 'PEN', 'USD') - +from odoo.addons.payment_payulatam.const import SUPPORTED_CURRENCIES class PaymentProvider(models.Model): _inherit = 'payment.provider' @@ -25,16 +24,14 @@ class PaymentProvider(models.Model): string="PayU Latam API Key", required_if_provider='payulatam', groups='base.group_system') - @api.model - def _get_compatible_providers(self, *args, currency_id=None, **kwargs): - """ Override of payment to unlist PayU Latam providers for unsupported currencies. """ - providers = super()._get_compatible_providers(*args, currency_id=currency_id, **kwargs) - - currency = self.env['res.currency'].browse(currency_id).exists() - if currency and currency.name not in SUPPORTED_CURRENCIES: - providers = providers.filtered(lambda p: p.code != 'payulatam') - - return providers + def _get_supported_currencies(self): + """ Override of `payment` to return the supported currencies. """ + supported_currencies = super()._get_supported_currencies() + if self.code == 'payulatam': + supported_currencies = supported_currencies.filtered( + lambda c: c.name in SUPPORTED_CURRENCIES + ) + return supported_currencies def _payulatam_generate_sign(self, values, incoming=True): """ Generate the signature for incoming or outgoing communications. diff --git a/addons/payment_payulatam/tests/test_payulatam.py b/addons/payment_payulatam/tests/test_payulatam.py index afc7be8ffcf..e8218a998f2 100644 --- a/addons/payment_payulatam/tests/test_payulatam.py +++ b/addons/payment_payulatam/tests/test_payulatam.py @@ -12,22 +12,12 @@ from odoo.tools import mute_logger from odoo.addons.payment.tests.http_common import PaymentHttpCommon from odoo.addons.payment_payulatam.controllers.main import PayuLatamController -from odoo.addons.payment_payulatam.models.payment_provider import SUPPORTED_CURRENCIES from odoo.addons.payment_payulatam.tests.common import PayULatamCommon @tagged('post_install', '-at_install') class PayULatamTest(PayULatamCommon, PaymentHttpCommon): - def test_compatibility_with_supported_currencies(self): - """ Test that the PayULatam provider is compatible with all supported currencies. """ - for supported_currency_code in SUPPORTED_CURRENCIES: - supported_currency = self._prepare_currency(supported_currency_code) - compatible_providers = self.env['payment.provider']._get_compatible_providers( - self.company.id, self.partner.id, self.amount, currency_id=supported_currency.id - ) - self.assertIn(self.payulatam, compatible_providers) - def test_incompatibility_with_unsupported_currency(self): """ Test that the PayULatam provider is not compatible with an unsupported currency. """ compatible_providers = self.env['payment.provider']._get_compatible_providers( diff --git a/addons/payment_payumoney/models/payment_provider.py b/addons/payment_payumoney/models/payment_provider.py index 1d92186d832..0ff91e78bdd 100644 --- a/addons/payment_payumoney/models/payment_provider.py +++ b/addons/payment_payumoney/models/payment_provider.py @@ -2,7 +2,7 @@ import hashlib -from odoo import api, fields, models +from odoo import fields, models class PaymentProvider(models.Model): @@ -16,16 +16,12 @@ class PaymentProvider(models.Model): payumoney_merchant_salt = fields.Char( string="Merchant Salt", required_if_provider='payumoney', groups='base.group_system') - @api.model - def _get_compatible_providers(self, *args, currency_id=None, **kwargs): - """ Override of payment to unlist PayUmoney providers when the currency is not INR. """ - providers = super()._get_compatible_providers(*args, currency_id=currency_id, **kwargs) - - currency = self.env['res.currency'].browse(currency_id).exists() - if currency and currency.name != 'INR': - providers = providers.filtered(lambda p: p.code != 'payumoney') - - return providers + def _get_supported_currencies(self): + """ Override of `payment` to return EUR as the only supported currency. """ + supported_currencies = super()._get_supported_currencies() + if self.code == 'payumoney': + supported_currencies = supported_currencies.filtered(lambda c: c.name == 'INR') + return supported_currencies def _payumoney_generate_sign(self, values, incoming=True): """ Generate the shasign for incoming or outgoing communications. diff --git a/addons/payment_razorpay/const.py b/addons/payment_razorpay/const.py index 2aaf70bb644..5a43c4a4645 100644 --- a/addons/payment_razorpay/const.py +++ b/addons/payment_razorpay/const.py @@ -2,15 +2,100 @@ # The currencies supported by Razorpay, in ISO 4217 format. Last updated on May 26, 2021. # See https://razorpay.com/docs/payments/payments/international-payments/#supported-currencies. +# Last seen online: 16 November 2022. SUPPORTED_CURRENCIES = [ - 'AED', 'ALL', 'AMD', 'ARS', 'AUD', 'AWG', 'BBD', 'BDT', 'BMD', 'BND', 'BOB', 'BSD', 'BWP', - 'BZD', 'CAD', 'CHF', 'CNY', 'COP', 'CRC', 'CUP', 'CZK', 'DKK', 'DOP', 'DZD', 'EGP', 'ETB', - 'EUR', 'FJD', 'GBP', 'GHS', 'GIP', 'GMD', 'GTQ', 'GYD', 'HKD', 'HNL', 'HRK', 'HTG', 'HUF', - 'IDR', 'ILS', 'INR', 'JMD', 'KES', 'KGS', 'KHR', 'KYD', 'KZT', 'LAK', 'LBP', 'LKR', 'LRD', - 'LSL', 'MAD', 'MDL', 'MKD', 'MMK', 'MNT', 'MOP', 'MUR', 'MVR', 'MWK', 'MXN', 'MYR', 'NAD', - 'NGN', 'NIO', 'NOK', 'NPR', 'NZD', 'PEN', 'PGK', 'PHP', 'PKR', 'QAR', 'RUB', 'SAR', 'SCR', - 'SEK', 'SGD', 'SLL', 'SOS', 'SSP', 'SVC', 'SZL', 'THB', 'TTD', 'TZS', 'USD', 'UYU', 'UZS', - 'YER', 'ZAR', + 'AED', + 'ALL', + 'AMD', + 'ARS', + 'AUD', + 'AWG', + 'BBD', + 'BDT', + 'BMD', + 'BND', + 'BOB', + 'BSD', + 'BWP', + 'BZD', + 'CAD', + 'CHF', + 'CNY', + 'COP', + 'CRC', + 'CUP', + 'CZK', + 'DKK', + 'DOP', + 'DZD', + 'EGP', + 'ETB', + 'EUR', + 'FJD', + 'GBP', + 'GHS', + 'GIP', + 'GMD', + 'GTQ', + 'GYD', + 'HKD', + 'HNL', + 'HRK', + 'HTG', + 'HUF', + 'IDR', + 'ILS', + 'INR', + 'JMD', + 'KES', + 'KGS', + 'KHR', + 'KYD', + 'KZT', + 'LAK', + 'LKR', + 'LRD', + 'LSL', + 'MAD', + 'MDL', + 'MKD', + 'MMK', + 'MNT', + 'MOP', + 'MUR', + 'MVR', + 'MWK', + 'MXN', + 'MYR', + 'NAD', + 'NGN', + 'NIO', + 'NOK', + 'NPR', + 'NZD', + 'PEN', + 'PGK', + 'PHP', + 'PKR', + 'QAR', + 'RUB', + 'SAR', + 'SCR', + 'SEK', + 'SGD', + 'SLL', + 'SOS', + 'SSP', + 'SVC', + 'SZL', + 'THB', + 'TTD', + 'TZS', + 'USD', + 'UYU', + 'UZS', + 'YER', + 'ZAR', ] # Mapping of transaction states to Razorpay's payment statuses. diff --git a/addons/payment_razorpay/models/payment_provider.py b/addons/payment_razorpay/models/payment_provider.py index c28275515ea..39eea782949 100644 --- a/addons/payment_razorpay/models/payment_provider.py +++ b/addons/payment_razorpay/models/payment_provider.py @@ -8,7 +8,7 @@ import pprint import requests from werkzeug.urls import url_join -from odoo import _, api, fields, models +from odoo import _, fields, models from odoo.exceptions import ValidationError from odoo.addons.payment_razorpay.const import SUPPORTED_CURRENCIES @@ -51,16 +51,14 @@ class PaymentProvider(models.Model): # === BUSINESS METHODS ===# - @api.model - def _get_compatible_providers(self, *args, currency_id=None, **kwargs): - """ Override of `payment` to filter out Razorpay providers for unsupported currencies. """ - providers = super()._get_compatible_providers(*args, currency_id=currency_id, **kwargs) - - currency = self.env['res.currency'].browse(currency_id).exists() - if currency and currency.name not in SUPPORTED_CURRENCIES: - providers = providers.filtered(lambda p: p.code != 'razorpay') - - return providers + def _get_supported_currencies(self): + """ Override of `payment` to return the supported currencies. """ + supported_currencies = super()._get_supported_currencies() + if self.code == 'razorpay': + supported_currencies = supported_currencies.filtered( + lambda c: c.name in SUPPORTED_CURRENCIES + ) + return supported_currencies def _razorpay_make_request(self, endpoint, payload=None, method='POST'): """ Make a request to Razorpay API at the specified endpoint. diff --git a/addons/payment_sips/models/const.py b/addons/payment_sips/const.py similarity index 79% rename from addons/payment_sips/models/const.py rename to addons/payment_sips/const.py index bfadc210d43..7dccc23e2bb 100644 --- a/addons/payment_sips/models/const.py +++ b/addons/payment_sips/const.py @@ -3,12 +3,7 @@ # ISO 4217 Data for currencies supported by sips # NOTE: these are listed on the Atos Wordline SIPS POST documentation page # at https://documentation.sips.worldline.com/en/WLSIPS.001-GD-Data-dictionary.html#Sips.001_DD_en-Value-currencyCode -# Yet with the simu environment, some of these currencies are *not* working -# I have no way to know if this is caused by the SIMU environment, or if it's -# the doc of SIPS that lists currencies that don't work, but since this list is -# restrictive, I'm gonna assume they are supported when using the right flow -# and payment methods, which may not work in SIMU... -# Since SIPS advises to use 'in production', well... +# Last seen on: 22 September 2022. SUPPORTED_CURRENCIES = { 'ARS': '032', 'AUD': '036', diff --git a/addons/payment_sips/models/payment_provider.py b/addons/payment_sips/models/payment_provider.py index 6be08652f7c..a68437c7bf0 100644 --- a/addons/payment_sips/models/payment_provider.py +++ b/addons/payment_sips/models/payment_provider.py @@ -3,9 +3,9 @@ from hashlib import sha256 -from odoo import api, fields, models +from odoo import fields, models -from .const import SUPPORTED_CURRENCIES +from odoo.addons.payment_sips.const import SUPPORTED_CURRENCIES class PaymentProvider(models.Model): @@ -29,16 +29,14 @@ class PaymentProvider(models.Model): sips_version = fields.Char( string="Interface Version", required_if_provider='sips', default="HP_2.31") - @api.model - def _get_compatible_providers(self, *args, currency_id=None, **kwargs): - """ Override of payment to unlist Sips providers when the currency is not supported. """ - providers = super()._get_compatible_providers(*args, currency_id=currency_id, **kwargs) - - currency = self.env['res.currency'].browse(currency_id).exists() - if currency and currency.name not in SUPPORTED_CURRENCIES: - providers = providers.filtered(lambda p: p.code != 'sips') - - return providers + def _get_supported_currencies(self): + """ Override of `payment` to return the supported currencies. """ + supported_currencies = super()._get_supported_currencies() + if self.code == 'sips': + supported_currencies = supported_currencies.filtered( + lambda c: c.name in SUPPORTED_CURRENCIES.keys() + ) + return supported_currencies def _sips_generate_shasign(self, data): """ Generate the shasign for incoming or outgoing communications. diff --git a/addons/payment_sips/models/payment_transaction.py b/addons/payment_sips/models/payment_transaction.py index 416f6a97e39..8dbc2be229a 100644 --- a/addons/payment_sips/models/payment_transaction.py +++ b/addons/payment_sips/models/payment_transaction.py @@ -10,8 +10,8 @@ from odoo import _, api, models from odoo.exceptions import ValidationError from odoo.addons.payment import utils as payment_utils +from odoo.addons.payment_sips.const import RESPONSE_CODES_MAPPING, SUPPORTED_CURRENCIES from odoo.addons.payment_sips.controllers.main import SipsController -from .const import RESPONSE_CODES_MAPPING, SUPPORTED_CURRENCIES _logger = logging.getLogger(__name__) diff --git a/addons/payment_sips/tests/test_sips.py b/addons/payment_sips/tests/test_sips.py index a482cc99519..4c6eed939a9 100644 --- a/addons/payment_sips/tests/test_sips.py +++ b/addons/payment_sips/tests/test_sips.py @@ -12,7 +12,6 @@ from odoo.tools import mute_logger from odoo.addons.payment.tests.http_common import PaymentHttpCommon from odoo.addons.payment_sips.controllers.main import SipsController -from odoo.addons.payment_sips.models.payment_provider import SUPPORTED_CURRENCIES from odoo.addons.payment_sips.tests.common import SipsCommon @@ -20,13 +19,6 @@ from odoo.addons.payment_sips.tests.common import SipsCommon class SipsTest(SipsCommon, PaymentHttpCommon): def test_compatible_providers(self): - for curr in SUPPORTED_CURRENCIES: - currency = self._prepare_currency(curr) - providers = self.env['payment.provider']._get_compatible_providers( - self.company.id, self.partner.id, self.amount, currency_id=currency.id - ) - self.assertIn(self.sips, providers) - unsupported_currency = self._prepare_currency('VEF') providers = self.env['payment.provider']._get_compatible_providers( self.company.id, self.partner.id, self.amount, currency_id=unsupported_currency.id