diff --git a/addons/l10n_es_edi_tbai/data/template_invoice.xml b/addons/l10n_es_edi_tbai/data/template_invoice.xml index 4067f490c40..edfeeadb419 100644 --- a/addons/l10n_es_edi_tbai/data/template_invoice.xml +++ b/addons/l10n_es_edi_tbai/data/template_invoice.xml @@ -119,7 +119,7 @@ - + diff --git a/addons/l10n_es_edi_tbai/models/account_edi_format.py b/addons/l10n_es_edi_tbai/models/account_edi_format.py index 32d4d2c67cd..ef1ddbae073 100644 --- a/addons/l10n_es_edi_tbai/models/account_edi_format.py +++ b/addons/l10n_es_edi_tbai/models/account_edi_format.py @@ -339,11 +339,12 @@ class AccountEdiFormat(models.Model): else: balance_before_discount = line.balance / (1 - line.discount / 100) discount = (balance_before_discount - line.balance) + line_price_total = self._l10n_es_tbai_get_invoice_line_price_total(line) if not any([t.l10n_es_type == 'sujeto_isp' for t in line.tax_ids]): - total = line.price_total * abs(line.balance / line.amount_currency if line.amount_currency != 0 else 1) * -refund_sign + total = line_price_total * abs(line.balance / line.amount_currency if line.amount_currency != 0 else 1) * -refund_sign else: - total = abs(line.balance) * -refund_sign * (-1 if line.price_total < 0 else 1) + total = abs(line.balance) * -refund_sign * (-1 if line_price_total < 0 else 1) invoice_lines.append({ 'line': line, 'discount': discount * refund_sign, @@ -353,9 +354,10 @@ class AccountEdiFormat(models.Model): }) values['invoice_lines'] = invoice_lines # Tax details (desglose) - importe_total, desglose = self._l10n_es_tbai_get_importe_desglose(invoice) + importe_total, desglose, amount_retention = self._l10n_es_tbai_get_importe_desglose(invoice) values['amount_total'] = importe_total values['invoice_info'] = desglose + values['amount_retention'] = amount_retention * refund_sign if amount_retention != 0.0 else 0.0 # Regime codes (ClaveRegimenEspecialOTrascendencia) # NOTE there's 11 more codes to implement, also there can be up to 3 in total @@ -368,18 +370,35 @@ class AccountEdiFormat(models.Model): return values + def _l10n_es_tbai_get_invoice_line_price_total(self, invoice_line): + price_total = invoice_line.price_total + retention_tax_lines = invoice_line.tax_ids.filtered(lambda t: t.l10n_es_type == "retencion") + if retention_tax_lines: + line_discount_price_unit = invoice_line.price_unit * (1 - (invoice_line.discount / 100.0)) + tax_lines_no_retention = invoice_line.tax_ids - retention_tax_lines + if tax_lines_no_retention: + taxes_res = tax_lines_no_retention.compute_all(line_discount_price_unit, + quantity=invoice_line.quantity, + currency=invoice_line.currency_id, + product=invoice_line.product_id, + partner=invoice_line.move_id.partner_id, + is_refund=invoice_line.is_refund) + price_total = taxes_res['total_included'] + return price_total + def _l10n_es_tbai_get_importe_desglose(self, invoice): com_partner = invoice.commercial_partner_id sign = -1 if invoice.move_type in ('out_refund', 'in_refund') else 1 if com_partner.country_id.code in ('ES', False) and not (com_partner.vat or '').startswith("ESN"): tax_details_info_vals = self._l10n_es_edi_get_invoices_tax_details_info(invoice) + tax_amount_retention = tax_details_info_vals['tax_amount_retention'] desglose = {'DesgloseFactura': tax_details_info_vals['tax_details_info']} desglose['DesgloseFactura'].update({'S1': tax_details_info_vals['S1_list'], 'S2': tax_details_info_vals['S2_list']}) importe_total = round(sign * ( tax_details_info_vals['tax_details']['base_amount'] + tax_details_info_vals['tax_details']['tax_amount'] - - tax_details_info_vals['tax_amount_retention'] + - tax_amount_retention ), 2) else: tax_details_info_service_vals = self._l10n_es_edi_get_invoices_tax_details_info( @@ -390,6 +409,8 @@ class AccountEdiFormat(models.Model): invoice, filter_invl_to_apply=lambda x: any(t.tax_scope == 'consu' for t in x.tax_ids) ) + service_retention = tax_details_info_service_vals['tax_amount_retention'] + consu_retention = tax_details_info_consu_vals['tax_amount_retention'] desglose = {} if tax_details_info_service_vals['tax_details_info']: desglose.setdefault('DesgloseTipoOperacion', {}) @@ -407,12 +428,13 @@ class AccountEdiFormat(models.Model): importe_total = round(sign * ( tax_details_info_service_vals['tax_details']['base_amount'] + tax_details_info_service_vals['tax_details']['tax_amount'] - - tax_details_info_service_vals['tax_amount_retention'] + - service_retention + tax_details_info_consu_vals['tax_details']['base_amount'] + tax_details_info_consu_vals['tax_details']['tax_amount'] - - tax_details_info_consu_vals['tax_amount_retention'] + - consu_retention ), 2) - return importe_total, desglose + tax_amount_retention = service_retention + consu_retention + return importe_total, desglose, tax_amount_retention def _l10n_es_tbai_get_trail_values(self, invoice, cancel): prev_invoice = invoice.company_id._get_l10n_es_tbai_last_posted_invoice(invoice) diff --git a/addons/l10n_es_edi_tbai/tests/test_edi_xml.py b/addons/l10n_es_edi_tbai/tests/test_edi_xml.py index 8050a1a10a5..3930c8cd620 100644 --- a/addons/l10n_es_edi_tbai/tests/test_edi_xml.py +++ b/addons/l10n_es_edi_tbai/tests/test_edi_xml.py @@ -118,6 +118,20 @@ class TestEdiTbaiXmls(TestEsEdiTbaiCommon): xml_expected = self.with_applied_xpath(xml_expected_base, xpath) self.assertXmlTreeEqual(xml_doc, xml_expected) + def test_xml_tree_post_retention(self): + self.out_invoice.invoice_line_ids.tax_ids = [(4, self._get_tax_by_xml_id('s_irpf15').id)] + with freeze_time(self.frozen_today): + xml_doc = self.edi_format._get_l10n_es_tbai_invoice_xml(self.out_invoice, cancel=False)[self.out_invoice]['xml_file'] + xml_doc.remove(xml_doc.find("Signature", namespaces=NS_MAP)) + xml_expected_base = etree.fromstring(super().L10N_ES_TBAI_SAMPLE_XML_POST) + xpath = """ + + 600.00 + + """ + xml_expected = self.with_applied_xpath(xml_expected_base, xpath) + self.assertXmlTreeEqual(xml_doc, xml_expected) + def test_xml_tree_in_post(self): """Test XML of vendor bill for LROE Batuz""" with freeze_time(self.frozen_today): diff --git a/doc/cla/individual/uolaizola.md b/doc/cla/individual/uolaizola.md new file mode 100644 index 00000000000..faf19b2779d --- /dev/null +++ b/doc/cla/individual/uolaizola.md @@ -0,0 +1,11 @@ +Spain, 2024-02-01 + +I hereby agree to the terms of the Odoo Individual Contributor License +Agreement v1.0. + +I declare that I am authorized and able to make this agreement and sign this +declaration. + +Signed, + +Ugaitz Olaizola Arbelaitz uolaizola@binovo.es https://github.com/uolaizola