This commit adds the possibility to define a maximum payment amount that a given acquirer can process. If the payment amount exceeds the value, the acquirer is filtered out of the available acquirers listed on the payment forms. While we're at it, the field `country_ids` is renamed to `available_country_ids` to better depict that it is not a property of the acquirer, but a configuration option. It will also be coherent with the field `available_currency_ids` that is expected to be added soon. Task - 2162165 closes odoo/odoo#82411 Related: odoo/enterprise#24412 Related: odoo/upgrade#3703 Signed-off-by: Antoine Vandevenne (anv) <anv@odoo.com>
151 lines
6.8 KiB
Python
151 lines
6.8 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
from werkzeug import urls
|
|
|
|
from odoo import _, api, fields, models
|
|
from odoo.exceptions import ValidationError
|
|
from odoo.tools import float_compare
|
|
|
|
from odoo.addons.payment import utils as payment_utils
|
|
|
|
|
|
class PaymentLinkWizard(models.TransientModel):
|
|
_name = "payment.link.wizard"
|
|
_description = "Generate Payment Link"
|
|
|
|
@api.model
|
|
def default_get(self, fields):
|
|
res = super(PaymentLinkWizard, self).default_get(fields)
|
|
res_id = self._context.get('active_id')
|
|
res_model = self._context.get('active_model')
|
|
res.update({'res_id': res_id, 'res_model': res_model})
|
|
amount_field = 'amount_residual' if res_model == 'account.move' else 'amount_total'
|
|
if res_id and res_model == 'account.move':
|
|
record = self.env[res_model].browse(res_id)
|
|
res.update({
|
|
'description': record.payment_reference,
|
|
'amount': record[amount_field],
|
|
'currency_id': record.currency_id.id,
|
|
'partner_id': record.partner_id.id,
|
|
'amount_max': record[amount_field],
|
|
})
|
|
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)
|
|
amount_max = fields.Monetary(currency_field='currency_id')
|
|
currency_id = fields.Many2one('res.currency')
|
|
partner_id = fields.Many2one('res.partner')
|
|
partner_email = fields.Char(related='partner_id.email')
|
|
link = fields.Char(string='Payment Link', compute='_compute_values')
|
|
description = fields.Char('Payment Ref')
|
|
access_token = fields.Char(compute='_compute_values')
|
|
company_id = fields.Many2one('res.company', compute='_compute_company')
|
|
available_acquirer_ids = fields.Many2many(
|
|
comodel_name='payment.acquirer',
|
|
string="Payment Acquirers Available",
|
|
compute='_compute_available_acquirer_ids',
|
|
compute_sudo=True,
|
|
)
|
|
has_multiple_acquirers = fields.Boolean(
|
|
string="Has Multiple Acquirers",
|
|
compute='_compute_has_multiple_acquirers',
|
|
)
|
|
payment_acquirer_selection = fields.Selection(
|
|
string="Allow Payment Acquirer",
|
|
help="If a specific payment acquirer is selected, customers will only be allowed to pay "
|
|
"via this one. If 'All' is selected, customers can pay via any available payment "
|
|
"acquirer.",
|
|
selection='_selection_payment_acquirer_selection',
|
|
default='all',
|
|
required=True,
|
|
)
|
|
|
|
@api.onchange('amount', 'description')
|
|
def _onchange_amount(self):
|
|
if float_compare(self.amount_max, self.amount, precision_rounding=self.currency_id.rounding or 0.01) == -1:
|
|
raise ValidationError(_("Please set an amount smaller than %s.") % (self.amount_max))
|
|
if self.amount <= 0:
|
|
raise ValidationError(_("The value of the payment amount must be positive."))
|
|
|
|
@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(
|
|
payment_link.partner_id.id, payment_link.amount, payment_link.currency_id.id
|
|
)
|
|
# must be called after token generation, obvsly - the link needs an up-to-date token
|
|
self._generate_link()
|
|
|
|
@api.depends('res_model', 'res_id')
|
|
def _compute_company(self):
|
|
for link in self:
|
|
record = self.env[link.res_model].browse(link.res_id)
|
|
link.company_id = record.company_id if 'company_id' in record else False
|
|
|
|
@api.depends('company_id', 'partner_id', 'currency_id')
|
|
def _compute_available_acquirer_ids(self):
|
|
for link in self:
|
|
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,
|
|
amount=link.amount,
|
|
currency_id=link.currency_id.id,
|
|
)
|
|
|
|
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,
|
|
amount=related_document.amount_total,
|
|
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:
|
|
link.has_multiple_acquirers = len(link.available_acquirer_ids) > 1
|
|
|
|
def _generate_link(self):
|
|
for payment_link in self:
|
|
related_document = self.env[payment_link.res_model].browse(payment_link.res_id)
|
|
base_url = related_document.get_base_url() # Don't generate links for the wrong website
|
|
payment_link.link = f'{base_url}/payment/pay' \
|
|
f'?reference={urls.url_quote(payment_link.description)}' \
|
|
f'&amount={payment_link.amount}' \
|
|
f'¤cy_id={payment_link.currency_id.id}' \
|
|
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.payment_acquirer_selection) if payment_link.payment_acquirer_selection != "all" else "" }' \
|
|
f'&access_token={payment_link.access_token}'
|