[REF] l10n_it_edi: modify edi prepare function and template
Several fields in the l10n_it_edi XML template are adapted fairly often as work progresses on l10n_it_edi/l10n_it_edi_sdicoop. This refactor moves the computation of these field to the python code in _prepare_fattura_pa_export_values. The computation is done for the: (DettaglioLinee) Invoice Lines: NumeroLinea (line number), Descrizione (description), PrezzoUnitario (unit price), PrezzoTotale (line subtotal), (DatiRiepilogo) Tax Lines: Arrotondamento (tax rounding), ImponibileImporto (tax base), Imposta (tax amount). These are computed by the new functions _l10n_it_edi_prepare_line_details and _l10n_it_edi_prepare_tax_details and returned as lists of dictionaries, which are passed to the function that renders the template. The template is modified on the above fields, to reference the above dictionaries, instead of performing the computation in situ. Moving the computation of these fields to the python code has the benefit of: a) easier, more concise computation of these terms b) not requiring users to upgrade the l10n_it_edi module in order to benefit from future changes to these fields (they will only need to upgrade once). Part-of: odoo/odoo#98534
This commit is contained in:
@@ -3,14 +3,8 @@
|
||||
<data>
|
||||
|
||||
<template id="account_invoice_line_it_FatturaPA">
|
||||
<t t-if="rc_refund">
|
||||
<t t-set="price_subtotal" t-value="-line.price_subtotal"/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<t t-set="price_subtotal" t-value="line.price_subtotal"/>
|
||||
</t>
|
||||
<DettaglioLinee>
|
||||
<NumeroLinea t-esc="line_counter"/>
|
||||
<NumeroLinea t-esc="line_dict['line_number']"/>
|
||||
<CodiceArticolo t-if="line.product_id.barcode">
|
||||
<CodiceTipo>EAN</CodiceTipo>
|
||||
<CodiceValore t-esc="format_alphanumeric(line.product_id.barcode)"/>
|
||||
@@ -19,18 +13,15 @@
|
||||
<CodiceTipo>INTERNAL</CodiceTipo>
|
||||
<CodiceValore t-esc="format_alphanumeric(line.product_id.default_code)"/>
|
||||
</CodiceArticolo>
|
||||
<Descrizione>
|
||||
<t t-esc="format_alphanumeric(line.name[:1000])"/>
|
||||
<t t-if="not line.name" t-esc="'NO NAME'"/>
|
||||
</Descrizione>
|
||||
<Quantita t-esc="format_numbers(line.quantity)"/>
|
||||
<Descrizione t-esc="format_alphanumeric(line_dict['description'])"/>
|
||||
<Quantita t-esc="format_numbers(abs(line.quantity))"/>
|
||||
<UnitaMisura t-if="line.product_uom_id.category_id != env.ref('uom.product_uom_categ_unit')" t-esc="format_alphanumeric(line.product_uom_id.name)"/>
|
||||
<PrezzoUnitario t-esc="'%.06f' % (price_subtotal / (( 1 - (line.discount or 0.0) / 100.0) * line.quantity) if line.quantity and line.discount != 100.0 else line.price_unit)"/>
|
||||
<PrezzoUnitario t-esc="'%.06f' % (line_dict['unit_price'])"/>
|
||||
<ScontoMaggiorazione t-if="line.discount != 0">
|
||||
<Tipo t-esc="discount_type(line.discount)"/>
|
||||
<Percentuale t-esc="format_numbers(abs(line.discount))"/>
|
||||
</ScontoMaggiorazione>
|
||||
<PrezzoTotale t-esc="format_monetary(price_subtotal, currency)"/>
|
||||
<PrezzoTotale t-esc="format_monetary(line_dict['subtotal_price'], currency)"/>
|
||||
<AliquotaIVA t-if="line.tax_ids.amount_type == 'percent'" t-esc="format_numbers(line.tax_ids.amount)"/>
|
||||
<AliquotaIVA t-elif="line.tax_ids.amount_type != 'percent'" t-esc="'0.00'"/>
|
||||
<Natura t-if="line.tax_ids.l10n_it_has_exoneration" t-esc="line.tax_ids.l10n_it_kind_exoneration"/>
|
||||
@@ -149,29 +140,20 @@
|
||||
</DatiDDT>
|
||||
</DatiGenerali>
|
||||
<DatiBeniServizi>
|
||||
<t t-set="line_counter" t-value="0"/>
|
||||
<t t-foreach="record.invoice_line_ids.filtered(lambda l: l.display_type not in ('line_note', 'line_section'))" t-as="line">
|
||||
<t t-set="line_counter" t-value="line_counter + 1"/>
|
||||
<t t-set="taxes" t-value="line.tax_ids.compute_all(line.price_unit)"/>
|
||||
<t t-foreach="invoice_lines" t-as="line_dict">
|
||||
<t t-set="line" t-value="line_dict['line']"/>
|
||||
<t t-call="l10n_it_edi.account_invoice_line_it_FatturaPA"/>
|
||||
</t>
|
||||
<t t-foreach="tax_details['tax_details']" t-as="tax_name">
|
||||
<t t-set="tax_dict" t-value="tax_details['tax_details'][tax_name]"/>
|
||||
<t t-set="tax" t-value="tax_dict['tax']"/>
|
||||
<t t-foreach="tax_lines" t-as="tax_line">
|
||||
<t t-set="tax" t-value="tax_line['tax']"/>
|
||||
<t t-set="has_exoneration" t-value="tax.l10n_it_has_exoneration"/>
|
||||
<t t-set="kind_exoneration" t-value="tax.l10n_it_kind_exoneration"/>
|
||||
<DatiRiepilogo>
|
||||
<AliquotaIVA t-esc="format_numbers(tax.amount)"/>
|
||||
<Natura t-if="has_exoneration" t-esc="kind_exoneration"/>
|
||||
<Arrotondamento t-if="tax_dict.get('rounding')" t-esc="format_numbers(tax_dict['rounding'])"/>
|
||||
<t t-if="rc_refund">
|
||||
<ImponibileImporto t-esc="format_monetary(tax_dict['base_amount_currency'], currency)"/>
|
||||
<Imposta t-esc="format_monetary(tax_dict['tax_amount_currency'], currency)"/>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<ImponibileImporto t-esc="format_monetary(abs(tax_dict['base_amount_currency']), currency)"/>
|
||||
<Imposta t-esc="format_monetary(abs(tax_dict['tax_amount_currency']), currency)"/>
|
||||
</t>
|
||||
<Arrotondamento t-if="tax_line['rounding']" t-esc="format_numbers(tax_line['rounding'])"/>
|
||||
<ImponibileImporto t-esc="format_monetary(tax_line['base_amount'], currency)"/>
|
||||
<Imposta t-esc="format_monetary(tax_line['tax_amount'], currency)"/>
|
||||
<EsigibilitaIVA t-if="not has_exoneration or kind_exoneration == 'N6'" t-esc="tax.l10n_it_vat_due_date"/>
|
||||
<RiferimentoNormativo t-if="has_exoneration" t-esc="format_alphanumeric(tax.l10n_it_law_reference[:100])"/>
|
||||
</DatiRiepilogo>
|
||||
|
||||
@@ -80,6 +80,60 @@ class AccountMove(models.Model):
|
||||
"""
|
||||
return len(self.commercial_partner_id.l10n_it_pa_index or '') == 6
|
||||
|
||||
def _l10n_it_edi_prepare_fatturapa_line_details(self, rc_refund=False):
|
||||
""" Returns a list of dictionaries passed to the template for the invoice lines (DettaglioLinee)
|
||||
"""
|
||||
invoice_lines = []
|
||||
lines = self.invoice_line_ids.filtered(lambda l: not l.display_type in ('line_note', 'line_section'))
|
||||
for num, line in enumerate(lines):
|
||||
# In the case of reverse charge refund, the values for the unit price and total price should be negative
|
||||
price_subtotal = line.price_subtotal if not rc_refund else -line.price_subtotal
|
||||
|
||||
# Unit price
|
||||
unit_price = 0
|
||||
if line.quantity and line.discount != 100.0:
|
||||
unit_price = price_subtotal / ((1 - (line.discount or 0.0) / 100.0) * line.quantity)
|
||||
else:
|
||||
unit_price = line.price_unit
|
||||
|
||||
line_dict = {
|
||||
'line': line,
|
||||
'line_number': num + 1,
|
||||
'description': line.name or 'NO NAME',
|
||||
'unit_price': unit_price,
|
||||
'subtotal_price': price_subtotal,
|
||||
}
|
||||
invoice_lines.append(line_dict)
|
||||
return invoice_lines
|
||||
|
||||
def _l10n_it_edi_prepare_fatturapa_tax_details(self, tax_details, rc_refund=False):
|
||||
""" Returns a list of dictionaries passed to the template for the invoice lines (DatiRiepilogo)
|
||||
"""
|
||||
# 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
|
||||
for _tax_name, tax_dict in tax_details['tax_details'].items():
|
||||
base_amount_currency = tax_dict['base_amount_currency']
|
||||
tax_amount_currency = tax_dict['tax_amount_currency']
|
||||
tax_rate = tax_dict['tax'].amount
|
||||
if tax_dict['tax'].price_include and tax_dict['tax'].amount_type == 'percent':
|
||||
expected_base_amount_currency = tax_amount_currency * 100 / tax_rate if tax_rate else False
|
||||
if expected_base_amount_currency and float_compare(base_amount_currency, expected_base_amount_currency, 2):
|
||||
tax_dict['rounding'] = base_amount_currency - (tax_amount_currency * 100 / tax_rate)
|
||||
tax_dict['base_amount_currency'] = base_amount_currency - tax_dict['rounding']
|
||||
|
||||
tax_lines = []
|
||||
for _tax_name, tax_dict in tax_details['tax_details'].items():
|
||||
tax = tax_dict['tax']
|
||||
tax_line_dict = {
|
||||
'tax': tax,
|
||||
'rounding': tax_dict.get('rounding', False),
|
||||
'base_amount': abs(tax_dict['base_amount_currency']) if not rc_refund else tax_dict['base_amount_currency'],
|
||||
'tax_amount': abs(tax_dict['tax_amount_currency']) if not rc_refund else tax_dict['tax_amount_currency'],
|
||||
}
|
||||
tax_lines.append(tax_line_dict)
|
||||
return tax_lines
|
||||
|
||||
def _prepare_fatturapa_export_values(self):
|
||||
self.ensure_one()
|
||||
|
||||
@@ -145,21 +199,9 @@ class AccountMove(models.Model):
|
||||
pdf = base64.b64encode(pdf).decode()
|
||||
pdf_name = re.sub(r'\W+', '', self.name) + '.pdf'
|
||||
|
||||
# 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(
|
||||
filter_to_apply=lambda l: l['tax_repartition_line_id'].factor_percent >= 0
|
||||
)
|
||||
for _tax_name, tax_dict in tax_details['tax_details'].items():
|
||||
base_amount = tax_dict['base_amount_currency']
|
||||
tax_amount = tax_dict['tax_amount_currency']
|
||||
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_currency'] = base_amount - tax_dict['rounding']
|
||||
|
||||
company = self.company_id
|
||||
partner = self.commercial_partner_id
|
||||
@@ -182,6 +224,9 @@ class AccountMove(models.Model):
|
||||
if rc_refund:
|
||||
document_total = -abs(document_total)
|
||||
|
||||
invoice_lines = self._l10n_it_edi_prepare_fatturapa_line_details(rc_refund)
|
||||
tax_lines = self._l10n_it_edi_prepare_fatturapa_tax_details(tax_details, rc_refund)
|
||||
|
||||
# Create file content.
|
||||
template_values = {
|
||||
'record': self,
|
||||
@@ -219,6 +264,8 @@ class AccountMove(models.Model):
|
||||
'get_vat_country': get_vat_country,
|
||||
'in_eu': in_eu,
|
||||
'rc_refund': rc_refund,
|
||||
'invoice_lines': invoice_lines,
|
||||
'tax_lines': tax_lines,
|
||||
}
|
||||
return template_values
|
||||
|
||||
|
||||
Reference in New Issue
Block a user