Raise a warning when posting supplier invoice with same reference
issue8244 review265201002
This commit is contained in:
1 parent
be7c00ff70
commit
33e7892bca
5 files changed
+87
-2
No files matched your search
@@ -1,3 +1,4 @@
|
||||
* Raise a warning when validating supplier invoice with same reference
|
||||
* Add support for Python 3.10
|
||||
* Remove support for Python 3.6
|
||||
|
||||
|
||||
@@ -39,3 +39,7 @@ class InvoicePaymentTermDateWarning(UserWarning):
|
||||
|
||||
class InvoiceFutureWarning(UserWarning):
|
||||
pass
|
||||
|
||||
|
||||
class InvoiceSimilarWarning(UserWarning):
|
||||
pass
|
||||
+57
-2
@@ -1,5 +1,6 @@
|
||||
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
||||
# this repository contains the full copyright notices and license terms.
|
||||
import datetime as dt
|
||||
from collections import defaultdict, namedtuple
|
||||
from decimal import Decimal
|
||||
from itertools import chain, combinations, groupby
|
||||
@@ -30,8 +31,8 @@ from trytond.wizard import (
|
||||
|
||||
from .exceptions import (
|
||||
InvoiceFutureWarning, InvoiceLineValidationError, InvoiceNumberError,
|
||||
InvoicePaymentTermDateWarning, InvoiceTaxValidationError,
|
||||
InvoiceValidationError, PayInvoiceError)
|
||||
InvoicePaymentTermDateWarning, InvoiceSimilarWarning,
|
||||
InvoiceTaxValidationError, InvoiceValidationError, PayInvoiceError)
|
||||
|
||||
if config.getboolean('account_invoice', 'filestore', default=False):
|
||||
file_id = 'invoice_report_cache_id'
|
||||
@@ -1524,6 +1525,7 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin):
|
||||
|
||||
invoices_in = cls.browse([i for i in invoices if i.type == 'in'])
|
||||
cls.set_number(invoices_in)
|
||||
cls._check_similar(invoices)
|
||||
moves = []
|
||||
for invoice in invoices_in:
|
||||
move = invoice.get_move()
|
||||
@@ -1576,6 +1578,7 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin):
|
||||
raise InvoiceFutureWarning(warning_key,
|
||||
gettext('account_invoice.msg_invoice_date_future',
|
||||
invoices=names))
|
||||
cls._check_similar([i for i in invoices if i.state != 'validated'])
|
||||
cls._post(invoices)
|
||||
|
||||
@classmethod
|
||||
@@ -1605,6 +1608,58 @@ class Invoice(Workflow, ModelSQL, ModelView, TaxableMixin):
|
||||
if reconciled:
|
||||
cls.__queue__.process(reconciled)
|
||||
|
||||
@classmethod
|
||||
def _check_similar(cls, invoices, type='in'):
|
||||
pool = Pool()
|
||||
Warning = pool.get('res.user.warning')
|
||||
for sub_invoices in grouped_slice(invoices):
|
||||
sub_invoices = list(sub_invoices)
|
||||
domain = list(filter(None,
|
||||
(i._similar_domain() for i in sub_invoices
|
||||
if i.type == type)))
|
||||
if not domain:
|
||||
continue
|
||||
if cls.search(['OR'] + domain, order=[]):
|
||||
for invoice in sub_invoices:
|
||||
domain = invoice._similar_domain()
|
||||
if not domain:
|
||||
continue
|
||||
try:
|
||||
similar, = cls.search(domain, limit=1)
|
||||
except ValueError:
|
||||
continue
|
||||
warning_key = Warning.format(
|
||||
'invoice_similar', [invoice])
|
||||
if Warning.check(warning_key):
|
||||
raise InvoiceSimilarWarning(warning_key,
|
||||
gettext('account_invoice.msg_invoice_similar',
|
||||
similar=similar.rec_name,
|
||||
invoice=invoice.rec_name))
|
||||
|
||||
def _similar_domain(self, delay=None):
|
||||
pool = Pool()
|
||||
Date = pool.get('ir.date')
|
||||
if not self.reference:
|
||||
return
|
||||
with Transaction().set_context(company=self.company.id):
|
||||
invoice_date = self.invoice_date or Date.today()
|
||||
if delay is None:
|
||||
delay = dt.timedelta(days=60)
|
||||
return [
|
||||
('company', '=', self.company.id),
|
||||
('type', '=', self.type),
|
||||
('party', '=', self.party.id),
|
||||
('reference', '=', self.reference),
|
||||
('id', '!=', self.id),
|
||||
['OR',
|
||||
('invoice_date', '=', None),
|
||||
[
|
||||
('invoice_date', '>=', invoice_date - delay),
|
||||
('invoice_date', '<=', invoice_date + delay),
|
||||
],
|
||||
],
|
||||
]
|
||||
|
||||
@classmethod
|
||||
@ModelView.button_action('account_invoice.wizard_pay')
|
||||
def pay(cls, invoices):
|
||||
|
||||
@@ -81,6 +81,9 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<record model="ir.message" id="msg_invoice_date_future">
|
||||
<field name="text">The invoices "%(invoices)s" have an invoice date in the future.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_similar">
|
||||
<field name="text">The invoice "%(invoice)s" is similar to invoice "%(similar)s".</field>
|
||||
</record>
|
||||
</data>
|
||||
</tryton>
|
||||
|
||||
@@ -3,6 +3,7 @@ Invoice Supplier Scenario
|
||||
=========================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime
|
||||
>>> from dateutil.relativedelta import relativedelta
|
||||
>>> from decimal import Decimal
|
||||
@@ -102,6 +103,7 @@ Create invoice::
|
||||
>>> invoice.party = party
|
||||
>>> invoice.payment_term = payment_term
|
||||
>>> invoice.invoice_date = today
|
||||
>>> invoice.reference = 'FAC001'
|
||||
>>> line = InvoiceLine()
|
||||
>>> invoice.lines.append(line)
|
||||
>>> line.product = product
|
||||
@@ -192,6 +194,26 @@ Credit invoice::
|
||||
>>> credit_note.total_amount == -invoice.total_amount
|
||||
True
|
||||
|
||||
A warning is raised when creating an invoice with same reference::
|
||||
|
||||
>>> invoice = Invoice()
|
||||
>>> invoice.type = 'in'
|
||||
>>> invoice.party = party
|
||||
>>> invoice.invoice_date = today
|
||||
>>> invoice.reference = 'FAC001'
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.product = product
|
||||
>>> line.quantity = 1
|
||||
>>> line.unit_price = Decimal('20')
|
||||
>>> invoice.click('post') # doctest: +IGNORE_EXCEPTION_DETAIL
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
InvoiceSimilarWarning: ...
|
||||
>>> invoice.reference = 'FAC002'
|
||||
>>> invoice.click('post')
|
||||
>>> invoice.state
|
||||
'posted'
|
||||
|
||||
Create a posted and a draft invoice to cancel::
|
||||
|
||||
>>> invoice = Invoice()
|
||||
|
||||
Reference in new issue
Block a user