Check the invoice amounts against the source amounts
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
* Check the invoice amounts against the source amounts
|
||||
* Add support for Python 3.14
|
||||
* Remove support for Python 3.9
|
||||
* Add payment means to invoices
|
||||
|
||||
@@ -310,16 +310,31 @@ class Invoice(
|
||||
origin_invoices = fields.Function(fields.Many2Many(
|
||||
'account.invoice', None, None, "Origin Invoices"),
|
||||
'get_origin_invoices', searcher='search_origin_invoices')
|
||||
source_untaxed_amount = Monetary(
|
||||
"Source Untaxed", digits='currency', readonly=True,
|
||||
states={
|
||||
'invisible': Eval('source_untaxed_amount', None) == Null,
|
||||
})
|
||||
untaxed_amount = fields.Function(Monetary(
|
||||
"Untaxed", currency='currency', digits='currency'),
|
||||
'get_amount', searcher='search_untaxed_amount')
|
||||
untaxed_amount_cache = fields.Numeric(
|
||||
"Untaxed Cache", digits='currency', readonly=True)
|
||||
source_tax_amount = Monetary(
|
||||
"Source Tax", digits='currency', readonly=True,
|
||||
states={
|
||||
'invisible': Eval('source_tax_amount', None) == Null,
|
||||
})
|
||||
tax_amount = fields.Function(Monetary(
|
||||
"Tax", currency='currency', digits='currency'),
|
||||
'get_amount', searcher='search_tax_amount')
|
||||
tax_amount_cache = fields.Numeric(
|
||||
"Tax Cache", digits='currency', readonly=True)
|
||||
source_total_amount = Monetary(
|
||||
"Source Total", digits='currency', readonly=True,
|
||||
states={
|
||||
'invisible': Eval('source_total_amount', None) == Null,
|
||||
})
|
||||
total_amount = fields.Function(Monetary(
|
||||
"Total", currency='currency', digits='currency'),
|
||||
'get_amount', searcher='search_total_amount')
|
||||
@@ -1719,8 +1734,30 @@ class Invoice(
|
||||
@classmethod
|
||||
def validate_fields(cls, invoices, field_names):
|
||||
super().validate_fields(invoices, field_names)
|
||||
cls.check_source(invoices, field_names)
|
||||
cls.check_supplier_payment_reference(invoices, field_names)
|
||||
|
||||
@classmethod
|
||||
def check_source(cls, invoices, field_names):
|
||||
pool = Pool()
|
||||
Lang = pool.get('ir.lang')
|
||||
if field_names and 'state' not in field_names:
|
||||
return
|
||||
for invoice in invoices:
|
||||
if invoice.state not in {'draft', 'cancelled'}:
|
||||
for field in ['untaxed_amount', 'tax_amount', 'total_amount']:
|
||||
source = getattr(invoice, f'source_{field}')
|
||||
value = getattr(invoice, field)
|
||||
if source not in {None, value}:
|
||||
lang = Lang.get()
|
||||
raise InvoiceValidationError(
|
||||
gettext('account_invoice'
|
||||
'.msg_invoice_source_mismatch',
|
||||
invoice=invoice.rec_name,
|
||||
source=lang.currency(source, invoice.currency),
|
||||
value=lang.currency(value, invoice.currency),
|
||||
**cls.__names__(field=field)))
|
||||
|
||||
@classmethod
|
||||
def check_supplier_payment_reference(cls, invoices, field_names):
|
||||
if field_names and not (
|
||||
|
||||
@@ -42,6 +42,9 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<record model="ir.message" id="msg_invoice_payment_lines_greater_amount">
|
||||
<field name="text">Payment lines amount on invoice "%(invoice)s" can not be greater than the invoice amount.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_source_mismatch">
|
||||
<field name="text">The "%(field)s" value of the invoice "%(invoice)s", %(value)s, must equal the source value, %(source)s.</field>
|
||||
</record>
|
||||
<record model="ir.message" id="msg_invoice_supplier_payment_reference_invalid">
|
||||
<field name="text">The %(type)s "%(reference)s" on invoice "%(invoice)s" is not valid.</field>
|
||||
</record>
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
=============================
|
||||
Invoice Check Source Scenario
|
||||
=============================
|
||||
|
||||
Imports::
|
||||
|
||||
>>> import datetime as dt
|
||||
>>> from decimal import Decimal
|
||||
|
||||
>>> from proteus import Model
|
||||
>>> from trytond.modules.account.tests.tools import (
|
||||
... create_chart, create_fiscalyear, create_tax, get_accounts)
|
||||
>>> from trytond.modules.account_invoice.tests.tools import (
|
||||
... set_fiscalyear_invoice_sequences)
|
||||
>>> from trytond.modules.company.tests.tools import create_company
|
||||
>>> from trytond.tests.tools import activate_modules
|
||||
|
||||
>>> today = dt.date.today()
|
||||
|
||||
Activate modules::
|
||||
|
||||
>>> config = activate_modules('account_invoice', create_company, create_chart)
|
||||
|
||||
>>> Invoice = Model.get('account.invoice')
|
||||
>>> Party = Model.get('party.party')
|
||||
|
||||
Create fiscal year::
|
||||
|
||||
>>> fiscalyear = set_fiscalyear_invoice_sequences(create_fiscalyear())
|
||||
>>> fiscalyear.click('create_period')
|
||||
|
||||
Get accounts::
|
||||
|
||||
>>> accounts = get_accounts()
|
||||
|
||||
Create tax::
|
||||
|
||||
>>> tax = create_tax(Decimal('.10'))
|
||||
>>> tax.save()
|
||||
|
||||
Create party::
|
||||
|
||||
>>> supplier = Party(name="Supplier")
|
||||
>>> supplier.save()
|
||||
|
||||
Create invoice::
|
||||
|
||||
>>> invoice = Invoice(type='in', party=supplier)
|
||||
>>> invoice.invoice_date = today
|
||||
>>> line = invoice.lines.new()
|
||||
>>> line.quantity = 1
|
||||
>>> line.unit_price = Decimal(10)
|
||||
>>> line.account = accounts['expense']
|
||||
>>> line.taxes.append(tax)
|
||||
>>> invoice.save()
|
||||
|
||||
>>> Invoice.write([invoice.id], {
|
||||
... 'source_untaxed_amount': Decimal('100.00'),
|
||||
... 'source_tax_amount': Decimal('10.00'),
|
||||
... 'source_total_amount': Decimal('110.00'),
|
||||
... }, invoice._context)
|
||||
|
||||
Try to validate::
|
||||
|
||||
>>> invoice.click('validate_invoice')
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
InvoiceValidationError: ...
|
||||
|
||||
Correct quantity::
|
||||
|
||||
>>> line, = invoice.lines
|
||||
>>> line.quantity = 10
|
||||
>>> invoice.click('validate_invoice')
|
||||
>>> invoice.state
|
||||
'validated'
|
||||
@@ -38,13 +38,21 @@ this repository contains the full copyright notices and license terms. -->
|
||||
<label name="state"/>
|
||||
<field name="state"/>
|
||||
</group>
|
||||
<group col="2" colspan="2" id="amount" yfill="1" yalign="1">
|
||||
<group col="4" colspan="2" id="amount" yfill="1" yalign="1">
|
||||
<label name="untaxed_amount" xalign="1.0" xexpand="1" xfill="0"/>
|
||||
<field name="untaxed_amount" xalign="1.0" xexpand="0"/>
|
||||
<label name="source_untaxed_amount" string="expected" xalign="1.0" xexpand="1" xfill="0"/>
|
||||
<field name="source_untaxed_amount" xalign="1.0" xexpand="0"/>
|
||||
|
||||
<label name="tax_amount" xalign="1.0" xexpand="1" xfill="0"/>
|
||||
<field name="tax_amount" xalign="1.0" xexpand="0"/>
|
||||
<label name="source_tax_amount" string="expected" xalign="1.0" xexpand="1" xfill="0"/>
|
||||
<field name="source_tax_amount" xalign="1.0" xexpand="0"/>
|
||||
|
||||
<label name="total_amount" xalign="1.0" xexpand="1" xfill="0"/>
|
||||
<field name="total_amount" xalign="1.0" xexpand="0"/>
|
||||
<label name="source_total_amount" string="expected" xalign="1.0" xexpand="1" xfill="0"/>
|
||||
<field name="source_total_amount" xalign="1.0" xexpand="0"/>
|
||||
</group>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
Reference in New Issue
Block a user