From 6bbf071a04b523cb5485d40b7f89bc1d468c2c31 Mon Sep 17 00:00:00 2001 From: "Luis J. Salvatierra" Date: Fri, 24 Nov 2023 12:52:48 +0100 Subject: [PATCH] [FIX] intrastat refunds. Modify transaction ID for the following cases: * Dispatches: * out_invoice B2B: 11 * out_invoice B2C: 12 * in_refund: 21 * Arrivals: * out_refund: 21 * in_invoice: 11 Don't propagate intrastat transaction to refund. Add some tests. Remove unused files. --- intrastat_base/tests/common.py | 4 +- intrastat_base/tests/models.py | 10 -- intrastat_base/tests/test_all.py | 97 +++++++++++++++++++ .../models/intrastat_product_declaration.py | 26 +++-- intrastat_product/tests/__init__.py | 1 + .../tests/test_account_move_reversal.py | 88 +++++++++++++++++ intrastat_product/wizards/__init__.py | 1 + .../wizards/account_move_reversal.py | 12 +++ 8 files changed, 220 insertions(+), 19 deletions(-) delete mode 100644 intrastat_base/tests/models.py create mode 100644 intrastat_product/tests/test_account_move_reversal.py create mode 100644 intrastat_product/wizards/account_move_reversal.py diff --git a/intrastat_base/tests/common.py b/intrastat_base/tests/common.py index ac1fbf30..c98b5b28 100644 --- a/intrastat_base/tests/common.py +++ b/intrastat_base/tests/common.py @@ -1,13 +1,15 @@ # Copyright 2021 ACSONE SA/NV # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo.tests.common import TransactionCase -class IntrastatCommon(object): +class IntrastatCommon(TransactionCase): @classmethod def setUpClass(cls): super().setUpClass() cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) cls.chart_template_obj = cls.env["account.chart.template"] + cls.fp_obj = cls.env["account.fiscal.position"] cls.mail_obj = cls.env["mail.mail"] cls.demo_user = cls.env.ref("base.user_demo") diff --git a/intrastat_base/tests/models.py b/intrastat_base/tests/models.py deleted file mode 100644 index 302914c6..00000000 --- a/intrastat_base/tests/models.py +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright 2021 ACSONE SA/NV -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import models - - -class IntrastatDeclarationTest(models.Model): - _inherit = ["mail.thread", "mail.activity.mixin", "intrastat.common"] - _name = "intrastat.declaration.test" - _description = "Intrastat Declaration Test" diff --git a/intrastat_base/tests/test_all.py b/intrastat_base/tests/test_all.py index 70913a60..9d8834a3 100644 --- a/intrastat_base/tests/test_all.py +++ b/intrastat_base/tests/test_all.py @@ -1,14 +1,43 @@ from odoo.exceptions import ValidationError +from odoo.tests import tagged from .common import IntrastatCommon +@tagged("post_install", "-at_install") class TestIntrastatBase(IntrastatCommon): """Tests for this module""" @classmethod def setUpClass(cls): super().setUpClass() + cls.env = cls.env( + context=dict( + cls.env.context, + mail_create_nolog=True, + mail_create_nosubscribe=True, + mail_notrack=True, + no_reset_password=True, + tracking_disable=True, + ) + ) + cls.fp_b2c = cls.fp_obj.create( + { + "name": "Test", + "vat_required": False, + "intrastat": "b2c", + } + ) + cls.fp_b2b = cls.fp_obj.create( + { + "name": "Test", + "vat_required": True, + "intrastat": "b2b", + } + ) + cls.journal_sale = cls.env["account.journal"].search( + [("company_id", "=", cls.demo_company.id), ("type", "=", "sale")], limit=1 + ) def test_company(self): # add 'Demo user' to intrastat_remind_user_ids @@ -46,3 +75,71 @@ def test_fiscal_position(self): "intrastat": "b2c", } ) + + def test_account_move_fp_intrastat(self): + invoice = self.env["account.move"].create( + { + "name": "Test", + "move_type": "out_invoice", + "invoice_date": "2021-01-01", + "invoice_date_due": "2021-01-01", + "partner_id": self.demo_company.partner_id.id, + "fiscal_position_id": self.fp_b2b.id, + "journal_id": self.journal_sale.id, + } + ) + self.assertEqual(invoice.intrastat_fiscal_position, "b2b") + invoice.intrastat_fiscal_position = "b2c" + self.assertEqual(invoice.intrastat_fiscal_position, "b2c") + # Check that duplicating the invoice does not copy the intrastat value + invoice_2 = invoice.copy() + self.assertEqual(invoice.fiscal_position_id, invoice_2.fiscal_position_id) + self.assertEqual(invoice_2.intrastat_fiscal_position, "b2b") + + def test_out_invoice_refund_fp_intrastat(self): + partner = self.demo_company.partner_id + invoice = self.env["account.move"].create( + { + "name": "Test", + "move_type": "out_invoice", + "invoice_date": "2021-01-01", + "invoice_date_due": "2021-01-01", + "partner_id": partner.id, + "fiscal_position_id": self.fp_b2b.id, + "journal_id": self.journal_sale.id, + "invoice_line_ids": [ + ( + 0, + 0, + { + "name": "Test", + "quantity": 1, + "price_unit": 100, + "account_id": partner.property_account_receivable_id.id, + }, + ) + ], + } + ) + invoice.intrastat_fiscal_position = "b2c" + invoice.action_post() + self.assertEqual(invoice.intrastat_fiscal_position, "b2c") + move_reversal = ( + self.env["account.move.reversal"] + .with_context(active_model="account.move", active_ids=invoice.ids) + .create( + { + "date": "2021-01-01", + "reason": "no reason", + "refund_method": "refund", + "journal_id": invoice.journal_id.id, + } + ) + ) + reversal = move_reversal.reverse_moves() + reverse_move = self.env["account.move"].browse(reversal["res_id"]) + + self.assertEqual( + reverse_move.intrastat_fiscal_position, + "b2b", + ) diff --git a/intrastat_product/models/intrastat_product_declaration.py b/intrastat_product/models/intrastat_product_declaration.py index c3f2bac5..c5416415 100644 --- a/intrastat_product/models/intrastat_product_declaration.py +++ b/intrastat_product/models/intrastat_product_declaration.py @@ -308,16 +308,26 @@ def _get_partner_country(self, inv_line, notedict, eu_countries): return country def _get_intrastat_transaction(self, inv_line, notedict): + tr_11 = self.env.ref("intrastat_product.intrastat_transaction_11") + tr_12 = self.env.ref("intrastat_product.intrastat_transaction_12") + tr_21 = self.env.ref("intrastat_product.intrastat_transaction_21") + invoice = inv_line.move_id transaction = invoice.intrastat_transaction_id - fp2transaction = { - "b2b": self.env.ref("intrastat_product.intrastat_transaction_11"), - "b2c": self.env.ref("intrastat_product.intrastat_transaction_12"), - } - if not transaction: - # as we have searched with intrastat_fiscal_position in ('b2b', 'b2c') - # we should always have intrastat_fiscal_position in fp2transaction - transaction = fp2transaction[invoice.intrastat_fiscal_position] + intr_fp = invoice.intrastat_fiscal_position + if not transaction and intr_fp: + if "arrivals" in self.declaration_type: + if "out_refund" == invoice.move_type: + transaction = tr_21 + elif "in_invoice" == invoice.move_type: + transaction = tr_11 + elif "dispatches" in self.declaration_type: + if "in_refund" == invoice.move_type: + transaction = tr_21 + elif "out_invoice" == invoice.move_type and "b2c" == intr_fp: + transaction = tr_12 + if not transaction and intr_fp: + transaction = tr_11 return transaction def _get_weight_and_supplunits(self, inv_line, hs_code, notedict): diff --git a/intrastat_product/tests/__init__.py b/intrastat_product/tests/__init__.py index a7ccefcb..f4591249 100644 --- a/intrastat_product/tests/__init__.py +++ b/intrastat_product/tests/__init__.py @@ -6,3 +6,4 @@ from . import test_company from . import test_purchase_order from . import test_sale_order +from . import test_account_move_reversal diff --git a/intrastat_product/tests/test_account_move_reversal.py b/intrastat_product/tests/test_account_move_reversal.py new file mode 100644 index 00000000..0bffde9a --- /dev/null +++ b/intrastat_product/tests/test_account_move_reversal.py @@ -0,0 +1,88 @@ +# © 2023 FactorLibre - Luis J. Salvatierra +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import fields +from odoo.tests.common import tagged + +from .common_purchase import IntrastatPurchaseCommon +from .common_sale import IntrastatSaleCommon + + +@tagged("post_install", "-at_install") +class TestIntrastatAccountMoveReversal(IntrastatSaleCommon, IntrastatPurchaseCommon): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.tr_11 = cls.env.ref("intrastat_product.intrastat_transaction_11") + cls.tr_12 = cls.env.ref("intrastat_product.intrastat_transaction_12") + cls.tr_21 = cls.env.ref("intrastat_product.intrastat_transaction_21") + + def _create_out_invoice(self): + self._create_sale_order() + self.sale.action_confirm() + self.sale.picking_ids.action_assign() + for line in self.sale.picking_ids.move_line_ids: + line.qty_done = line.reserved_uom_qty + self.sale.picking_ids._action_done() + self.assertEqual("done", self.sale.picking_ids.state) + return self.sale._create_invoices() + + def _create_in_invoice(self): + self._create_purchase_order() + self.purchase.button_confirm() + self.purchase.picking_ids.action_assign() + for line in self.purchase.picking_ids.move_line_ids: + line.qty_done = line.reserved_uom_qty + self.purchase.picking_ids._action_done() + self.assertEqual("done", self.purchase.picking_ids.state) + action = self.purchase.action_create_invoice() + invoice_id = action["res_id"] + return self.move_obj.browse(invoice_id) + + def test_out_invoice_reversal(self): + date_order = "2021-09-01" + invoice = self._create_out_invoice() + invoice.invoice_date = fields.Date.from_string(date_order) + invoice.intrastat_fiscal_position = "b2c" + invoice.action_post() + self.assertFalse(invoice.intrastat_transaction_id) + invoice.intrastat_transaction_id = self.tr_12 + + move_reversal = ( + self.env["account.move.reversal"] + .with_context(active_model="account.move", active_ids=invoice.ids) + .create( + { + "date": fields.Date.from_string("2021-09-02"), + "reason": "no reason", + "refund_method": "refund", + "journal_id": invoice.journal_id.id, + } + ) + ) + reversal = move_reversal.reverse_moves() + in_refund = self.env["account.move"].browse(reversal["res_id"]) + self.assertFalse(in_refund.intrastat_transaction_id) + + def test_in_invoice_reversal(self): + date_order = "2021-09-01" + invoice = self._create_in_invoice() + invoice.invoice_date = fields.Date.from_string(date_order) + invoice.action_post() + self.assertFalse(invoice.intrastat_transaction_id) + invoice.intrastat_transaction_id = self.tr_11 + + move_reversal = ( + self.env["account.move.reversal"] + .with_context(active_model="account.move", active_ids=invoice.ids) + .create( + { + "date": fields.Date.from_string("2021-09-02"), + "reason": "no reason", + "refund_method": "refund", + "journal_id": invoice.journal_id.id, + } + ) + ) + reversal = move_reversal.reverse_moves() + in_refund = self.env["account.move"].browse(reversal["res_id"]) + self.assertFalse(in_refund.intrastat_transaction_id) diff --git a/intrastat_product/wizards/__init__.py b/intrastat_product/wizards/__init__.py index 552479c7..b4a5c9a3 100644 --- a/intrastat_product/wizards/__init__.py +++ b/intrastat_product/wizards/__init__.py @@ -1,2 +1,3 @@ from . import res_config_settings from . import intrastat_result_view +from . import account_move_reversal diff --git a/intrastat_product/wizards/account_move_reversal.py b/intrastat_product/wizards/account_move_reversal.py new file mode 100644 index 00000000..7cf15a03 --- /dev/null +++ b/intrastat_product/wizards/account_move_reversal.py @@ -0,0 +1,12 @@ +# © 2023 FactorLibre - Luis J. Salvatierra +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import models + + +class AccountMoveReversal(models.TransientModel): + _inherit = "account.move.reversal" + + def _prepare_default_reversal(self, move): + res = super()._prepare_default_reversal(move) + res["intrastat_transaction_id"] = False + return res