[REF] payment, (website_)sale: factorize payment values logic in controllers

The payment values in the Qweb context of sale, sale_subscription and
website_sale were computed separately, although there is a large common
basis. This split made it hard to communicate between modules and
generated a lot of duplicates. The new function `_get_payment_values`
in sales is now used as a common basis method for all sale_* modules to
get the common payment values.

closes odoo/odoo#107788

Related: odoo/enterprise#34922
Signed-off-by: Vallaeys Valentin (vava) <vava@odoo.com>
This commit is contained in:
Valentin Vallaeys (vava)
2023-02-03 17:40:58 +01:00
parent fa37c67014
commit e534ab41e7
8 changed files with 119 additions and 106 deletions
+3 -4
View File
@@ -30,9 +30,9 @@ class PortalAccount(portal.PortalAccount):
invoice.amount_total,
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'].search(
[('provider_id', 'in', providers_sudo.ids), ('partner_id', '=', partner_sudo.id)]
) # Tokens are cleared at the end if the user is not logged in
tokens = request.env['payment.token']._get_available_tokens(
providers_sudo.ids, partner_id=partner_sudo.id, logged_in=logged_in
)
# Make sure that the partner's company matches the invoice's company.
company_mismatch = not PaymentPortal._can_partner_pay_in_company(
@@ -69,6 +69,5 @@ class PortalAccount(portal.PortalAccount):
# inform that logging in will make them available.
values.update({
'existing_token': bool(tokens),
'tokens': request.env['payment.token'],
})
return values
+6 -11
View File
@@ -112,9 +112,9 @@ class PaymentPortal(portal.CustomerPortal):
) # In sudo mode to read the fields of providers and partner (if not logged in)
if provider_id in providers_sudo.ids: # Only keep the desired provider if it's suitable
providers_sudo = providers_sudo.browse(provider_id)
payment_tokens = request.env['payment.token'].search(
[('provider_id', 'in', providers_sudo.ids), ('partner_id', '=', partner_sudo.id)]
) if logged_in else request.env['payment.token']
payment_tokens = request.env['payment.token']._get_available_tokens(
providers_sudo.ids, partner_id=partner_sudo.id, logged_in=logged_in
)
# Make sure that the partner's company matches the company passed as parameter.
company_mismatch = not PaymentPortal._can_partner_pay_in_company(partner_sudo, company)
@@ -192,17 +192,12 @@ class PaymentPortal(portal.CustomerPortal):
force_tokenization=True,
is_validation=True,
)
# Get all partner's tokens for which providers are not disabled.
tokens_sudo = request.env['payment.token'].sudo().search([
('partner_id', 'in', [partner_sudo.id, partner_sudo.commercial_partner_id.id]),
('provider_id.state', 'in', ['enabled', 'test']),
])
access_token = payment_utils.generate_access_token(partner_sudo.id, None, None)
rendering_context = {
'providers': providers_sudo,
'tokens': tokens_sudo,
'tokens': request.env['payment.token'].sudo()._get_available_tokens(
None, partner_id=partner_sudo.id, is_validation=True, logged_in=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,
'access_token': access_token,
+31
View File
@@ -89,6 +89,37 @@ class PaymentToken(models.Model):
#=== BUSINESS METHODS ===#
def _get_available_tokens(
self, providers_ids, partner_id=None, is_validation=False, logged_in=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
information in the kwargs to define the context of the request.
: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`
"""
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)]
)
else:
# Get all the tokens of the partner and of their commercial partner, regardless of
# whether the providers are available.
partner = self.env['res.partner'].browse(partner_id)
return self.env['payment.token'].search(
[('partner_id', 'in', [partner.id, partner.commercial_partner_id.id])]
)
def _build_display_name(self, *args, max_length=34, should_pad=True, **kwargs):
""" Build a token name of the desired maximum length with the format `•••• 1234`.
+7 -4
View File
@@ -324,7 +324,10 @@
- 'minor_amount' - The amount to pay in the minor units of its currency.
- 'currency' - The currency of the transaction, as a `res.currency` record.
- 'merchant_name' - The merchant name.
- 'access_token' - The access token used to authenticate the partner.
- 'payment_access_token' - The access token used to authenticate the partner. Since this
template is loaded in the shopping cart, this parameter is
called `payment_access_token` to prevent mixing up with the
`access_token` used for abandoned carts.
- 'shipping_info_required' - Whether the shipping information is required or not.
- 'transaction_route' - The route used to create a transaction when the user clicks Pay.
- 'shipping_address_update_route' - The route where available carriers are computed
@@ -348,12 +351,12 @@
t-att-data-shipping-address-update-route="shipping_address_update_route"
t-att-data-express-checkout-route="express_checkout_route"
t-att-data-landing-route="landing_route">
<t t-foreach="providers_sudo" t-as="provider_sudo">
<t t-foreach="providers" t-as="provider">
<t t-set="express_checkout_form_xml_id"
t-value="provider_sudo.express_checkout_form_view_id.xml_id"/>
t-value="provider.express_checkout_form_view_id.xml_id"/>
<t t-if="express_checkout_form_xml_id">
<t t-call="{{express_checkout_form_xml_id}}">
<t t-set="provider_sudo" t-value="provider_sudo"/>
<t t-set="provider" t-value="provider"/>
</t>
</t>
</t>
@@ -3,11 +3,11 @@
<template id="express_checkout_form">
<div name="o_express_checkout_container"
t-attf-id="o_stripe_express_checkout_container_{{provider_sudo.id}}"
t-att-data-provider-id="provider_sudo.id"
t-att-data-provider-code="provider_sudo.code"
t-att-data-stripe-publishable-key="provider_sudo._stripe_get_publishable_key()"
t-att-data-country-code="provider_sudo.company_id.country_id.code"
t-attf-id="o_stripe_express_checkout_container_{{provider.id}}"
t-att-data-provider-id="provider.id"
t-att-data-provider-code="provider.code"
t-att-data-stripe-publishable-key="provider._stripe_get_publishable_key()"
t-att-data-country-code="provider.company_id.country_id.code"
class="w-100 mt-2"/>
</template>
+28 -27
View File
@@ -168,54 +168,55 @@ class CustomerPortal(portal.CustomerPortal):
return request.render('sale.sale_order_portal_template', values)
def _get_payment_values(self, order_sudo):
def _get_payment_values(self, order_sudo, **kwargs):
""" Return the payment-specific QWeb context values.
:param recordset order_sudo: The sales order being paid, as a `sale.order` record.
:param sale.order order_sudo: The sales order being paid.
:param dict kwargs: Locally unused data passed to `_get_compatible_providers` and
`_get_available_tokens`.
: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
currency = order_sudo.currency_id
providers_sudo = request.env['payment.provider'].sudo()._get_compatible_providers(
order_sudo.company_id.id,
order_sudo.partner_id.id,
order_sudo.amount_total,
currency_id=order_sudo.currency_id.id,
company.id,
partner.id,
amount,
currency_id=currency.id,
sale_order_id=order_sudo.id,
) # In sudo mode to read the fields of providers and partner (if not logged in)
tokens = request.env['payment.token'].search([
('provider_id', 'in', providers_sudo.ids),
('partner_id', '=', order_sudo.partner_id.id)
]) if logged_in else request.env['payment.token']
# Make sure that the partner's company matches the order's company.
company_mismatch = not payment_portal.PaymentPortal._can_partner_pay_in_company(
order_sudo.partner_id, order_sudo.company_id
)
**kwargs,
) # In sudo mode to read the fields of providers and partner (if not logged in).
fees_by_provider = {
provider: provider._compute_fees(
order_sudo.amount_total,
order_sudo.currency_id,
order_sudo.partner_id.country_id,
amount, currency, partner.country_id
) for provider in providers_sudo.filtered('fees_active')
}
portal_page_values = {
'company_mismatch': company_mismatch,
'expected_company': order_sudo.company_id,
}
payment_form_values = {
'providers': providers_sudo,
'tokens': tokens,
'tokens': request.env['payment.token']._get_available_tokens(
providers_sudo.ids, partner_id=partner.id, logged_in=logged_in, **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
),
'amount': order_sudo.amount_total,
'currency': order_sudo.currency_id,
'partner_id': order_sudo.partner_id.id,
'access_token': order_sudo.access_token,
'amount': amount,
'currency': currency,
'partner_id': partner.id,
'access_token': order_sudo._portal_ensure_token(),
'transaction_route': order_sudo.get_portal_url(suffix='/transaction'),
'landing_route': order_sudo.get_portal_url(),
}
company_mismatch = not payment_portal.PaymentPortal._can_partner_pay_in_company(
partner, company
) # Make sure that the partner's company matches the order's company.
portal_page_values = {'company_mismatch': company_mismatch, 'expected_company': company}
return {**portal_page_values, **payment_form_values}
@http.route(['/my/orders/<int:order_id>/accept'], type='json', auth="public", website=True)
+18 -55
View File
@@ -23,7 +23,7 @@ from odoo.exceptions import AccessError, MissingError, ValidationError
from odoo.addons.portal.controllers.portal import _build_url_w_params
from odoo.addons.website.controllers import main
from odoo.addons.website.controllers.form import WebsiteForm
from odoo.addons.sale.controllers import portal
from odoo.addons.sale.controllers import portal as sale_portal
from odoo.osv import expression
from odoo.tools import lazy
from odoo.tools.json import scriptsafe as json_scriptsafe
@@ -1424,76 +1424,39 @@ class WebsiteSale(http.Controller):
# ------------------------------------------------------
def _get_express_shop_payment_values(self, order, **kwargs):
logged_in = not request.website.is_public_user()
providers_sudo = request.env['payment.provider'].sudo()._get_compatible_providers(
order.company_id.id,
order.partner_id.id,
order.amount_total,
currency_id=order.currency_id.id,
is_express_checkout=True,
sale_order_id=order.id,
website_id=request.website.id,
) # In sudo mode to read the fields of providers, order and partner (if not logged in)
fees_by_provider = {
p_sudo: p_sudo._compute_fees(
order.amount_total, order.currency_id, order.partner_id.country_id
) for p_sudo in providers_sudo.filtered('fees_active')
}
return {
# Payment express form values
'providers_sudo': providers_sudo,
'fees_by_provider': fees_by_provider,
'amount': order.amount_total,
payment_form_values = sale_portal.CustomerPortal._get_payment_values(
self, order, website_id=request.website.id, is_express_checkout=True
)
payment_form_values.update({
'payment_access_token': payment_form_values.pop('access_token'), # Rename the key.
'minor_amount': payment_utils.to_minor_currency_units(
order.amount_total, order.currency_id
order.amount_total, order.currency_id
),
'merchant_name': request.website.name,
'currency': order.currency_id,
'partner_id': order.partner_id.id if logged_in else -1,
'payment_access_token': order._portal_ensure_token(),
'transaction_route': f'/shop/payment/transaction/{order.id}',
'express_checkout_route': self._express_checkout_route,
'landing_route': '/shop/payment/validate',
}
})
if request.website.is_public_user():
payment_form_values['partner_id'] = -1
return payment_form_values
def _get_shop_payment_values(self, order, **kwargs):
logged_in = not request.env.user._is_public()
providers_sudo = request.env['payment.provider'].sudo()._get_compatible_providers(
order.company_id.id,
order.partner_id.id,
order.amount_total,
currency_id=order.currency_id.id,
sale_order_id=order.id,
website_id=request.website.id,
) # In sudo mode to read the fields of providers, order and partner (if not logged in)
tokens = request.env['payment.token'].search(
[('provider_id', 'in', providers_sudo.ids), ('partner_id', '=', order.partner_id.id)]
) if logged_in else request.env['payment.token']
fees_by_provider = {
p_sudo: p_sudo._compute_fees(
order.amount_total, order.currency_id, order.partner_id.country_id
) for p_sudo in providers_sudo.filtered('fees_active')
}
return {
portal_page_values = {
'website_sale_order': order,
'errors': [],
'partner': order.partner_id,
'order': order,
'payment_action_id': request.env.ref('payment.action_payment_provider').id,
# Payment form common (checkout and manage) values
'providers': providers_sudo,
'tokens': tokens,
'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.id
}
payment_form_values = {
**sale_portal.CustomerPortal._get_payment_values(
self, order, website_id=request.website.id
),
'amount': order.amount_total,
'currency': order.currency_id,
'partner_id': order.partner_id.id,
'access_token': order._portal_ensure_token(),
'transaction_route': f'/shop/payment/transaction/{order.id}',
'landing_route': '/shop/payment/validate',
}
return {**portal_page_values, **payment_form_values}
@http.route('/shop/payment', type='http', auth='public', website=True, sitemap=False)
def shop_payment(self, **post):
@@ -1777,7 +1740,7 @@ class PaymentPortal(payment_portal.PaymentPortal):
return tx_sudo._get_processing_values()
class CustomerPortal(portal.CustomerPortal):
class CustomerPortal(sale_portal.CustomerPortal):
def _sale_reorder_get_line_context(self):
return {}
@@ -0,0 +1,21 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
class PaymentToken(models.Model):
_inherit = 'payment.token'
def _get_available_tokens(self, *args, is_express_checkout=False, **kwargs):
""" Override of `payment` not to return the tokens in case of express checkout.
:param dict args: Locally unused arguments.
:param bool is_express_checkout: Whether the payment is made through express checkout.
:param dict kwargs: Locally unused keywords arguments.
:return: The available tokens.
:rtype: payment.token
"""
if is_express_checkout:
return self.env['payment.token']
return super()._get_available_tokens(*args, **kwargs)