[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:
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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`.
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
Reference in New Issue
Block a user