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
)