This commit aims to improve the user experience when using payment acquirers. There currently are no error feedback with some acquirers, which leaves the user wondering what is going on and what is the real status of its payment. In some cases, the user is currently being redirected to the home page even though the payment has failed. We want to make it more obvious to the user that something unexpected has happened by redirecting to an intermediate page that will provide good feedback on payments status. Another goal of this commit is to order acquirers by sequence instead of by flow and to select the first acquirer by default. This feature was already implmented in commit fe294fd43e521bd2d339e962f43acf46c3d4cb97, some UI adaptations were needed though. Related to task #36680 Closes #26958
164 lines
7.4 KiB
Python
164 lines
7.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
import re
|
|
|
|
from odoo import api, fields, models, _
|
|
|
|
|
|
class PaymentAcquirer(models.Model):
|
|
_inherit = 'payment.acquirer'
|
|
|
|
so_reference_type = fields.Selection(string='Communication',
|
|
selection=[
|
|
('so_name', _('Based on Document Reference')),
|
|
('partner', _('Based on Customer ID'))], default='so_name',
|
|
help='You can set here the communication type that will appear on sales orders.'
|
|
'The communication will be given to the customer when they choose the payment method.')
|
|
|
|
|
|
class PaymentTransaction(models.Model):
|
|
_inherit = 'payment.transaction'
|
|
|
|
sale_order_ids = fields.Many2many('sale.order', 'sale_order_transaction_rel', 'transaction_id', 'sale_order_id',
|
|
string='Sales Orders', copy=False, readonly=True)
|
|
sale_order_ids_nbr = fields.Integer(compute='_compute_sale_order_ids_nbr', string='# of Sales Orders')
|
|
|
|
@api.multi
|
|
def _compute_sale_order_reference(self, order):
|
|
self.ensure_one()
|
|
if self.acquirer_id.so_reference_type == 'so_name':
|
|
identification_number = int(re.match('.*?([0-9]+)$', order.name).group(1))
|
|
prefix = order.name
|
|
else:
|
|
# self.acquirer_id.so_reference_type == 'partner'
|
|
identification_number = order.partner_id.id
|
|
prefix = 'CUST'
|
|
return '%s/%s' % (prefix, str(identification_number % 97).rjust(2, '0'))
|
|
|
|
@api.depends('sale_order_ids')
|
|
def _compute_sale_order_ids_nbr(self):
|
|
for trans in self:
|
|
trans.sale_order_ids_nbr = len(trans.sale_order_ids)
|
|
|
|
@api.multi
|
|
def _log_payment_transaction_sent(self):
|
|
super(PaymentTransaction, self)._log_payment_transaction_sent()
|
|
for trans in self:
|
|
post_message = trans._get_payment_transaction_sent_message()
|
|
for so in trans.sale_order_ids:
|
|
so.message_post(body=post_message)
|
|
|
|
@api.multi
|
|
def _log_payment_transaction_received(self):
|
|
super(PaymentTransaction, self)._log_payment_transaction_received()
|
|
for trans in self.filtered(lambda t: t.provider not in ('manual', 'transfer')):
|
|
post_message = trans._get_payment_transaction_received_message()
|
|
for so in trans.sale_order_ids:
|
|
so.message_post(body=post_message)
|
|
|
|
@api.multi
|
|
def _set_transaction_pending(self):
|
|
# Override of '_set_transaction_pending' in the 'payment' module
|
|
# to sent the quotations automatically.
|
|
super(PaymentTransaction, self)._set_transaction_pending()
|
|
|
|
for record in self:
|
|
sales_orders = record.sale_order_ids.filtered(lambda so: so.state == 'draft')
|
|
sales_orders.force_quotation_send()
|
|
|
|
if record.acquirer_id.provider == 'transfer':
|
|
for so in sales_orders:
|
|
so.reference = record._compute_sale_order_reference(so)
|
|
|
|
@api.multi
|
|
def _set_transaction_authorized(self):
|
|
# Override of '_set_transaction_authorized' in the 'payment' module
|
|
# to confirm the quotations automatically.
|
|
super(PaymentTransaction, self)._set_transaction_authorized()
|
|
sales_orders = self.mapped('sale_order_ids').filtered(lambda so: so.state == 'draft')
|
|
sales_orders.force_quotation_send()
|
|
sales_orders = self.mapped('sale_order_ids').filtered(lambda so: so.state == 'sent')
|
|
for so in sales_orders:
|
|
# For loop because some override of action_confirm are ensure_one.
|
|
so.action_confirm()
|
|
|
|
@api.multi
|
|
def _reconcile_after_transaction_done(self):
|
|
# Override of '_set_transaction_done' in the 'payment' module
|
|
# to confirm the quotations automatically and to generate the invoices if needed.
|
|
sales_orders = self.mapped('sale_order_ids').filtered(lambda so: so.state == 'draft')
|
|
sales_orders.force_quotation_send()
|
|
sales_orders = self.mapped('sale_order_ids').filtered(lambda so: so.state == 'sent')
|
|
sales_orders.action_confirm()
|
|
# invoice the sale orders if needed
|
|
self._invoice_sale_orders()
|
|
res = super(PaymentTransaction, self)._reconcile_after_transaction_done()
|
|
if self.env['ir.config_parameter'].sudo().get_param('sale.automatic_invoice'):
|
|
default_template = self.env['ir.config_parameter'].sudo().get_param('sale.default_email_template')
|
|
if default_template:
|
|
ctx_company = {'company_id': self.acquirer_id.company_id.id,
|
|
'force_company': self.acquirer_id.company_id.id,
|
|
'mark_invoice_as_sent': True,
|
|
}
|
|
for trans in self.filtered(lambda t: t.sale_order_ids):
|
|
trans = trans.with_context(ctx_company)
|
|
for invoice in trans.invoice_ids:
|
|
invoice.message_post_with_template(int(default_template), notif_layout="mail.mail_notification_paynow")
|
|
return res
|
|
|
|
@api.multi
|
|
def _invoice_sale_orders(self):
|
|
if self.env['ir.config_parameter'].sudo().get_param('sale.automatic_invoice'):
|
|
ctx_company = {'company_id': self.acquirer_id.company_id.id,
|
|
'force_company': self.acquirer_id.company_id.id}
|
|
for trans in self.filtered(lambda t: t.sale_order_ids):
|
|
trans = trans.with_context(ctx_company)
|
|
trans.sale_order_ids._force_lines_to_invoice_policy_order()
|
|
invoices = trans.sale_order_ids.action_invoice_create()
|
|
trans.invoice_ids = [(6, 0, invoices)]
|
|
|
|
@api.model
|
|
def _compute_reference_prefix(self, values):
|
|
prefix = super(PaymentTransaction, self)._compute_reference_prefix(values)
|
|
if not prefix and values and values.get('sale_order_ids'):
|
|
many_list = self.resolve_2many_commands('sale_order_ids', values['sale_order_ids'], fields=['name'])
|
|
return ','.join(dic['name'] for dic in many_list)
|
|
return prefix
|
|
|
|
@api.multi
|
|
def action_view_sales_orders(self):
|
|
action = {
|
|
'name': _('Sales Order(s)'),
|
|
'type': 'ir.actions.act_window',
|
|
'res_model': 'sale.order',
|
|
'target': 'current',
|
|
}
|
|
sale_order_ids = self.sale_order_ids.ids
|
|
if len(sale_order_ids) == 1:
|
|
action['res_id'] = sale_order_ids[0]
|
|
action['view_mode'] = 'form'
|
|
else:
|
|
action['view_mode'] = 'tree,form'
|
|
action['domain'] = [('id', 'in', sale_order_ids)]
|
|
return action
|
|
|
|
# --------------------------------------------------
|
|
# Tools for payment
|
|
# --------------------------------------------------
|
|
|
|
def render_sale_button(self, order, submit_txt=None, render_values=None):
|
|
values = {
|
|
'partner_id': order.partner_shipping_id.id or order.partner_invoice_id.id,
|
|
'billing_partner_id': order.partner_invoice_id.id,
|
|
}
|
|
if render_values:
|
|
values.update(render_values)
|
|
# Not very elegant to do that here but no choice regarding the design.
|
|
self._log_payment_transaction_sent()
|
|
return self.acquirer_id.with_context(submit_class='btn btn-primary', submit_txt=submit_txt or _('Pay Now')).sudo().render(
|
|
self.reference,
|
|
order.amount_total,
|
|
order.pricelist_id.currency_id.id,
|
|
values=values,
|
|
)
|