[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:
@@ -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()
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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">&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">&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">&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">&times;</button>
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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.'))
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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')">
|
||||
|
||||
Reference in New Issue
Block a user