[IMP] sale: add reference on SO + improve usability

Like invoices, a reference is now computed on sales orders.
The configuration of such reference is done through the Wire Transfer acquirer.

The usability of the communication feature (on invoices/SO) has been a bit improved to be more clear.

Was task: 1870121
Was PR #26152
This commit is contained in:
Laurent Smet
2018-08-14 14:44:11 +02:00
committed by qdp-odoo
parent c8a4f42ef8
commit ef42e03b69
11 changed files with 72 additions and 15 deletions
+3 -3
View File
@@ -248,8 +248,8 @@ class AccountInvoice(models.Model):
move_name = fields.Char(string='Journal Entry Name', readonly=False,
default=False, copy=False,
help="Technical field holding the number given to the invoice, automatically set when the invoice is validated then stored to set the same number again if the invoice is cancelled, set to draft and re-validated.")
reference = fields.Char(string='Payment Ref.', copy=False,
help="The partner reference of this invoice.", readonly=True, states={'draft': [('readonly', False)]})
reference = fields.Char(string='Payment Ref.', copy=False, readonly=True, states={'draft': [('readonly', False)]},
help='The payment communication that will be automatically populated once the invoice validation. You can also write a free communication.')
comment = fields.Text('Additional Information', readonly=True, states={'draft': [('readonly', False)]})
state = fields.Selection([
@@ -1270,7 +1270,7 @@ class AccountInvoice(models.Model):
invoice.message_subscribe([invoice.partner_id.id])
# Auto-compute reference, if not already existing and if configured on company
if not invoice.reference and invoice.company_id.invoice_reference_type != 'none' and invoice.type == 'out_invoice':
if not invoice.reference and invoice.type == 'out_invoice':
invoice.reference = invoice._get_computed_reference()
self._check_duplicate_supplier_reference()
+2 -2
View File
@@ -16,7 +16,7 @@ class ResCompany(models.Model):
_inherit = "res.company"
def _get_invoice_reference_types(self):
return [('invoice_number', _('Based on Invoice Number')), ('partner', _('Based on Partner')), ('none', _('Free Communication'))]
return [('invoice_number', _('Based on Invoice Number')), ('partner', _('Based on Customer'))]
#TODO check all the options/fields are in the views (settings + company form view)
fiscalyear_last_day = fields.Integer(default=31, required=True)
@@ -64,7 +64,7 @@ Best Regards,'''))
incoterm_id = fields.Many2one('account.incoterms', string='Default incoterm',
help='International Commercial Terms are a series of predefined commercial terms used in international transactions.')
invoice_reference_type = fields.Selection(string='Default Communication Type', selection='_get_invoice_reference_types',
default='none', help='You can set here the default communication that will appear on customer invoices, once validated, to help the customer to refer to that particular invoice when making the payment.')
default='invoice_number', help='You can set here the default communication that will appear on customer invoices, once validated, to help the customer to refer to that particular invoice when making the payment.')
account_sanitize_invoice_ref = fields.Boolean(string="Sanitize Invoice References", default=True, help="Whether or not customer invoices and vendor bills should automatically correct their reference they are maximum 140 characters long, consist only of latin characters, contain no '//' sequence, and have no leading or trailing /. (these are the SEPA criteria for payment communications)")
qr_code = fields.Boolean(string='Display SEPA QR code')
@@ -261,10 +261,10 @@
<div class="o_setting_right_pane">
<label for="invoice_reference_type"/>
<div class="text-muted">
Default payment communication on customer invoices.
Default payment communication on customer invoices
</div>
<div class="content-group">
<div class="row mt16">
<div class="row mt16 ml4">
<field name="invoice_reference_type"/>
</div>
</div>
@@ -89,6 +89,7 @@
<div t-if="invoice.transaction_ids and invoice.amount_total and not success and not error" class="o_account_payment_tx_status" t-att-data-invoice-id="invoice.id">
<t t-call="payment.payment_confirmation_status">
<t t-set="payment_tx_id" t-value="invoice.get_portal_last_transaction()"/>
<t t-set="reference" t-value="invoice.reference"/>
</t>
</div>
<t t-set="tx_ids" t-value="invoice.transaction_ids.filtered(lambda tx: tx.state in ('authorized', 'done'))"/>
@@ -134,6 +135,9 @@
<t t-set="payment_tx_id" t-value="invoice.get_portal_last_transaction()"/>
<span t-if='payment_tx_id.acquirer_id.done_msg' t-raw="payment_tx_id.acquirer_id.done_msg"/>
<span t-if='payment_tx_id.acquirer_id.post_msg' t-raw="payment_tx_id.acquirer_id.post_msg"/>
<div t-if="payment_tx_id.acquirer_id.post_msg and payment_tx_id.acquirer_id.provider == 'transfer' and invoice.reference">
<b>Communication: </b><span t-esc='invoice.reference'/>
</div>
</t>
<t t-if="success == 'pay_invoice' and invoice.state == 'paid' and not invoice.payment_acquirer_id">
Done, your online payment has been successfully processed. Thank you for your order.
+2 -2
View File
@@ -640,13 +640,13 @@ class PaymentTransaction(models.Model):
def _get_payment_transaction_sent_message(self):
self.ensure_one()
if self.payment_token_id:
message = _('A transaction %s with %s has been initiated using %s credit card.')
message = _('A transaction %s with %s initiated using %s credit card.')
message_vals = (self.reference, self.acquirer_id.name, self.payment_token_id.name)
elif self.provider in ('manual', 'transfer'):
message = _('The customer has selected %s to pay this document.')
message_vals = (self.acquirer_id.name)
else:
message = _('A transaction %s with %s has been initiated.')
message = _('A transaction %s with %s initiated.')
message_vals = (self.reference, self.acquirer_id.name)
if self.provider not in ('manual', 'transfer'):
message += ' ' + _('Waiting for payment confirmation...')
@@ -134,6 +134,9 @@
<button type="button" class="close" data-dismiss="alert" aria-label="Close">&amp;times;</button>
<span t-if='payment_tx_id.acquirer_id.pending_msg' t-raw="payment_tx_id.acquirer_id.pending_msg"/>
<span t-if='thanks_msg' t-raw="thanks_msg"/>
<div t-if="thanks_msg and payment_tx_id.acquirer_id.provider == 'transfer' and reference">
<b>Communication: </b><span t-esc='reference'/>
</div>
<div t-if="payment_tx_id.acquirer_id.company_id.qr_code and payment_tx_id.acquirer_id.company_id.qr_code_valid and (payment_tx_id.currency_id.name == 'EUR')">
<h3>Or scan me with your banking app.</h3>
<img class="border border-dark rounded" t-att-src="payment_tx_id.acquirer_id.company_id.qr_code_payment_journal_id.bank_account_id.build_qr_code_url(payment_tx_id.amount,payment_tx_id.reference)"/>
@@ -146,11 +149,17 @@
<button type="button" class="close" data-dismiss="alert" title="Dismiss" aria-label="Dismiss">&amp;times;</button>
Your payment has been authorized.
<span t-if='thanks_msg' t-raw="thanks_msg"/>
<div t-if="thanks_msg and payment_tx_id.acquirer_id.provider == 'transfer' and reference">
<b>Communication: </b><span t-esc='reference'/>
</div>
</div>
<div t-if="payment_tx_id and payment_tx_id.state == 'done'" class="alert alert-success alert-dismissable">
<button type="button" class="close" data-dismiss="alert" title="Dismiss" aria-label="Dismiss">&amp;times;</button>
<span t-if='payment_tx_id.acquirer_id.done_msg' t-raw="payment_tx_id.acquirer_id.done_msg"/>
<span t-if='thanks_msg' t-raw="thanks_msg"/>
<div t-if="thanks_msg and payment_tx_id.acquirer_id.provider == 'transfer' and reference">
<b>Communication: </b><span t-esc='reference'/>
</div>
</div>
<div t-if="payment_tx_id and payment_tx_id.state == 'cancel'" class="alert alert-danger alert-dismissable">
<button type="button" class="close" data-dismiss="alert" title="Dismiss" aria-label="Dismiss">&amp;times;</button>
+30 -6
View File
@@ -1,12 +1,19 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
import re
from odoo import api, fields, models, _
from odoo.exceptions import UserError
from odoo.tools import float_compare
_logger = logging.getLogger(__name__)
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):
@@ -16,6 +23,18 @@ class PaymentTransaction(models.Model):
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:
@@ -43,8 +62,13 @@ class PaymentTransaction(models.Model):
# to sent the quotations automatically.
super(PaymentTransaction, self)._set_transaction_pending()
sales_orders = self.mapped('sale_order_ids').filtered(lambda so: so.state == 'draft')
sales_orders.force_quotation_send()
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):
+5
View File
@@ -118,6 +118,8 @@ class SaleOrder(models.Model):
name = fields.Char(string='Order Reference', required=True, copy=False, readonly=True, states={'draft': [('readonly', False)]}, index=True, default=lambda self: _('New'))
origin = fields.Char(string='Source Document', help="Reference of the document that generated this sales order request.")
client_order_ref = fields.Char(string='Customer Reference', copy=False)
reference = fields.Char(string='Payment Ref.', copy=False,
help='The payment communication of this sale order.')
state = fields.Selection([
('draft', 'Quotation'),
('sent', 'Quotation Sent'),
@@ -508,6 +510,9 @@ class SaleOrder(models.Model):
for group_key in invoices:
invoices[group_key].write({'name': ', '.join(invoices_name[group_key]),
'origin': ', '.join(invoices_origin[group_key])})
sale_orders = references[invoices[group_key]]
if len(sale_orders) == 1:
invoices[group_key].reference = sale_orders.reference
if not invoices:
raise UserError(_('There is no invoiceable line. If a product has a Delivered quantities invoicing policy, please make sure that a quantity has been delivered.'))
+11
View File
@@ -1,6 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="acquirer_form_inherit_sale" model="ir.ui.view">
<field name="name">payment.acquirer.form.inherit.sale.payment</field>
<field name="model">payment.acquirer</field>
<field name="inherit_id" ref="payment.acquirer_form"/>
<field name="arch" type="xml">
<xpath expr="//group[@name='acquirer_config']" position="inside">
<field name="so_reference_type" attrs="{'invisible': [('provider', '!=', 'transfer')]}"/>
</xpath>
</field>
</record>
<record id="transaction_form_inherit_sale" model="ir.ui.view">
<field name="name">payment.transaction.form.inherit.sale.payment</field>
<field name="model">payment.transaction</field>
+1
View File
@@ -533,6 +533,7 @@
<field name="user_id"/>
<field name="team_id" options="{'no_create': True}"/>
<field name="client_order_ref"/>
<field name="reference" readonly="1" attrs="{'invisible': [('reference', '=', False)]}"/>
<field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/>
<field name="analytic_account_id" context="{'default_partner_id':partner_invoice_id, 'default_name':name}" attrs="{'readonly': [('invoice_count','!=',0),('state','=','sale')]}" groups="analytic.group_analytic_accounting"/>
</group>
+3
View File
@@ -1591,6 +1591,9 @@
</div>
<div class="card-body" t-if="payment_tx_id.acquirer_id.post_msg">
<t t-raw="payment_tx_id.acquirer_id.post_msg" />
<div t-if="payment_tx_id.acquirer_id.provider == 'transfer' and order.reference">
<b>Communication: </b><span t-esc='order.reference'/>
</div>
</div>
<div t-if="payment_tx_id.acquirer_id.provider == 'transfer'">
<div class="card-body" t-if="payment_tx_id.acquirer_id.company_id.qr_code and payment_tx_id.acquirer_id.company_id.qr_code_valid and (payment_tx_id.currency_id.name == 'EUR')">