diff --git a/addons/account_payment/controllers/portal.py b/addons/account_payment/controllers/portal.py index d337a7a62a9..95969365aba 100644 --- a/addons/account_payment/controllers/portal.py +++ b/addons/account_payment/controllers/portal.py @@ -31,7 +31,7 @@ class PortalAccount(portal.PortalAccount): currency_id=invoice.currency_id.id ) # In sudo mode to read the fields of providers and partner (if not logged in) tokens = request.env['payment.token']._get_available_tokens( - providers_sudo.ids, partner_id=partner_sudo.id, logged_in=logged_in + providers_sudo.ids, partner_sudo.id ) # Make sure that the partner's company matches the invoice's company. @@ -54,7 +54,7 @@ class PortalAccount(portal.PortalAccount): 'tokens': tokens, 'fees_by_provider': fees_by_provider, 'show_tokenize_input': PaymentPortal._compute_show_tokenize_input_mapping( - providers_sudo, logged_in=logged_in + providers_sudo ), 'amount': invoice.amount_residual, 'currency': invoice.currency_id, @@ -64,10 +64,4 @@ class PortalAccount(portal.PortalAccount): 'landing_route': _build_url_w_params(invoice.access_url, {'access_token': access_token}) } values.update(**portal_page_values, **payment_form_values) - if not logged_in: - # Don't display payment tokens of the invoice partner if the user is not logged in, but - # inform that logging in will make them available. - values.update({ - 'existing_token': bool(tokens), - }) return values diff --git a/addons/account_payment/views/account_portal_templates.xml b/addons/account_payment/views/account_portal_templates.xml index 8c8a8021c37..14aa3c37604 100644 --- a/addons/account_payment/views/account_portal_templates.xml +++ b/addons/account_payment/views/account_portal_templates.xml @@ -93,11 +93,6 @@
-
-
- You have credits card registered, you can log-in to be able to use them. -
-
diff --git a/addons/payment/controllers/portal.py b/addons/payment/controllers/portal.py index e7f14cbde91..b3d73357210 100644 --- a/addons/payment/controllers/portal.py +++ b/addons/payment/controllers/portal.py @@ -113,7 +113,7 @@ class PaymentPortal(portal.CustomerPortal): if provider_id in providers_sudo.ids: # Only keep the desired provider if it's suitable providers_sudo = providers_sudo.browse(provider_id) tokens_sudo = request.env['payment.token'].sudo()._get_available_tokens( - providers_sudo.ids, partner_id=partner_sudo.id, logged_in=logged_in + providers_sudo.ids, partner_sudo.id ) # In sudo mode to be able to read tokens of other partners. # Make sure that the partner's company matches the company passed as parameter. @@ -138,7 +138,7 @@ class PaymentPortal(portal.CustomerPortal): 'tokens': tokens_sudo, 'fees_by_provider': fees_by_provider, 'show_tokenize_input': self._compute_show_tokenize_input_mapping( - providers_sudo, logged_in=logged_in, **kwargs + providers_sudo, **kwargs ), 'reference_prefix': reference, 'amount': amount, @@ -154,13 +154,12 @@ class PaymentPortal(portal.CustomerPortal): return request.render(self._get_payment_page_template_xmlid(**kwargs), rendering_context) @staticmethod - def _compute_show_tokenize_input_mapping(providers_sudo, logged_in=False, **kwargs): + def _compute_show_tokenize_input_mapping(providers_sudo, **kwargs): """ Determine for each provider whether the tokenization input should be shown or not. :param recordset providers_sudo: The providers for which to determine whether the tokenization input should be shown or not, as a sudoed `payment.provider` recordset. - :param bool logged_in: Whether the user is logged in or not. :param dict kwargs: The optional data passed to the helper methods. :return: The mapping of the computed value for each provider id. :rtype: dict @@ -168,8 +167,7 @@ class PaymentPortal(portal.CustomerPortal): show_tokenize_input_mapping = {} for provider_sudo in providers_sudo: show_tokenize_input = provider_sudo.allow_tokenization \ - and not provider_sudo._is_tokenization_required(**kwargs) \ - and logged_in + and not provider_sudo._is_tokenization_required(**kwargs) show_tokenize_input_mapping[provider_sudo.id] = show_tokenize_input return show_tokenize_input_mapping @@ -196,7 +194,7 @@ class PaymentPortal(portal.CustomerPortal): rendering_context = { 'providers': providers_sudo, 'tokens': request.env['payment.token'].sudo()._get_available_tokens( - None, partner_id=partner_sudo.id, is_validation=True, logged_in=True + None, partner_sudo.id, is_validation=True ), # In sudo mode to read the commercial partner's fields. 'reference_prefix': payment_utils.singularize_reference_prefix(prefix='V'), 'partner_id': partner_sudo.id, diff --git a/addons/payment/models/payment_token.py b/addons/payment/models/payment_token.py index 8a58c2beed2..1450e89b907 100644 --- a/addons/payment/models/payment_token.py +++ b/addons/payment/models/payment_token.py @@ -3,7 +3,7 @@ import logging from odoo import _, api, fields, models -from odoo.exceptions import UserError +from odoo.exceptions import UserError, ValidationError class PaymentToken(models.Model): @@ -74,6 +74,13 @@ class PaymentToken(models.Model): return super().write(values) + @api.constrains('partner_id') + def _check_partner_is_never_public(self): + """ Check that the partner associated with the token is never public. """ + for token in self: + if token.partner_id.is_public: + raise ValidationError(_("No token can be assigned to the public partner.")) + def _handle_archiving(self): """ Handle the archiving of tokens. @@ -89,9 +96,7 @@ class PaymentToken(models.Model): #=== BUSINESS METHODS ===# - def _get_available_tokens( - self, providers_ids, partner_id=None, is_validation=False, logged_in=False, **kwargs - ): + def _get_available_tokens(self, providers_ids, partner_id, is_validation=False, **kwargs): """ Return the available tokens linked to the given providers and partner. For a module to retrieve the available tokens, it must override this method and add @@ -100,14 +105,10 @@ class PaymentToken(models.Model): :param list providers_ids: The ids of the providers available for the transaction. :param int partner_id: The id of the partner. :param bool is_validation: Whether the transaction is a validation operation. - :param bool logged_in: Whether the user is logged in. :param dict kwargs: Locally unused keywords arguments. :return: The available tokens. - :rtype: `payment.token` + :rtype: payment.token """ - if partner_id is None or not logged_in: - return self.env['payment.token'] - if not is_validation: return self.env['payment.token'].search( [('provider_id', 'in', providers_ids), ('partner_id', '=', partner_id)] diff --git a/addons/payment/tests/test_flows.py b/addons/payment/tests/test_flows.py index d98c6f20715..2d8abc9799e 100644 --- a/addons/payment/tests/test_flows.py +++ b/addons/payment/tests/test_flows.py @@ -379,16 +379,23 @@ class TestFlows(PaymentHttpCommon): ) self.assertEqual(patched.call_count, 1) - def test_tokenization_input_is_show_to_logged_in_users(self): + def test_tokenization_input_is_shown_to_logged_in_users(self): + # Test both for portal and internal users + self.user = self.portal_user self.provider.allow_tokenization = True - show_tokenize_input = PaymentPortal._compute_show_tokenize_input_mapping( - self.provider, logged_in=True - ) + + show_tokenize_input = PaymentPortal._compute_show_tokenize_input_mapping(self.provider) self.assertDictEqual(show_tokenize_input, {self.provider.id: True}) - def test_tokenization_input_is_hidden_for_logged_out_users(self): - self.provider.allow_tokenization = False - show_tokenize_input = PaymentPortal._compute_show_tokenize_input_mapping( - self.provider, logged_in=True - ) - self.assertDictEqual(show_tokenize_input, {self.provider.id: False}) + self.user = self.internal_user + self.provider.allow_tokenization = True + + show_tokenize_input = PaymentPortal._compute_show_tokenize_input_mapping(self.provider) + self.assertDictEqual(show_tokenize_input, {self.provider.id: True}) + + def test_tokenization_input_is_shown_to_logged_out_users(self): + self.user = self.public_user + self.provider.allow_tokenization = True + + show_tokenize_input = PaymentPortal._compute_show_tokenize_input_mapping(self.provider) + self.assertDictEqual(show_tokenize_input, {self.provider.id: True}) diff --git a/addons/payment/tests/test_payment_token.py b/addons/payment/tests/test_payment_token.py index cb653dad9cd..29b5695fe24 100644 --- a/addons/payment/tests/test_payment_token.py +++ b/addons/payment/tests/test_payment_token.py @@ -2,7 +2,7 @@ from datetime import date -from odoo.exceptions import AccessError, UserError +from odoo.exceptions import AccessError, UserError, ValidationError from odoo.tests import tagged from odoo.tools import mute_logger @@ -20,6 +20,12 @@ class TestPaymentToken(PaymentCommon): with self.assertRaises(AccessError): token.with_user(user).read() + def test_cannot_assign_token_to_public_partner(self): + """ Test that no token can be assigned to the public partner. """ + token = self._create_token() + with self.assertRaises(ValidationError): + token.partner_id = self.public_user.partner_id + def test_token_cannot_be_unarchived(self): """ Test that unarchiving disabled tokens is forbidden. """ token = self._create_token(active=False) diff --git a/addons/sale/controllers/portal.py b/addons/sale/controllers/portal.py index d79c5032bf5..24d515d1c62 100644 --- a/addons/sale/controllers/portal.py +++ b/addons/sale/controllers/portal.py @@ -177,7 +177,6 @@ class CustomerPortal(portal.CustomerPortal): :return: The payment-specific values. :rtype: dict """ - logged_in = not request.env.user._is_public() partner = order_sudo.partner_id company = order_sudo.company_id amount = order_sudo.amount_total @@ -198,11 +197,11 @@ class CustomerPortal(portal.CustomerPortal): payment_form_values = { 'providers': providers_sudo, 'tokens': request.env['payment.token']._get_available_tokens( - providers_sudo.ids, partner_id=partner.id, logged_in=logged_in, **kwargs + providers_sudo.ids, partner.id, **kwargs ), 'fees_by_provider': fees_by_provider, 'show_tokenize_input': PaymentPortal._compute_show_tokenize_input_mapping( - providers_sudo, logged_in=logged_in, sale_order_id=order_sudo.id + providers_sudo, sale_order_id=order_sudo.id ), 'amount': amount, 'currency': currency, diff --git a/addons/sale/tests/test_payment_flow.py b/addons/sale/tests/test_payment_flow.py index bfcc4ea106d..d3f2a0f7ed2 100644 --- a/addons/sale/tests/test_payment_flow.py +++ b/addons/sale/tests/test_payment_flow.py @@ -32,7 +32,7 @@ class TestSalePayment(AccountPaymentCommon, SaleCommon, PaymentHttpCommon): '._compute_show_tokenize_input_mapping' ) as patched: tx_context = self._get_tx_checkout_context(**route_values) - patched.assert_called_once_with(ANY, logged_in=ANY, sale_order_id=ANY) + patched.assert_called_once_with(ANY, sale_order_id=ANY) self.assertEqual(tx_context['currency_id'], self.sale_order.currency_id.id) self.assertEqual(tx_context['partner_id'], self.sale_order.partner_id.id) diff --git a/addons/website_payment/controllers/portal.py b/addons/website_payment/controllers/portal.py index 9516f15010c..c9d72e2fe9f 100644 --- a/addons/website_payment/controllers/portal.py +++ b/addons/website_payment/controllers/portal.py @@ -64,7 +64,13 @@ class PaymentPortal(payment_portal.PaymentPortal): else: partner_id = request.env.user.partner_id.id - kwargs.pop('custom_create_values', None) # Don't allow passing arbitrary create values + # Don't allow passing arbitrary create values and avoid tokenization for + # the public user. + if use_public_partner: + kwargs['custom_create_values'] = {'tokenize': False} + else: + kwargs.pop('custom_create_values', None) + tx_sudo = self._create_transaction( amount=amount, currency_id=currency_id, partner_id=partner_id, **kwargs )