# -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. from collections import namedtuple from lxml import etree from odoo import fields from odoo.tests import tagged from odoo.addons.l10n_it_edi.tests.common import TestItEdi @tagged('post_install_l10n', 'post_install', '-at_install') class TestItEdiReverseCharge(TestItEdi): @classmethod def setUpClass(cls): super().setUpClass() # Helper functions ----------- def get_tag_ids(tag_codes): """ Helper function to define tag ids for taxes """ return cls.env['account.account.tag'].search([ ('applicability', '=', 'taxes'), ('country_id.code', '=', 'IT'), ('name', 'in', tag_codes)]).ids RepartitionLine = namedtuple('Line', 'factor_percent repartition_type tag_ids') def repartition_lines(*lines): """ Helper function to define repartition lines in taxes """ return [(5, 0, 0)] + [(0, 0, {**line._asdict(), 'tag_ids': get_tag_ids(line[2])}) for line in lines] ProductLine = namedtuple('Line', 'data name product_id') def product_lines(*lines): """ Helper function to define move lines based on a product """ return [(0, 0, {**line[0], 'name': line[1], 'product_id': line[2]}) for line in lines] # Company ----------- cls.company.partner_id.l10n_it_pa_index = "0803HR0" # Partner ----------- cls.french_partner = cls.env['res.partner'].create({ 'name': 'Alessi', 'vat': 'FR15437982937', 'country_id': cls.env.ref('base.fr').id, 'street': 'Avenue Test rue', 'zip': '84000', 'city': 'Avignon', 'is_company': True }) cls.san_marino_partner = cls.env['res.partner'].create({ 'name': 'Prospectra', 'vat': 'SM6784', 'country_id': cls.env.ref('base.sm').id, 'street': 'Via Ventotto Luglio 212 Centro Uffici', 'zip': '47893', 'city': 'San Marino', 'company_id': cls.company.id, 'is_company': True, }) # Taxes ----------- tax_data = { 'name': 'Tax 4% (Goods) Reverse Charge', 'amount': 4.0, 'amount_type': 'percent', 'type_tax_use': 'purchase', 'invoice_repartition_line_ids': repartition_lines( RepartitionLine(100, 'base', ('+03', '+vj9')), RepartitionLine(100, 'tax', ('+5v',)), RepartitionLine(-100, 'tax', ('-4v',))), 'refund_repartition_line_ids': repartition_lines( RepartitionLine(100, 'base', ('-03', '-vj9')), RepartitionLine(100, 'tax', False), RepartitionLine(-100, 'tax', False)), } # Purchase tax 4% with Reverse Charge cls.purchase_tax_4p = cls.env['account.tax'].with_company(cls.company).create(tax_data) cls.line_tax_4p = cls.standard_line.copy() cls.line_tax_4p['tax_ids'] = [(6, 0, cls.purchase_tax_4p.ids)] # Purchase tax 4% with Reverse Charge, targeting the tax grid for import of goods # already in Italy in a VAT deposit tax_data_4p_already_in_italy = { **tax_data, 'name': 'Tax 4% purchase Reverse Charge, in Italy', 'invoice_repartition_line_ids': repartition_lines( RepartitionLine(100, 'base', ('+03', '+vj3')), RepartitionLine(100, 'tax', ('+5v',)), RepartitionLine(-100, 'tax', ('-4v',))), 'refund_repartition_line_ids': repartition_lines( RepartitionLine(100, 'base', ('-03', '-vj3')), RepartitionLine(100, 'tax', False), RepartitionLine(-100, 'tax', False)), } cls.purchase_tax_4p_already_in_italy = cls.env['account.tax'].with_company(cls.company).create(tax_data_4p_already_in_italy) cls.line_tax_4p_already_in_italy = cls.standard_line.copy() cls.line_tax_4p_already_in_italy['tax_ids'] = [(6, 0, cls.purchase_tax_4p_already_in_italy.ids)] # Purchase tax 22% with Reverse Charge tax_data_22p = {**tax_data, 'name': 'Tax 22% purchase Reverse Charge', 'amount': 22.0} cls.purchase_tax_22p = cls.env['account.tax'].with_company(cls.company).create(tax_data_22p) cls.line_tax_22p = cls.standard_line.copy() cls.line_tax_22p['tax_ids'] = [(6, 0, cls.purchase_tax_22p.ids)] # Export tax 0% tax_data_0v = {**tax_data, "type_tax_use": "sale", "amount": 0} cls.sale_tax_0v = cls.env['account.tax'].with_company(cls.company).create(tax_data_0v) cls.line_tax_sale = cls.standard_line.copy() cls.line_tax_sale['tax_ids'] = [(6, 0, cls.sale_tax_0v.ids)] # Products ----------- # Product A with 0% sale export and tax 4% reverse carge purchase tax product_a = cls.env['product.product'].with_company(cls.company).create({ 'name': 'product_a', 'lst_price': 1000.0, 'standard_price': 800.0, 'type': 'consu', 'taxes_id': [(6, 0, cls.sale_tax_0v.ids)], 'supplier_taxes_id': [(6, 0, cls.purchase_tax_4p.ids)], }) # Product B with 0% sale export and tax 4% reverse charge purchase tax product_b = cls.env['product.product'].with_company(cls.company).create({ 'name': 'product_b', 'lst_price': 1000.0, 'standard_price': 800.0, 'type': 'consu', 'taxes_id': [(6, 0, cls.sale_tax_0v.ids)], 'supplier_taxes_id': [(6, 0, cls.purchase_tax_4p.ids)], }) # Moves ----------- # Export invoice cls.reverse_charge_invoice = cls.env['account.move'].with_company(cls.company).create({ 'company_id': cls.company.id, 'move_type': 'out_invoice', 'invoice_date': fields.Date.from_string('2022-03-24'), 'invoice_date_due': fields.Date.from_string('2022-03-24'), 'partner_id': cls.french_partner.id, 'partner_bank_id': cls.test_bank.id, 'invoice_line_ids': product_lines( ProductLine(cls.line_tax_sale, 'Product A', product_a.id), ProductLine(cls.line_tax_sale, 'Product B', product_b.id) ), }) # Import bill #1 bill_data = { 'company_id': cls.company.id, 'move_type': 'in_invoice', 'invoice_date': fields.Date.from_string('2022-03-24'), 'invoice_date_due': fields.Date.from_string('2022-03-24'), 'partner_id': cls.french_partner.id, 'partner_bank_id': cls.test_bank.id, 'invoice_line_ids': product_lines( ProductLine(cls.line_tax_22p, 'Product A', product_a.id), ProductLine(cls.line_tax_4p, 'Product B, taxed 4%', product_b.id) ) } cls.reverse_charge_bill = cls.env['account.move'].with_company(cls.company).create(bill_data) # Import bill #2 bill_data_2 = { **bill_data, 'invoice_line_ids': product_lines( ProductLine(cls.line_tax_22p, 'Product A', product_a.id), ProductLine(cls.line_tax_4p_already_in_italy, 'Product B, taxed 4% Already in Italy', product_b.id), ), } cls.reverse_charge_bill_2 = cls.env['account.move'].with_company(cls.company).create(bill_data_2) cls.reverse_charge_refund = cls.reverse_charge_bill.with_company(cls.company)._reverse_moves([{ 'invoice_date': fields.Date.from_string('2022-03-24'), }]) # Import bill San Marino bill_data_san_marino = { 'company_id': cls.company.id, 'move_type': 'in_invoice', 'invoice_date': fields.Date.from_string('2022-03-24'), 'invoice_date_due': fields.Date.from_string('2022-03-24'), 'partner_id': cls.san_marino_partner.id, 'partner_bank_id': cls.test_bank.id, 'invoice_line_ids': product_lines( ProductLine(cls.line_tax_22p, 'Product A', product_a.id), ProductLine(cls.line_tax_4p, 'Product B, taxed 4%', product_b.id) ) } cls.reverse_charge_bill_san_marino = cls.env['account.move'].with_company(cls.company).create(bill_data_san_marino) # Posting moves ----------- cls.reverse_charge_invoice._post() cls.reverse_charge_bill._post() cls.reverse_charge_bill_2._post() cls.reverse_charge_bill_san_marino._post() cls.reverse_charge_refund._post() def test_reverse_charge_invoice(self): self._test_invoice_with_sample_file(self.reverse_charge_invoice, "reverse_charge_invoice.xml") def test_reverse_charge_bill(self): self._test_invoice_with_sample_file(self.reverse_charge_bill, "reverse_charge_bill.xml") def test_reverse_charge_bill_2(self): self._test_invoice_with_sample_file( self.reverse_charge_bill_2, "reverse_charge_bill.xml", xpaths_result={ "//DatiGeneraliDocumento/Numero": "", "(//DettaglioLinee/Descrizione)[2]": "", }, xpaths_file={ "//DatiGeneraliDocumento/TipoDocumento": "TD19", "//DatiGeneraliDocumento/Numero": "", "(//DettaglioLinee/Descrizione)[2]": "", } ) def test_reverse_charge_bill_san_marino(self): self._test_invoice_with_sample_file( self.reverse_charge_bill_san_marino, "reverse_charge_bill.xml", xpaths_result={ "//DatiGeneraliDocumento/Numero": "", "(//DettaglioLinee/Descrizione)[2]": "", }, xpaths_file={ "//CedentePrestatore": """ SM 6784 Prospectra RF18 Via Ventotto Luglio 212 Centro Uffici 00000 San Marino SM """, "//DatiGeneraliDocumento/TipoDocumento": "TD28", "//DatiGeneraliDocumento/Numero": "", "(//DettaglioLinee/Descrizione)[2]": "", } ) def test_reverse_charge_refund(self): self._test_invoice_with_sample_file( self.reverse_charge_refund, "reverse_charge_bill.xml", xpaths_result={ "//DatiGeneraliDocumento/Numero": "", "//DatiPagamento/DettaglioPagamento/DataScadenzaPagamento": "", }, xpaths_file={ "//DatiGenerali": f""" TD18 EUR 2022-03-24 -1808.91 {self.reverse_charge_bill.name} {self.reverse_charge_refund.invoice_date} """, "//DatiPagamento/DettaglioPagamento/DataScadenzaPagamento": "", "(//DettaglioLinee/PrezzoUnitario)[1]": "-800.400000", "(//DettaglioLinee/PrezzoUnitario)[2]": "-800.400000", "(//DettaglioLinee/PrezzoTotale)[1]": "-800.40", "(//DettaglioLinee/PrezzoTotale)[2]": "-800.40", "(//DatiRiepilogo/ImponibileImporto)[1]": "-800.40", "(//DatiRiepilogo/ImponibileImporto)[2]": "-800.40", "(//DatiRiepilogo/Imposta)[1]": "-176.09", "(//DatiRiepilogo/Imposta)[2]": "-32.02", } )