[FIX] l10n_it_edi: Tax calculation fails edi constraint if tax price included

When using price included taxes, the xml contained data that failed to
meet the constraints of the edi. This is due to the local rounding on
the lines of the invoice.
For example:

	A product costing 321€, on two lines of the invoice, with
	a price included tax of 22%
	Rounded per line:
	float_round(
		321 - (321/122 * 100),
		2 # To two decimal places
	)
	evaluates to 57.89, the total tax will be 2 * 57.89 = 115.78

	In the case of global rounding
	float_round(
		321 + 321 - (321/122*100) - (321/122*100),
		2 # To two decimal places
	)
	evaluates to 115.77,

	so we have a difference of one cent.

This can be exacerbated by more lines.

The constraints on the EDI that this conflicts with are on the tax
summary section for each tax.
The constraints (roughly reworded):
00422:  The base taxable amount for the tax must be equal to the sum of
	the base product prices (for which we have already used the
	rounded computed values, calcuated in the invoice) +
	<Arrotondamento> (rounding).

00421:  The value provided for the <importo> that is the value of the
	vat is equal to the taxable base multiplied by the tax rate.

The problem is that because of our local roundings, the taxable base
is equal to our products, but the tax rate * taxable base is not equal
to the tax amount (as present in the invoice).

This commit adds to the rounding field and subtracts from the
taxable base of the tax summary a value rounding value that
should make tax rate * taxable base equal to the value of the vat.

closes odoo/odoo#86711

Task: 2789290
X-original-commit: def954e85e83e5be18c0a1805f0af20f8116bf3d
Signed-off-by: Josse Colpaert <jco@odoo.com>
This commit is contained in:
dbkosky
2022-03-20 20:29:23 +01:00
committed by dbkosky
parent 00d356d54d
commit d40358639f
2 changed files with 25 additions and 19 deletions
@@ -162,26 +162,17 @@
<t t-set="line_counter" t-value="line_counter + 1"/>
<t t-call="l10n_it_edi.account_invoice_line_it_FatturaPA"/>
</t>
<t t-foreach="record.line_ids.filtered(lambda line: line.tax_line_id)" t-as="tax_line">
<t t-foreach="tax_details['tax_details']" t-as="tax_name">
<t t-set="tax_dict" t-value="tax_details['tax_details'][tax_name]"/>
<DatiRiepilogo>
<!--2.2.2-->
<AliquotaIVA t-esc="format_numbers(tax_line.tax_line_id.amount)"/>
<Natura t-if="tax_line.tax_line_id.l10n_it_has_exoneration" t-esc="tax_line.tax_line_id.l10n_it_kind_exoneration"/>
<ImponibileImporto t-esc="format_monetary(tax_line.tax_base_amount, currency)"/>
<Imposta t-esc="format_monetary(tax_line.price_unit, currency)"/>
<EsigibilitaIVA t-if="not tax_line.tax_line_id.l10n_it_has_exoneration or tax_line.tax_line_id.l10n_it_kind_exoneration=='N6'" t-esc="tax_line.tax_line_id.l10n_it_vat_due_date"/>
<RiferimentoNormativo t-if="tax_line.tax_line_id.l10n_it_has_exoneration" t-esc="tax_line.tax_line_id.l10n_it_law_reference"/>
</DatiRiepilogo>
</t>
<!-- 0% tax lines -->
<t t-foreach="tax_map" t-as="tax">
<DatiRiepilogo>
<AliquotaIVA t-esc="format_numbers(tax.amount)"/>
<Natura t-if="tax.l10n_it_has_exoneration" t-esc="tax.l10n_it_kind_exoneration"/>
<ImponibileImporto t-esc="format_monetary(tax_map[tax], currency)"/>
<Imposta t-esc="format_monetary(0.00, currency)"/>
<EsigibilitaIVA t-if="not tax.l10n_it_has_exoneration or tax.l10n_it_kind_exoneration=='N6'" t-esc="tax.l10n_it_vat_due_date"/>
<RiferimentoNormativo t-if="tax.l10n_it_has_exoneration" t-esc="tax.l10n_it_law_reference"/>
<AliquotaIVA t-esc="format_numbers(tax_dict['tax'].amount)"/>
<Natura t-if="tax_dict['tax'].l10n_it_has_exoneration" t-esc="tax_dict['tax'].l10n_it_kind_exoneration"/>
<Arrotondamento t-if="tax_dict.get('rounding')" t-esc="format_numbers(tax_dict['rounding'])"/>
<ImponibileImporto t-esc="format_monetary(abs(tax_dict['base_amount']), currency)"/>
<Imposta t-esc="format_monetary(abs(tax_dict['tax_amount']), currency)"/>
<EsigibilitaIVA t-if="not tax_dict['tax'].l10n_it_has_exoneration or tax_dict['tax'].l10n_it_kind_exoneration=='N6'" t-esc="tax_dict['tax'].l10n_it_vat_due_date"/>
<RiferimentoNormativo t-if="tax_dict['tax'].l10n_it_has_exoneration" t-esc="tax_dict['tax'].l10n_it_law_reference"/>
</DatiRiepilogo>
</t>
</DatiBeniServizi>
+16 -1
View File
@@ -11,7 +11,7 @@ from datetime import date, datetime
from lxml import etree
from odoo import api, fields, models, _
from odoo.tools import float_repr
from odoo.tools import float_repr, float_compare
from odoo.exceptions import UserError, ValidationError
from odoo.addons.base.models.ir_mail_server import MailDeliveryException
from odoo.tests.common import Form
@@ -164,6 +164,20 @@ class AccountMove(models.Model):
if tax.amount == 0.0:
tax_map[tax] = tax_map.get(tax, 0.0) + line.price_subtotal
# Constraints within the edi make local rounding on price included taxes a problem.
# To solve this there is a <Arrotondamento> or 'rounding' field, such that:
# taxable base = sum(taxable base for each unit) + Arrotondamento
tax_details = self._prepare_edi_tax_details()
for _tax_name, tax_dict in tax_details['tax_details'].items():
base_amount = tax_dict['base_amount']
tax_amount = tax_dict['tax_amount']
tax_rate = tax_dict['tax'].amount
if tax_dict['tax'].price_include and tax_dict['tax'].amount_type == 'percent':
expected_base_amount = tax_amount * 100 / tax_rate if tax_rate else False
if expected_base_amount and float_compare(base_amount, expected_base_amount, 2):
tax_dict['rounding'] = base_amount - (tax_amount * 100 / tax_rate)
tax_dict['base_amount'] = base_amount - tax_dict['rounding']
# Create file content.
template_values = {
'record': self,
@@ -182,6 +196,7 @@ class AccountMove(models.Model):
'pdf': pdf,
'pdf_name': pdf_name,
'tax_map': tax_map,
'tax_details': tax_details,
}
return template_values