[IMP] account: automatic communication management on out_invoices
On the accounting config, you can select an automatic way to compute the reference on invoice and then, improve the reconciliation in a later task: - free communication: set what you want as reference (default) - based on partner: find the partner more easily - based on number: retrieve the invoice directly Was task: 1847703 Was part of PR #25921
This commit is contained in:
@@ -98,10 +98,6 @@ class AccountInvoice(models.Model):
|
||||
journal = self._default_journal()
|
||||
return journal.currency_id or journal.company_id.currency_id or self.env.user.company_id.currency_id
|
||||
|
||||
@api.model
|
||||
def _get_reference_type(self):
|
||||
return [('none', _('Free Reference'))]
|
||||
|
||||
@api.one
|
||||
@api.depends(
|
||||
'state', 'currency_id', 'invoice_line_ids.price_subtotal',
|
||||
@@ -256,11 +252,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='Vendor Reference', copy=False,
|
||||
reference = fields.Char(string='Payment Ref.', copy=False,
|
||||
help="The partner reference of this invoice.", readonly=True, states={'draft': [('readonly', False)]})
|
||||
reference_type = fields.Selection('_get_reference_type', string='Payment Reference',
|
||||
required=True, readonly=True, states={'draft': [('readonly', False)]},
|
||||
default='none')
|
||||
comment = fields.Text('Additional Information', readonly=True, states={'draft': [('readonly', False)]})
|
||||
|
||||
state = fields.Selection([
|
||||
@@ -394,6 +387,18 @@ class AccountInvoice(models.Model):
|
||||
invoice.vendor_display_name = vendor_display_name
|
||||
invoice.invoice_icon = invoice.source_email and '@' or ''
|
||||
|
||||
@api.multi
|
||||
def _get_computed_reference(self):
|
||||
self.ensure_one()
|
||||
if self.company_id.invoice_reference_type == 'invoice_number':
|
||||
identification_number = int(re.match('.*?([0-9]+)$', self.number).group(1))
|
||||
prefix = self.number
|
||||
else:
|
||||
#self.company_id.invoice_reference_type == 'partner'
|
||||
identification_number = self.partner_id.id
|
||||
prefix = 'CUST'
|
||||
return '%s/%s' % (prefix, str(identification_number % 97).rjust(2, '0'))
|
||||
|
||||
# Load all Vendor Bill lines
|
||||
@api.onchange('vendor_bill_id')
|
||||
def _onchange_vendor_bill(self):
|
||||
@@ -1310,7 +1315,12 @@ class AccountInvoice(models.Model):
|
||||
def invoice_validate(self):
|
||||
for invoice in self.filtered(lambda invoice: invoice.partner_id not in invoice.message_partner_ids):
|
||||
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':
|
||||
invoice.reference = invoice._get_computed_reference()
|
||||
self._check_duplicate_supplier_reference()
|
||||
|
||||
return self.write({'state': 'open'})
|
||||
|
||||
@api.model
|
||||
|
||||
@@ -15,6 +15,9 @@ from odoo.tools import DEFAULT_SERVER_DATE_FORMAT, date_utils
|
||||
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'))]
|
||||
|
||||
#TODO check all the options/fields are in the views (settings + company form view)
|
||||
fiscalyear_last_day = fields.Integer(default=31, required=True)
|
||||
fiscalyear_last_month = fields.Selection([(1, 'January'), (2, 'February'), (3, 'March'), (4, 'April'), (5, 'May'), (6, 'June'), (7, 'July'), (8, 'August'), (9, 'September'), (10, 'October'), (11, 'November'), (12, 'December')], default=12, required=True)
|
||||
@@ -54,6 +57,11 @@ If you have any queries regarding your account, Please contact us.
|
||||
Thank you in advance for your cooperation.
|
||||
Best Regards,'''))
|
||||
tax_exigibility = fields.Boolean(string='Use Cash Basis')
|
||||
|
||||
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.')
|
||||
|
||||
#Fields of the setup step for opening move
|
||||
account_opening_move_id = fields.Many2one(string='Opening Journal Entry', comodel_name='account.move', help="The journal entry containing the initial balance of all this company's accounts.")
|
||||
@@ -66,8 +74,6 @@ Best Regards,'''))
|
||||
account_setup_fy_data_done = fields.Boolean('Financial Year Setup Marked As Done', help="Technical field holding the status of the financial year setup step.")
|
||||
account_setup_coa_done = fields.Boolean(string='Chart of Account Checked', help="Technical field holding the status of the chart of account setup step.")
|
||||
account_setup_bar_closed = fields.Boolean(string='Setup Bar Closed', help="Technical field set to True when setup bar has been closed by the user.")
|
||||
incoterm_id = fields.Many2one('account.incoterms', string='Default incoterm',
|
||||
help='International Commercial Terms are a series of predefined commercial terms used in international transactions.')
|
||||
|
||||
# account invoice onboarding
|
||||
account_invoice_onboarding_closed = fields.Boolean(
|
||||
|
||||
@@ -75,6 +75,8 @@ class ResConfigSettings(models.TransientModel):
|
||||
tax_exigibility = fields.Boolean(string='Cash Basis', related='company_id.tax_exigibility')
|
||||
tax_cash_basis_journal_id = fields.Many2one('account.journal', related='company_id.tax_cash_basis_journal_id', string="Tax Cash Basis Journal")
|
||||
account_hide_setup_bar = fields.Boolean(string='Hide Setup Bar', related='company_id.account_setup_bar_closed',help="Tick if you wish to hide the setup bar on the dashboard")
|
||||
invoice_reference_type = fields.Selection(string='Communication',
|
||||
related='company_id.invoice_reference_type', help='Default Reference Type on Invoices.')
|
||||
|
||||
@api.multi
|
||||
def set_values(self):
|
||||
|
||||
@@ -48,7 +48,6 @@ class TestAccountCustomerInvoice(AccountTestUsers):
|
||||
|
||||
self.account_invoice_customer0 = self.account_invoice_obj.sudo(self.account_user.id).create(dict(
|
||||
name="Test Customer Invoice",
|
||||
reference_type="none",
|
||||
payment_term_id=self.payment_term.id,
|
||||
journal_id=self.journalrec.id,
|
||||
partner_id=self.partner3.id,
|
||||
|
||||
@@ -35,7 +35,6 @@ class TestAccountInvoiceRounding(AccountingTestCase):
|
||||
""" Returns an open invoice """
|
||||
invoice_id = self.env['account.invoice'].create({
|
||||
'partner_id': self.env.ref("base.res_partner_2").id,
|
||||
'reference_type': 'none',
|
||||
'currency_id': self.env.ref('base.USD').id,
|
||||
'name': 'invoice test rounding',
|
||||
'account_id': self.account_receivable.id,
|
||||
|
||||
@@ -45,7 +45,6 @@ class TestPayment(AccountingTestCase):
|
||||
""" Returns an open invoice """
|
||||
invoice = self.invoice_model.create({
|
||||
'partner_id': partner or self.partner_agrolait.id,
|
||||
'reference_type': 'none',
|
||||
'currency_id': currency_id or self.currency_eur_id,
|
||||
'name': type,
|
||||
'account_id': account_id or self.account_receivable.id,
|
||||
|
||||
@@ -55,7 +55,6 @@ class TestProductIdChange(AccountingTestCase):
|
||||
|
||||
out_invoice = self.invoice_model.create({
|
||||
'partner_id': partner.id,
|
||||
'reference_type': 'none',
|
||||
'name': 'invoice to client',
|
||||
'account_id': self.account_receivable.id,
|
||||
'type': 'out_invoice',
|
||||
@@ -73,7 +72,6 @@ class TestProductIdChange(AccountingTestCase):
|
||||
|
||||
in_invoice = self.invoice_model.create({
|
||||
'partner_id': partner.id,
|
||||
'reference_type': 'none',
|
||||
'name': 'invoice to supplier',
|
||||
'account_id': self.account_receivable.id,
|
||||
'type': 'in_invoice',
|
||||
|
||||
@@ -52,7 +52,6 @@ class TestReconciliation(AccountingTestCase):
|
||||
def create_invoice(self, type='out_invoice', invoice_amount=50, currency_id=None):
|
||||
#we create an invoice in given currency
|
||||
invoice = self.account_invoice_model.create({'partner_id': self.partner_agrolait_id,
|
||||
'reference_type': 'none',
|
||||
'currency_id': currency_id,
|
||||
'name': type == 'out_invoice' and 'invoice to client' or 'invoice to vendor',
|
||||
'account_id': self.account_rcv.id,
|
||||
@@ -530,7 +529,6 @@ class TestReconciliation(AccountingTestCase):
|
||||
|
||||
# Preparing Invoices (from vendor)
|
||||
invoice_a = self.account_invoice_model.create({'partner_id': self.partner_agrolait_id,
|
||||
'reference_type': 'none',
|
||||
'currency_id': self.currency_usd_id,
|
||||
'name': 'invoice to vendor',
|
||||
'account_id': self.account_rsa.id,
|
||||
@@ -546,7 +544,6 @@ class TestReconciliation(AccountingTestCase):
|
||||
})
|
||||
|
||||
invoice_b = self.account_invoice_model.create({'partner_id': self.partner_agrolait_id,
|
||||
'reference_type': 'none',
|
||||
'currency_id': self.currency_usd_id,
|
||||
'name': 'invoice to vendor',
|
||||
'account_id': self.account_rsa.id,
|
||||
@@ -766,7 +763,6 @@ class TestReconciliation(AccountingTestCase):
|
||||
def create_invoice_partner(self, type='out_invoice', invoice_amount=50, currency_id=None, partner_id=False):
|
||||
#we create an invoice in given currency
|
||||
invoice = self.account_invoice_model.create({'partner_id': partner_id,
|
||||
'reference_type': 'none',
|
||||
'currency_id': currency_id,
|
||||
'name': type == 'out_invoice' and 'invoice to client' or 'invoice to vendor',
|
||||
'account_id': self.account_rcv.id,
|
||||
|
||||
@@ -585,6 +585,7 @@
|
||||
<field name="origin" groups="base.group_user" attrs="{'invisible': [('origin', '=', False)]}"/>
|
||||
<field name="incoterm_id" options="{'no_create': True}"/>
|
||||
<field name="name"/>
|
||||
<field name="reference" attrs="{'readonly':[('state','!=','draft')]}"/>
|
||||
</group>
|
||||
</group>
|
||||
<field name="tax_line_ids">
|
||||
|
||||
@@ -195,6 +195,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<p t-if="o.reference">
|
||||
Please use the following communication for your payment : <b><span t-field="o.reference"/></b>
|
||||
</p>
|
||||
<p t-if="o.comment" name="comment">
|
||||
<span t-field="o.comment"/>
|
||||
</p>
|
||||
|
||||
@@ -257,6 +257,20 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xs-12 col-md-6 o_setting_box">
|
||||
<div class="o_setting_left_pane"/>
|
||||
<div class="o_setting_right_pane">
|
||||
<label for="invoice_reference_type"/>
|
||||
<div class="text-muted">
|
||||
Default payment communication on customer invoices.
|
||||
</div>
|
||||
<div class="content-group">
|
||||
<div class="row mt16">
|
||||
<field name="invoice_reference_type"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h2>Supplier Payments</h2>
|
||||
|
||||
@@ -32,7 +32,6 @@ class TestPrintCheck(AccountingTestCase):
|
||||
def create_invoice(self, amount=100, is_refund=False):
|
||||
invoice = self.invoice_model.create({
|
||||
'partner_id': self.partner_axelor.id,
|
||||
'reference_type': 'none',
|
||||
'name': is_refund and "Supplier Refund" or "Supplier Invoice",
|
||||
'type': is_refund and "in_refund" or "in_invoice",
|
||||
'account_id': self.account_payable.id,
|
||||
|
||||
@@ -10,11 +10,8 @@
|
||||
'website': 'https://www.odoo.com/page/accounting',
|
||||
'category': 'Localization',
|
||||
'description': """
|
||||
|
||||
Belgian localization for in- and outgoing invoices (prereq to account_coda):
|
||||
============================================================================
|
||||
- Rename 'reference' field labels to 'Communication'
|
||||
- Add support for Belgian Structured Communication
|
||||
|
||||
Add support for Belgian Structured Communication
|
||||
|
||||
A Structured Communication can be generated automatically on outgoing invoices according to the following algorithms:
|
||||
---------------------------------------------------------------------------------------------------------------------
|
||||
@@ -34,7 +31,5 @@ generated if no algorithm is specified on the Partner record.
|
||||
'data' : [
|
||||
'data/mail_template_data.xml',
|
||||
'views/res_partner_view.xml',
|
||||
'views/account_invoice_view.xml',
|
||||
'views/report_invoice.xml',
|
||||
],
|
||||
}
|
||||
|
||||
@@ -7,179 +7,75 @@ import random
|
||||
import re
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
"""
|
||||
account.invoice object:
|
||||
- Add support for Belgian structured communication
|
||||
- Rename 'reference' field labels to 'Communication'
|
||||
account.invoice object: add support for Belgian structured communication
|
||||
"""
|
||||
|
||||
|
||||
class AccountInvoice(models.Model):
|
||||
_inherit = 'account.invoice'
|
||||
|
||||
@api.model
|
||||
def _get_reference_type(self):
|
||||
"""Add BBA Structured Communication Type and change labels from 'reference' into 'communication' """
|
||||
res = super(AccountInvoice, self)._get_reference_type()
|
||||
res[[i for i, x in enumerate(res) if x[0] == 'none'][0]] = ('none', _('Free Communication'))
|
||||
res.append(('bba', 'BBA Structured Communication'))
|
||||
return res
|
||||
|
||||
reference_type = fields.Selection('_get_reference_type', string='Payment Reference',
|
||||
required=True, readonly=True)
|
||||
|
||||
@api.constrains('reference', 'reference_type')
|
||||
def _check_communication(self):
|
||||
for inv in self:
|
||||
if inv.reference_type == 'bba' and not self.check_bbacomm(inv.reference):
|
||||
raise ValidationError(_('Invalid BBA Structured Communication.'))
|
||||
|
||||
def check_bbacomm(self, val):
|
||||
supported_chars = '0-9+*/ '
|
||||
pattern = re.compile('[^' + supported_chars + ']')
|
||||
if pattern.findall(val or ''):
|
||||
return False
|
||||
bbacomm = re.sub('\D', '', val or '')
|
||||
if len(bbacomm) == 12:
|
||||
base = int(bbacomm[:10])
|
||||
mod = base % 97 or 97
|
||||
if mod == int(bbacomm[-2:]):
|
||||
return True
|
||||
|
||||
@api.onchange('partner_id', 'type', 'reference_type')
|
||||
def _onchange_partner_id(self):
|
||||
result = super(AccountInvoice, self)._onchange_partner_id()
|
||||
reference = False
|
||||
reference_type = 'none'
|
||||
if self.partner_id:
|
||||
if (self.type == 'out_invoice'):
|
||||
reference_type = self.partner_id.out_inv_comm_type
|
||||
if reference_type:
|
||||
reference = self.generate_bbacomm(self.type, reference_type, self.partner_id.id, '')['value']['reference']
|
||||
self.reference_type = reference_type or 'none'
|
||||
self.reference = reference
|
||||
return result
|
||||
|
||||
def generate_bbacomm(self, type, reference_type, partner_id, reference):
|
||||
reference = reference or ''
|
||||
algorithm = False
|
||||
if partner_id:
|
||||
algorithm = self.env['res.partner'].browse(partner_id).out_inv_comm_algorithm
|
||||
algorithm = algorithm or 'random'
|
||||
if (type == 'out_invoice'):
|
||||
if reference_type == 'bba':
|
||||
if algorithm == 'date':
|
||||
if not self.check_bbacomm(reference):
|
||||
date = fields.Date.from_string(fields.Date.today())
|
||||
doy = date.strftime('%j')
|
||||
year = date.strftime('%Y')
|
||||
seq = '001'
|
||||
invoices = self.search([('type', '=', 'out_invoice'), ('reference_type', '=', 'bba'),
|
||||
('reference', 'like', '+++%s/%s/%%' % (doy, year))], order='reference')
|
||||
if invoices:
|
||||
prev_seq = int(invoices[-1].reference[12:15])
|
||||
if prev_seq < 999:
|
||||
seq = '%03d' % (prev_seq + 1)
|
||||
else:
|
||||
raise UserError(_('The daily maximum of outgoing invoices with an automatically generated BBA Structured Communications has been exceeded!'
|
||||
'\nPlease create manually a unique BBA Structured Communication.'))
|
||||
bbacomm = doy + year + seq
|
||||
base = int(bbacomm)
|
||||
mod = base % 97 or 97
|
||||
reference = '+++%s/%s/%s%02d+++' % (doy, year, seq, mod)
|
||||
elif algorithm == 'partner_ref':
|
||||
if not self.check_bbacomm(reference):
|
||||
partner_ref = self.env['res.partner'].browse(partner_id).ref
|
||||
partner_ref_nr = re.sub('\D', '', partner_ref or '')
|
||||
if (len(partner_ref_nr) < 3) or (len(partner_ref_nr) > 7):
|
||||
raise UserError(_('The Partner should have a 3-7 digit Reference Number for the generation of BBA Structured Communications!'
|
||||
'\nPlease correct the Partner record.'))
|
||||
else:
|
||||
partner_ref_nr = partner_ref_nr.ljust(7, '0')
|
||||
seq = '001'
|
||||
invoices = self.search([('type', '=', 'out_invoice'), ('reference_type', '=', 'bba'),
|
||||
('reference', 'like', '+++%s/%s/%%' % (partner_ref_nr[:3], partner_ref_nr[3:]))], order='reference')
|
||||
if invoices:
|
||||
prev_seq = int(invoices[-1].reference[12:15])
|
||||
if prev_seq < 999:
|
||||
seq = '%03d' % (prev_seq + 1)
|
||||
else:
|
||||
raise UserError(_('The daily maximum of outgoing invoices with an automatically generated BBA Structured Communications has been exceeded!'
|
||||
'\nPlease create manually a unique BBA Structured Communication.'))
|
||||
bbacomm = partner_ref_nr + seq
|
||||
base = int(bbacomm)
|
||||
mod = base % 97 or 97
|
||||
reference = '+++%s/%s/%s%02d+++' % (partner_ref_nr[:3], partner_ref_nr[3:], seq, mod)
|
||||
elif algorithm == 'random':
|
||||
if not self.check_bbacomm(reference):
|
||||
base = random.randint(1, 9999999999)
|
||||
bbacomm = str(base).rjust(10, '0')
|
||||
base = int(bbacomm)
|
||||
mod = base % 97 or 97
|
||||
mod = str(mod).rjust(2, '0')
|
||||
reference = '+++%s/%s/%s%s+++' % (bbacomm[:3], bbacomm[3:7], bbacomm[7:], mod)
|
||||
def generate_bbacomm(self, partner):
|
||||
reference = ''
|
||||
algorithm = partner.out_inv_comm_algorithm or 'random'
|
||||
if algorithm == 'date':
|
||||
date = fields.Date.from_string(fields.Date.today())
|
||||
doy = date.strftime('%j')
|
||||
year = date.strftime('%Y')
|
||||
seq = '001'
|
||||
invoices = self.search([('type', '=', 'out_invoice'),
|
||||
('reference', 'like', '+++%s/%s/%%' % (doy, year))], order='reference')
|
||||
if invoices:
|
||||
prev_seq = int(invoices[-1].reference[12:15])
|
||||
if prev_seq < 999:
|
||||
seq = '%03d' % (prev_seq + 1)
|
||||
else:
|
||||
raise UserError(_("Unsupported Structured Communication Type Algorithm '%s' !"
|
||||
"\nPlease contact your Odoo support channel.") % algorithm)
|
||||
return {'value': {'reference': reference}}
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
reference = vals.get('reference', False)
|
||||
reference_type = vals.get('reference_type', False)
|
||||
if vals.get('type') == 'out_invoice' and not reference_type:
|
||||
# fallback on default communication type for partner
|
||||
partner = self.env['res.partner'].browse(vals['partner_id'])
|
||||
reference_type = partner.out_inv_comm_type
|
||||
if reference_type == 'bba':
|
||||
reference = self.generate_bbacomm(vals['type'], reference_type, partner.id, '')['value']['reference']
|
||||
vals.update({
|
||||
'reference_type': reference_type or 'none',
|
||||
'reference': reference,
|
||||
})
|
||||
|
||||
if reference_type == 'bba':
|
||||
if not reference:
|
||||
raise UserError(_('Empty BBA Structured Communication!'
|
||||
'\nPlease fill in a unique BBA Structured Communication.'))
|
||||
if self.check_bbacomm(reference):
|
||||
reference = re.sub('\D', '', reference)
|
||||
vals['reference'] = '+++' + reference[0:3] + '/' + reference[3:7] + '/' + reference[7:] + '+++'
|
||||
same_ids = self.search([('type', '=', 'out_invoice'), ('reference_type', '=', 'bba'), ('reference', '=', vals['reference'])])
|
||||
if same_ids:
|
||||
raise UserError(_('The BBA Structured Communication has already been used!'
|
||||
raise UserError(_('The daily maximum of outgoing invoices with an automatically generated BBA Structured Communications has been exceeded!'
|
||||
'\nPlease create manually a unique BBA Structured Communication.'))
|
||||
return super(AccountInvoice, self).create(vals)
|
||||
|
||||
@api.multi
|
||||
def write(self, vals):
|
||||
for invoice in self:
|
||||
if 'reference_type' in vals:
|
||||
reference_type = vals['reference_type']
|
||||
bbacomm = doy + year + seq
|
||||
base = int(bbacomm)
|
||||
mod = base % 97 or 97
|
||||
reference = '+++%s/%s/%s%02d+++' % (doy, year, seq, mod)
|
||||
elif algorithm == 'partner_ref':
|
||||
partner_ref = partner.ref
|
||||
print(partner_ref)
|
||||
partner_ref_nr = re.sub('\D', '', partner_ref or '')
|
||||
if (len(partner_ref_nr) < 3) or (len(partner_ref_nr) > 7):
|
||||
raise UserError(_('The Partner should have a 3-7 digit Reference Number for the generation of BBA Structured Communications!'
|
||||
'\nPlease correct the Partner record.'))
|
||||
else:
|
||||
reference_type = invoice.reference_type or ''
|
||||
|
||||
if reference_type == 'bba' and 'reference' in vals:
|
||||
if self.check_bbacomm(vals['reference']):
|
||||
reference = re.sub('\D', '', vals['reference'])
|
||||
vals['reference'] = '+++' + reference[0:3] + '/' + reference[3:7] + '/' + reference[7:] + '+++'
|
||||
same_ids = self.search([('id', '!=', invoice.id), ('type', '=', 'out_invoice'),
|
||||
('reference_type', '=', 'bba'), ('reference', '=', vals['reference'])])
|
||||
if same_ids:
|
||||
raise UserError(_('The BBA Structured Communication has already been used!'
|
||||
partner_ref_nr = partner_ref_nr.ljust(7, '0')
|
||||
seq = '001'
|
||||
invoices = self.search([('type', '=', 'out_invoice'),
|
||||
('reference', 'like', '+++%s/%s/%%' % (partner_ref_nr[:3], partner_ref_nr[3:]))], order='reference')
|
||||
if invoices:
|
||||
prev_seq = int(invoices[-1].reference[12:15])
|
||||
if prev_seq < 999:
|
||||
seq = '%03d' % (prev_seq + 1)
|
||||
else:
|
||||
raise UserError(_('The daily maximum of outgoing invoices with an automatically generated BBA Structured Communications has been exceeded!'
|
||||
'\nPlease create manually a unique BBA Structured Communication.'))
|
||||
return super(AccountInvoice, self).write(vals)
|
||||
bbacomm = partner_ref_nr + seq
|
||||
base = int(bbacomm)
|
||||
mod = base % 97 or 97
|
||||
reference = '+++%s/%s/%s%02d+++' % (partner_ref_nr[:3], partner_ref_nr[3:], seq, mod)
|
||||
elif algorithm == 'random':
|
||||
base = random.randint(1, 9999999999)
|
||||
bbacomm = str(base).rjust(10, '0')
|
||||
base = int(bbacomm)
|
||||
mod = base % 97 or 97
|
||||
mod = str(mod).rjust(2, '0')
|
||||
reference = '+++%s/%s/%s%s+++' % (bbacomm[:3], bbacomm[3:7], bbacomm[7:], mod)
|
||||
else:
|
||||
raise UserError(_("Unsupported Structured Communication Type Algorithm '%s' !"
|
||||
"\nPlease contact your Odoo support channel.") % algorithm)
|
||||
return reference
|
||||
|
||||
@api.multi
|
||||
@api.returns('self', lambda value: value.id)
|
||||
def copy(self, default=None):
|
||||
def _get_computed_reference(self):
|
||||
self.ensure_one()
|
||||
default = default or {}
|
||||
if self.type in ['out_invoice']:
|
||||
reference_type = self.reference_type or 'none'
|
||||
default['reference_type'] = reference_type
|
||||
if reference_type == 'bba':
|
||||
default['reference'] = self.generate_bbacomm(self.type, reference_type, self.partner_id.id, '')['value']['reference']
|
||||
return super(AccountInvoice, self).copy(default)
|
||||
if self.company_id.invoice_reference_type == 'bba':
|
||||
return self.generate_bbacomm(self.partner_id)
|
||||
return super(AccountInvoice, self)._get_computed_reference()
|
||||
|
||||
@@ -6,13 +6,26 @@
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class ResCompany(models.Model):
|
||||
_inherit = "res.company"
|
||||
|
||||
@api.model
|
||||
def _get_invoice_reference_types(self):
|
||||
res = super(ResCompany, self)._get_invoice_reference_types()
|
||||
# Add BBA Structured Communication Type
|
||||
res.append(('bba', 'BBA Structured Communication'))
|
||||
return res
|
||||
|
||||
invoice_reference_type = fields.Selection(string='Default Communication Type', selection='_get_invoice_reference_types',
|
||||
default='bba', 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.')
|
||||
|
||||
class ResPartner(models.Model):
|
||||
""" add field to indicate default 'Communication Type' on customer invoices """
|
||||
_inherit = 'res.partner'
|
||||
|
||||
@api.model
|
||||
def _get_comm_type(self):
|
||||
return self.env['account.invoice']._get_reference_type()
|
||||
return self.env['res.company']._get_invoice_reference_types()
|
||||
|
||||
out_inv_comm_type = fields.Selection('_get_comm_type', string='Communication Type', change_default=True,
|
||||
help='Select Default Communication Type for Outgoing Invoices.', default='none')
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<!-- Adapt Customer Invoices to support bba structured communication -->
|
||||
<record id="customer_invoice_bbacomm_form" model="ir.ui.view">
|
||||
<field name="name">account.invoice.form.inherit</field>
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="inherit_id" ref="account.invoice_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="date_due" position="after">
|
||||
<field name="reference_type" nolabel="1" attrs="{'readonly':[('state','!=','draft')]}"/>
|
||||
<field name="reference" nolabel="1" attrs="{'readonly':[('state','!=','draft')]}"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<template id="report_invoice_document_inherit" inherit_id="account.report_invoice_document">
|
||||
<xpath expr="//div[@id='total']" position="after">
|
||||
<p t-if="o.reference_type=='bba' and o.reference">
|
||||
Please use the following communication for your payment : <b><span t-field="o.reference"/></b>
|
||||
</p>
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
||||
@@ -21,7 +21,6 @@ class ISRTest(AccountingTestCase):
|
||||
|
||||
invoice = self.env['account.invoice'].create({
|
||||
'partner_id': partner_agrolait.id,
|
||||
'reference_type': 'none',
|
||||
'currency_id': currency.id,
|
||||
'name': 'invoice to client',
|
||||
'account_id': account_receivable.id,
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
<record id="demo_invoice_1" model="account.invoice">
|
||||
<field name="partner_id" ref="base.res_partner_12"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="reference_type">none</field>
|
||||
<field name="payment_term_id" ref="account.account_payment_term"/>
|
||||
<field name="type">out_invoice</field>
|
||||
<field name="date_invoice" eval="time.strftime('%Y-%m')+'-01'"/>
|
||||
@@ -86,7 +85,6 @@
|
||||
<record id="demo_invoice_followup" model="account.invoice">
|
||||
<field name="partner_id" ref="base.res_partner_2"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="reference_type">none</field>
|
||||
<field name="payment_term_id" ref="account.account_payment_term_immediate"/>
|
||||
<field name="type">out_invoice</field>
|
||||
<field name="date_invoice" eval="(datetime.today() + timedelta(days=-15)).strftime('%Y-%m-%d')"/>
|
||||
@@ -115,7 +113,6 @@
|
||||
<record id="demo_invoice_0" model="account.invoice">
|
||||
<field name="partner_id" ref="base.res_partner_12"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="reference_type">none</field>
|
||||
<field name="payment_term_id" ref="account.account_payment_term"/>
|
||||
<field name="type">in_invoice</field>
|
||||
<field name="journal_id" search="[('type', '=', 'purchase')]"/>
|
||||
@@ -201,4 +198,4 @@
|
||||
</function>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
</odoo>
|
||||
|
||||
@@ -48,7 +48,6 @@ class TestValuationReconciliation(ValuationReconciliationTestCase):
|
||||
rslt = self.env['account.invoice'].create({
|
||||
'purchase_id': purchase_order.id,
|
||||
'partner_id': self.test_partner.id,
|
||||
'reference_type': 'none',
|
||||
'currency_id': self.currency_two.id,
|
||||
'name': 'vendor bill',
|
||||
'type': 'in_invoice',
|
||||
|
||||
@@ -49,7 +49,6 @@ class TestValuationReconciliation(ValuationReconciliationTestCase):
|
||||
def _create_invoice_for_so(self, sale_order, product):
|
||||
rslt = self.env['account.invoice'].create({
|
||||
'partner_id': self.test_partner.id,
|
||||
'reference_type': 'none',
|
||||
'currency_id': self.currency_two.id,
|
||||
'name': 'customer invoice',
|
||||
'type': 'out_invoice',
|
||||
|
||||
@@ -807,7 +807,7 @@ QUnit.module('core', function () {
|
||||
"currency_id": 1, "partner_id": 4,
|
||||
"fiscal_position_id": false,
|
||||
"date_invoice": false, "date": false,
|
||||
"payment_term_id": false, "reference_type": "none",
|
||||
"payment_term_id": false,
|
||||
"reference": false, "account_id": 440,
|
||||
"name": false, "invoice_line_ids": [],
|
||||
"tax_line_ids": [], "amount_untaxed": 0,
|
||||
@@ -843,7 +843,7 @@ QUnit.module('core', function () {
|
||||
"type": "out_invoice", "currency_id": 1,
|
||||
"partner_id": 4, "fiscal_position_id": false,
|
||||
"date_invoice": false, "date": false,
|
||||
"payment_term_id": false, "reference_type": "none",
|
||||
"payment_term_id": false,
|
||||
"reference": false, "account_id": 440, "name": false,
|
||||
"tax_line_ids": [], "amount_untaxed": 0, "amount_tax": 0,
|
||||
"reconciled": false, "amount_total": 0,
|
||||
|
||||
Reference in New Issue
Block a user