289 lines
12 KiB
Python
289 lines
12 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from collections import namedtuple
|
|
|
|
from odoo import Command, 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()
|
|
|
|
# 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': cls.repartition_lines(
|
|
cls.RepartitionLine(100, 'base', ('+03', '+vj9')),
|
|
cls.RepartitionLine(100, 'tax', ('+5v',)),
|
|
cls.RepartitionLine(-100, 'tax', ('-4v',))),
|
|
'refund_repartition_line_ids': cls.repartition_lines(
|
|
cls.RepartitionLine(100, 'base', ('-03', '-vj9')),
|
|
cls.RepartitionLine(100, 'tax', False),
|
|
cls.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)
|
|
|
|
# Purchase tax 4% with External Reverse Charge, targeting the tax grid for import of goods
|
|
# already in Italy in a VAT deposit
|
|
cls.purchase_tax_4p_already_in_italy = cls.env['account.tax'].with_company(cls.company).create({
|
|
**tax_data,
|
|
'name': 'Tax 4% purchase Reverse Charge, in Italy',
|
|
'invoice_repartition_line_ids': cls.repartition_lines(
|
|
cls.RepartitionLine(100, 'base', ('+03', '+vj3')),
|
|
cls.RepartitionLine(100, 'tax', ('+5v',)),
|
|
cls.RepartitionLine(-100, 'tax', ('-4v',))),
|
|
'refund_repartition_line_ids': cls.repartition_lines(
|
|
cls.RepartitionLine(100, 'base', ('-03', '-vj3')),
|
|
cls.RepartitionLine(100, 'tax', False),
|
|
cls.RepartitionLine(-100, 'tax', False)),
|
|
})
|
|
|
|
# Purchase tax 22% with Reverse Charge, targeting the tax grid for Construction Subcontractors
|
|
# already in Italy in a VAT deposit
|
|
cls.purchase_tax_vj12 = cls.env['account.tax'].with_company(cls.company).create({
|
|
**tax_data,
|
|
'name': '22% purchase RC Construction Subcontractors',
|
|
'amount': 22.0,
|
|
'invoice_repartition_line_ids': cls.repartition_lines(
|
|
cls.RepartitionLine(100, 'base', ('+03', '+vj12')),
|
|
cls.RepartitionLine(100, 'tax', ('+5v',)),
|
|
cls.RepartitionLine(-100, 'tax', ('-4v',))),
|
|
'refund_repartition_line_ids': cls.repartition_lines(
|
|
cls.RepartitionLine(100, 'base', ('-03', '-vj12')),
|
|
cls.RepartitionLine(100, 'tax', False),
|
|
cls.RepartitionLine(-100, 'tax', False)),
|
|
})
|
|
|
|
# Purchase tax 22% with Reverse Charge
|
|
cls.purchase_tax_22p = cls.env['account.tax'].with_company(cls.company).create({
|
|
**tax_data,
|
|
'name': 'Tax 22% purchase Reverse Charge',
|
|
'amount': 22.0,
|
|
})
|
|
|
|
# Export tax 0%
|
|
cls.sale_tax_0v = cls.env['account.tax'].with_company(cls.company).create({
|
|
**tax_data,
|
|
'type_tax_use': 'sale',
|
|
'amount': 0.0,
|
|
'amount_type': 'percent',
|
|
'l10n_it_exempt_reason': 'N1',
|
|
'l10n_it_law_reference': 'test',
|
|
})
|
|
|
|
# Export tax 0% Internal Reverse Charge
|
|
cls.sale_tax_n63 = cls.env['account.tax'].with_company(cls.company).create({
|
|
**tax_data,
|
|
'name': 'Construction subcontractors',
|
|
'type_tax_use': 'sale',
|
|
'amount': 0.0,
|
|
'amount_type': 'percent',
|
|
'l10n_it_exempt_reason': 'N6.3',
|
|
'l10n_it_law_reference': 'test',
|
|
})
|
|
|
|
def test_invoice_external_reverse_charge(self):
|
|
invoice = self.env['account.move'].with_company(self.company).create({
|
|
'move_type': 'out_invoice',
|
|
'invoice_date': '2022-03-24',
|
|
'invoice_date_due': '2022-03-24',
|
|
'partner_id': self.french_partner.id,
|
|
'partner_bank_id': self.test_bank.id,
|
|
'invoice_line_ids': [
|
|
Command.create({
|
|
'name': "Product A",
|
|
'product_id': self.product_a.id,
|
|
'price_unit': 800.40,
|
|
'tax_ids': [Command.set(self.sale_tax_0v.ids)],
|
|
}),
|
|
Command.create({
|
|
'name': "Product B",
|
|
'product_id': self.product_b.id,
|
|
'price_unit': 800.40,
|
|
'tax_ids': [Command.set(self.sale_tax_0v.ids)],
|
|
}),
|
|
],
|
|
})
|
|
invoice.action_post()
|
|
self._assert_export_invoice(invoice, 'invoice_external_reverse_charge.xml')
|
|
|
|
def test_invoice_internal_reverse_charge(self):
|
|
invoice = self.env['account.move'].with_company(self.company).create({
|
|
'move_type': 'out_invoice',
|
|
'invoice_date': '2022-03-24',
|
|
'invoice_date_due': '2022-03-24',
|
|
'partner_id': self.italian_partner_a.id,
|
|
'partner_bank_id': self.test_bank.id,
|
|
'invoice_line_ids': [
|
|
Command.create({
|
|
'name': f"Construction subcontracting service {month}",
|
|
'product_id': self.product_a.id,
|
|
'price_unit': price,
|
|
'tax_ids': [Command.set(self.sale_tax_n63.ids)],
|
|
}) for month, price in [("January", 350.0), ("February", 300.0), ("March", 50.0), ("April", 50.0)]
|
|
],
|
|
})
|
|
invoice.action_post()
|
|
self._assert_export_invoice(invoice, 'invoice_internal_reverse_charge.xml')
|
|
|
|
def test_bill_reverse_charge_and_refund(self):
|
|
bills = self.env['account.move'].with_company(self.company).create([{
|
|
'move_type': 'in_invoice',
|
|
'invoice_date': '2022-03-24',
|
|
'invoice_date_due': '2022-03-24',
|
|
'partner_id': self.french_partner.id,
|
|
'partner_bank_id': self.test_bank.id,
|
|
'invoice_line_ids': [
|
|
Command.create({
|
|
'name': name,
|
|
'product_id': product.id,
|
|
'price_unit': 800.40,
|
|
'tax_ids': [Command.set(tax.ids)],
|
|
}),
|
|
],
|
|
} for name, product, tax in [
|
|
("Product B, taxed 4%", self.product_b, self.purchase_tax_4p),
|
|
("Product A", self.product_a, self.purchase_tax_22p),
|
|
]])
|
|
bills.action_post()
|
|
self._assert_export_invoice(bills[0], 'bill_reverse_charge.xml')
|
|
|
|
credit_note = self.env['account.move'].with_company(self.company).create({
|
|
'invoice_date': '2022-03-24',
|
|
'invoice_date_due': '2022-03-24',
|
|
'move_type': 'in_refund',
|
|
'partner_id': self.french_partner.id,
|
|
'invoice_line_ids': [
|
|
Command.create({
|
|
'name': "Product A",
|
|
'product_id': self.product_a.id,
|
|
'price_unit': 800.40,
|
|
'tax_ids': [Command.set(self.purchase_tax_22p.ids)],
|
|
}),
|
|
Command.create({
|
|
'name': "Product B, taxed 4%",
|
|
'product_id': self.product_b.id,
|
|
'price_unit': 400.40,
|
|
'tax_ids': [Command.set(self.purchase_tax_4p.ids)],
|
|
}),
|
|
],
|
|
})
|
|
credit_note.action_post()
|
|
|
|
(bills.line_ids + credit_note.line_ids).filtered(lambda line: line.account_type == 'liability_payable').reconcile()
|
|
self._assert_export_invoice(credit_note, 'credit_note_reverse_charge.xml')
|
|
|
|
def test_reverse_charge_bill_2(self):
|
|
bill = self.env['account.move'].with_company(self.company).create({
|
|
'move_type': 'in_invoice',
|
|
'invoice_date': '2022-03-24',
|
|
'invoice_date_due': '2022-03-24',
|
|
'partner_id': self.french_partner.id,
|
|
'partner_bank_id': self.test_bank.id,
|
|
'invoice_line_ids': [
|
|
Command.create({
|
|
'name': "Product A",
|
|
'product_id': self.product_a.id,
|
|
'price_unit': 800.40,
|
|
'tax_ids': [Command.set(self.purchase_tax_22p.ids)],
|
|
}),
|
|
Command.create({
|
|
'name': "Product B, taxed 4% Already in Italy",
|
|
'product_id': self.product_b.id,
|
|
'price_unit': 800.40,
|
|
'tax_ids': [Command.set(self.purchase_tax_4p_already_in_italy.ids)],
|
|
}),
|
|
],
|
|
})
|
|
bill.action_post()
|
|
self._assert_export_invoice(bill, 'bill_reverse_charge_2.xml')
|
|
|
|
def test_bill_reverse_charge_san_marino(self):
|
|
bill = self.env['account.move'].with_company(self.company).create({
|
|
'move_type': 'in_invoice',
|
|
'invoice_date': '2022-03-24',
|
|
'invoice_date_due': '2022-03-24',
|
|
'partner_id': self.san_marino_partner.id,
|
|
'partner_bank_id': self.test_bank.id,
|
|
'invoice_line_ids': [
|
|
Command.create({
|
|
'name': "Product A",
|
|
'product_id': self.product_a.id,
|
|
'price_unit': 800.40,
|
|
'tax_ids': [Command.set(self.purchase_tax_22p.ids)],
|
|
}),
|
|
Command.create({
|
|
'name': "Product B, taxed 4%",
|
|
'product_id': self.product_b.id,
|
|
'price_unit': 800.40,
|
|
'tax_ids': [Command.set(self.purchase_tax_4p.ids)],
|
|
}),
|
|
],
|
|
})
|
|
bill.action_post()
|
|
self._assert_export_invoice(bill, 'bill_reverse_charge_san_marino.xml')
|
|
|
|
def test_receive_bill_reverse_charge_internal(self):
|
|
""" Imported Internal Reverse Charge bill about Construction Subcontractors
|
|
has the Sale 0% RC Tax Exemption N6.3 tax turned into Purchase 22% RC tax,
|
|
targeting tag +VJ12
|
|
"""
|
|
invoice = self._assert_import_invoice('IT01234567891_FPR01.xml', [{
|
|
'invoice_date': fields.Date.from_string('2022-03-24'),
|
|
'amount_untaxed': 750.0,
|
|
'amount_tax': 0.0,
|
|
'invoice_line_ids': [{
|
|
'name': 'Construction subcontracting service January',
|
|
'price_unit': 350.0,
|
|
}, {
|
|
'name': 'Construction subcontracting service February',
|
|
'price_unit': 300.0,
|
|
}, {
|
|
'name': 'Construction subcontracting service March',
|
|
'price_unit': 50.0,
|
|
}, {
|
|
'name': 'Construction subcontracting service April',
|
|
'price_unit': 50.0,
|
|
}]
|
|
}])
|
|
for line in invoice.invoice_line_ids:
|
|
self.assertEqual(len(line.tax_ids), 1)
|
|
rc_tax = line.tax_ids[0]
|
|
self.assertEqual(rc_tax.amount, 22.0)
|
|
self.assertTrue('+vj12' in rc_tax.invoice_repartition_line_ids[0].tag_ids.mapped("name"))
|