[FIX] payment,sale: access error in payment link

When a user was opening the payment_link_wizard and tried to select a
preferred payment acquirer, he was getting an AccessError telling that
he was not allowed to access 'payment.acquirer' records.

Fix PR #69334 (task-2504225)

task-2666881

closes odoo/odoo#80005

X-original-commit: 2ff580c073ac105219582dbf2108a1437645c9b5
Signed-off-by: Antoine Vandevenne (anv) <anv@odoo.com>
This commit is contained in:
Valentin Chevalier
2021-11-18 18:45:11 +00:00
parent 812115716e
commit 46be53f2c1
2 changed files with 105 additions and 16 deletions
+93 -5
View File
@@ -1,5 +1,5 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from lxml import etree
from werkzeug import urls
from odoo import _, api, fields, models
@@ -31,6 +31,41 @@ class PaymentLinkWizard(models.TransientModel):
})
return res
@api.model
def fields_view_get(self, *args, **kwargs):
""" Overrides orm fields_view_get
Using a Many2One field, when a user opens this wizard and tries to select a preferred
payment acquirer, he will get an AccessError telling that he is not allowed to access
'payment.acquirer' records. This error is thrown because the Many2One field is filled
by the name_get() function and users don't have clearance to read 'payment.acquirer' records.
This override allows replacing the Many2One with a selection field, that is prefilled in the
backend with the name of available acquirers. Therefore, Users will be able to select their
preferred acquirer.
:return: composition of the requested view (including inherited views and extensions)
:rtype: dict
"""
res = super().fields_view_get(*args, **kwargs)
if res['type'] == 'form':
doc = etree.XML(res['arch'])
# Replace acquirer_id with payment_acquirer_selection in the view
acq = doc.xpath("//field[@name='acquirer_id']")[0]
acq.attrib['name'] = 'payment_acquirer_selection'
acq.attrib['widget'] = 'selection'
acq.attrib['string'] = 'Force Payment Acquirer'
del acq.attrib['options']
del acq.attrib['placeholder']
# Replace acquirer_id with payment_acquirer_selection in the fields list
xarch, xfields = self.env['ir.ui.view'].postprocess_and_fields(doc, model=self._name)
res['arch'] = xarch
res['fields'] = xfields
return res
res_model = fields.Char('Related Document Model', required=True)
res_id = fields.Integer('Related Document ID', required=True)
amount = fields.Monetary(currency_field='currency_id', required=True)
@@ -58,6 +93,14 @@ class PaymentLinkWizard(models.TransientModel):
string="Has Multiple Acquirers",
compute='_compute_has_multiple_acquirers',
)
payment_acquirer_selection = fields.Selection(
string="Payment acquirer selected",
selection='_selection_payment_acquirer_selection',
default='all',
compute='_compute_payment_acquirer_selection',
inverse='_inverse_payment_acquirer_selection',
required=True,
)
@api.onchange('amount', 'description')
def _onchange_amount(self):
@@ -66,7 +109,7 @@ class PaymentLinkWizard(models.TransientModel):
if self.amount <= 0:
raise ValidationError(_("The value of the payment amount must be positive."))
@api.depends('amount', 'description', 'partner_id', 'currency_id', 'acquirer_id')
@api.depends('amount', 'description', 'partner_id', 'currency_id', 'payment_acquirer_selection')
def _compute_values(self):
for payment_link in self:
payment_link.access_token = payment_utils.generate_access_token(
@@ -84,12 +127,57 @@ class PaymentLinkWizard(models.TransientModel):
@api.depends('company_id', 'partner_id', 'currency_id')
def _compute_available_acquirer_ids(self):
for link in self:
link.available_acquirer_ids = link.env['payment.acquirer']._get_compatible_acquirers(
link.available_acquirer_ids = link._get_payment_acquirer_available(
res_model=link.res_model,
res_id=link.res_id,
company_id=link.company_id.id,
partner_id=link.partner_id.id,
currency_id=link.currency_id.id
currency_id=link.currency_id.id,
)
@api.depends('acquirer_id')
def _compute_payment_acquirer_selection(self):
for link in self:
link.payment_acquirer_selection = link.acquirer_id.id if link.acquirer_id else 'all'
def _inverse_payment_acquirer_selection(self):
for link in self:
link.acquirer_id = link.payment_acquirer_selection if link.payment_acquirer_selection != 'all' else False
def _selection_payment_acquirer_selection(self):
""" Specify available acquirers in the selection field.
:return: The selection list of available acquirers.
:rtype: list[tuple]
"""
defaults = self.default_get(['res_model', 'res_id'])
selection = [('all', "All")]
res_model, res_id = defaults['res_model'], defaults['res_id']
if res_id and res_model in ['account.move', "sale.order"]:
# At module install, the selection method is called
# but the document context isn't specified.
related_document = self.env[res_model].browse(res_id)
company_id = related_document.company_id
partner_id = related_document.partner_id
currency_id = related_document.currency_id
selection.extend(
self._get_payment_acquirer_available(
res_model=res_model,
res_id=res_id,
company_id=company_id.id,
partner_id=partner_id.id,
currency_id=currency_id.id,
).name_get()
)
return selection
def _get_payment_acquirer_available(self, **kwargs):
""" Select and return the acquirers matching the criteria.
:return: The compatible acquirers
:rtype: recordset of `payment.acquirer`
"""
return self.env['payment.acquirer'].sudo()._get_compatible_acquirers(**kwargs)
@api.depends('available_acquirer_ids')
def _compute_has_multiple_acquirers(self):
for link in self:
@@ -106,5 +194,5 @@ class PaymentLinkWizard(models.TransientModel):
f'&partner_id={payment_link.partner_id.id}' \
f'&company_id={payment_link.company_id.id}' \
f'&invoice_id={payment_link.res_id}' \
f'{"&acquirer_id=" + str(payment_link.acquirer_id.id) if payment_link.acquirer_id else "" }' \
f'{"&acquirer_id=" + str(payment_link.payment_acquirer_selection) if payment_link.payment_acquirer_selection != "all" else "" }' \
f'&access_token={payment_link.access_token}'
+12 -11
View File
@@ -24,16 +24,17 @@ class PaymentLinkWizard(models.TransientModel):
})
return res
@api.depends('company_id', 'partner_id', 'currency_id')
def _compute_available_acquirer_ids(self):
sale_links = self.filtered(lambda link: link.res_model == 'sale.order')
super(PaymentLinkWizard, self-sale_links)._compute_available_acquirer_ids()
for link in sale_links:
link.available_acquirer_ids = link.env['payment.acquirer']._get_compatible_acquirers(
company_id=link.company_id.id,
partner_id=link.partner_id.id,
currency_id=link.currency_id.id,
sale_order_id=link.res_id)
def _get_payment_acquirer_available(self, res_model, res_id, **kwargs):
""" Select and return the acquirers matching the criteria.
:param str res_model: active model
:param int res_id: id of 'active_model' record
:return: The compatible acquirers
:rtype: recordset of `payment.acquirer`
"""
if res_model == 'sale.order':
kwargs['sale_order_id'] = res_id
return super()._get_payment_acquirer_available(**kwargs)
def _generate_link(self):
""" Override of payment to add the sale_order_id in the link. """
@@ -46,7 +47,7 @@ class PaymentLinkWizard(models.TransientModel):
f'?reference={urls.url_quote(payment_link.description)}' \
f'&amount={payment_link.amount}' \
f'&sale_order_id={payment_link.res_id}' \
f'{"&acquirer_id=" + str(payment_link.acquirer_id.id) if payment_link.acquirer_id else "" }' \
f'{"&acquirer_id=" + str(payment_link.payment_acquirer_selection) if payment_link.payment_acquirer_selection != "all" else "" }' \
f'&access_token={payment_link.access_token}'
# Order-related fields are retrieved in the controller
else: