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