From 902eb6d2f0f4b16769927fa210d0a8946241ad14 Mon Sep 17 00:00:00 2001 From: Luc De Meyer Date: Mon, 2 May 2022 15:35:56 +0200 Subject: [PATCH] [14.0]intrastat - improved brexit support --- .../models/intrastat_product_declaration.py | 63 +++++++--- .../report/intrastat_product_report_xls.py | 4 +- intrastat_product/tests/__init__.py | 1 + intrastat_product/tests/common.py | 4 +- intrastat_product/tests/test_brexit.py | 111 ++++++++++++++++++ .../views/intrastat_product_declaration.xml | 6 +- 6 files changed, 170 insertions(+), 19 deletions(-) create mode 100644 intrastat_product/tests/test_brexit.py diff --git a/intrastat_product/models/intrastat_product_declaration.py b/intrastat_product/models/intrastat_product_declaration.py index 92abd20..a3b85e0 100644 --- a/intrastat_product/models/intrastat_product_declaration.py +++ b/intrastat_product/models/intrastat_product_declaration.py @@ -1,5 +1,5 @@ # Copyright 2011-2020 Akretion France (http://www.akretion.com) -# Copyright 2009-2021 Noviat (http://www.noviat.com) +# Copyright 2009-2022 Noviat (http://www.noviat.com) # @author Alexis de Lattre # @author Luc de Meyer @@ -492,21 +492,20 @@ class IntrastatProductDeclaration(models.Model): def _get_product_origin_country_code( self, inv_line, product_origin_country, notedict ): - product_origin_country_code = "QU" + cc = "QU" if product_origin_country.code: - product_origin_country_code = product_origin_country.code + cc = product_origin_country.code year = self.year or str(inv_line.move_id.date.year) if year >= "2021": - if ( - hasattr(inv_line.product_id, "origin_state_id") - and inv_line.product_id.origin_state_id - and inv_line.product_id.origin_state_id.name.lower() - == "northern ireland" - ): - product_origin_country_code = "XI" - elif inv_line.product_id.origin_country_id.code == "GB": - product_origin_country_code = "XU" - return product_origin_country_code + product_origin_state = getattr( + inv_line.product_id, + "origin_state_id", + self.env["res.country.state"], + ) + cc = self.env["res.partner"]._get_intrastat_country_code( + product_origin_country, product_origin_state + ) + return cc def _get_vat(self, inv_line, notedict): vat = False @@ -683,6 +682,9 @@ class IntrastatProductDeclaration(models.Model): partner_country = self._get_partner_country( inv_line, notedict, eu_countries ) + partner_country_code = ( + invoice.commercial_partner_id._get_intrastat_country_code() + ) if inv_intrastat_line: hs_code = inv_intrastat_line.hs_code_id @@ -741,6 +743,7 @@ class IntrastatProductDeclaration(models.Model): "parent_id": self.id, "invoice_line_id": inv_line.id, "src_dest_country_id": partner_country.id, + "src_dest_country_code": partner_country_code, "product_id": inv_line.product_id.id, "hs_code_id": hs_code.id, "weight": weight, @@ -847,7 +850,7 @@ class IntrastatProductDeclaration(models.Model): @api.model def _group_line_hashcode_fields(self, computation_line): return { - "country": computation_line.src_dest_country_id.id or False, + "country": computation_line.src_dest_country_code, "hs_code_id": computation_line.hs_code_id.id or False, "intrastat_unit": computation_line.intrastat_unit_id.id or False, "transaction": computation_line.transaction_id.id or False, @@ -866,6 +869,7 @@ class IntrastatProductDeclaration(models.Model): def _prepare_grouped_fields(self, computation_line, fields_to_sum): vals = { "src_dest_country_id": computation_line.src_dest_country_id.id, + "src_dest_country_code": computation_line.src_dest_country_code, "intrastat_unit_id": computation_line.intrastat_unit_id.id, "hs_code_id": computation_line.hs_code_id.id, "transaction_id": computation_line.transaction_id.id, @@ -1074,6 +1078,15 @@ class IntrastatProductComputationLine(models.Model): string="Country", help="Country of Origin/Destination", ) + src_dest_country_code = fields.Char( + string="Country Code", + compute="_compute_src_dest_country_code", + store=True, + required=True, + readonly=False, + help="2 digit code of country of origin/destination.\n" + "Specify 'XI' for UK Northern Ireland and 'XU' for rest of the UK.", + ) product_id = fields.Many2one( "product.product", related="invoice_line_id.product_id" ) @@ -1133,6 +1146,14 @@ class IntrastatProductComputationLine(models.Model): incoterm_id = fields.Many2one("account.incoterms", string="Incoterm") transport_id = fields.Many2one("intrastat.transport_mode", string="Transport Mode") + @api.onchange("src_dest_country_id") + def _onchange_src_dest_country_id(self): + self.src_dest_country_code = self.src_dest_country_id.code + if self.parent_id.year >= "2021": + self.src_dest_country_code = self.env[ + "res.partner" + ]._get_intrastat_country_code(country=self.src_dest_country_id) + @api.depends("transport_id") def _compute_check_validity(self): """TO DO: logic based upon fields""" @@ -1188,6 +1209,12 @@ class IntrastatProductDeclarationLine(models.Model): string="Country", help="Country of Origin/Destination", ) + src_dest_country_code = fields.Char( + string="Country Code", + required=True, + help="2 digit code of country of origin/destination.\n" + "Specify 'XI' for UK Northern Ireland and 'XU' for rest of the UK.", + ) hs_code_id = fields.Many2one("hs.code", string="Intrastat Code") intrastat_unit_id = fields.Many2one( "intrastat.unit", @@ -1230,6 +1257,14 @@ class IntrastatProductDeclarationLine(models.Model): incoterm_id = fields.Many2one("account.incoterms", string="Incoterm") transport_id = fields.Many2one("intrastat.transport_mode", string="Transport Mode") + @api.onchange("src_dest_country_id") + def _onchange_src_dest_country_id(self): + self.src_dest_country_code = self.src_dest_country_id.code + if self.parent_id.year >= "2021": + self.src_dest_country_code = self.env[ + "res.partner" + ]._get_intrastat_country_code(country=self.src_dest_country_id) + @api.constrains("vat") def _check_vat(self): for this in self: diff --git a/intrastat_product/report/intrastat_product_report_xls.py b/intrastat_product/report/intrastat_product_report_xls.py index 4fdc891..a666d25 100644 --- a/intrastat_product/report/intrastat_product_report_xls.py +++ b/intrastat_product/report/intrastat_product_report_xls.py @@ -1,4 +1,4 @@ -# Copyright 2009-2020 Noviat +# Copyright 2009-2022 Noviat # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). import logging @@ -83,7 +83,7 @@ class IntrastatProductDeclarationXlsx(models.AbstractModel): }, "line": { "type": "string", - "value": self._render("line.src_dest_country_id.name"), + "value": self._render("line.src_dest_country_code"), }, "width": 28, }, diff --git a/intrastat_product/tests/__init__.py b/intrastat_product/tests/__init__.py index 804b5f7..a7ccefc 100644 --- a/intrastat_product/tests/__init__.py +++ b/intrastat_product/tests/__init__.py @@ -2,6 +2,7 @@ from . import common from . import common_purchase from . import common_sale from . import test_intrastat_product +from . import test_brexit from . import test_company from . import test_purchase_order from . import test_sale_order diff --git a/intrastat_product/tests/common.py b/intrastat_product/tests/common.py index 4047ba6..17518d5 100644 --- a/intrastat_product/tests/common.py +++ b/intrastat_product/tests/common.py @@ -41,7 +41,7 @@ class IntrastatProductCommon(IntrastatCommon): @classmethod def _init_regions(cls): - # Create Belgium Region + # Create Region cls._create_region() vals = { @@ -153,7 +153,7 @@ class IntrastatProductCommon(IntrastatCommon): @classmethod def _create_region(cls, vals=None): values = { - "code": "BE_w", + "code": "2", "country_id": cls.env.ref("base.be").id, "company_id": cls.env.company.id, "description": "Belgium Walloon Region", diff --git a/intrastat_product/tests/test_brexit.py b/intrastat_product/tests/test_brexit.py new file mode 100644 index 0000000..1917a98 --- /dev/null +++ b/intrastat_product/tests/test_brexit.py @@ -0,0 +1,111 @@ +# Copyright 2022 Noviat. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.tests import Form, SavepointCase + +from .common import IntrastatProductCommon + + +class TestIntrastatBrexit(IntrastatProductCommon, SavepointCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.inv_obj = cls.env["account.move"] + cls.hs_code_whiskey = cls.env["hs.code"].create( + { + "description": "Whiskey", + "local_code": "22083000", + } + ) + cls.product_xi = cls.env["product.product"].create( + { + "name": "Bushmills Original", + "weight": 1.4, + "list_price": 30.0, + "standard_price": 15.0, + "origin_country_id": cls.env.ref("base.uk").id, + "origin_state_id": cls.env.ref("base.state_uk18").id, + "hs_code_id": cls.hs_code_whiskey.id, + } + ) + cls.product_xu = cls.env["product.product"].create( + { + "name": "Glenfiddich", + "weight": 1.4, + "list_price": 50.0, + "standard_price": 25.0, + "origin_country_id": cls.env.ref("base.uk").id, + "origin_state_id": cls.env.ref("base.state_uk6").id, + "hs_code_id": cls.hs_code_whiskey.id, + } + ) + cls.partner_xi = cls.env["res.partner"].create( + { + "name": "Bushmills Distillery", + "country_id": cls.env.ref("base.uk").id, + "state_id": cls.env.ref("base.state_uk18").id, + "vat": "XI123456782", + "property_account_position_id": cls.position.id, + } + ) + + def test_brexit_sale(self): + inv_out_xi = self.inv_obj.with_context(default_move_type="out_invoice").create( + { + "partner_id": self.partner_xi.id, + "fiscal_position_id": self.position.id, + } + ) + with Form(inv_out_xi) as inv_form: + with inv_form.invoice_line_ids.new() as ail: + ail.product_id = self.product_c3po.product_variant_ids[0] + inv_out_xi.action_post() + + self._create_declaration( + { + "declaration_type": "dispatches", + "year": str(inv_out_xi.date.year), + "month": str(inv_out_xi.date.month).zfill(2), + } + ) + self.declaration.action_gather() + self.declaration.generate_declaration() + cline = self.declaration.computation_line_ids + dline = self.declaration.declaration_line_ids + self.assertEqual(cline.src_dest_country_code, "XI") + self.assertEqual(dline.src_dest_country_code, "XI") + + def test_brexit_purchase(self): + inv_in_xi = self.inv_obj.with_context(default_move_type="in_invoice").create( + { + "partner_id": self.partner_xi.id, + "fiscal_position_id": self.position.id, + } + ) + with Form(inv_in_xi) as inv_form: + with inv_form.invoice_line_ids.new() as ail: + ail.product_id = self.product_xi + with inv_form.invoice_line_ids.new() as ail: + ail.product_id = self.product_xu + inv_in_xi.invoice_date = inv_in_xi.date + inv_in_xi.action_post() + + self._create_declaration( + { + "declaration_type": "arrivals", + "year": str(inv_in_xi.date.year), + "month": str(inv_in_xi.date.month).zfill(2), + } + ) + self.declaration.action_gather() + self.declaration.generate_declaration() + clines = self.declaration.computation_line_ids + cl_xi = clines.filtered(lambda r: r.product_id == self.product_xi) + cl_xu = clines.filtered(lambda r: r.product_id == self.product_xu) + dlines = self.declaration.declaration_line_ids + dl_xi = dlines.filtered(lambda r: r.computation_line_ids == cl_xi) + dl_xu = dlines.filtered(lambda r: r.computation_line_ids == cl_xu) + self.assertEqual(cl_xi.product_origin_country_code, "XI") + self.assertEqual(cl_xu.product_origin_country_code, "XU") + self.assertEqual(dl_xi.product_origin_country_code, "XI") + self.assertEqual(dl_xu.product_origin_country_code, "XU") diff --git a/intrastat_product/views/intrastat_product_declaration.xml b/intrastat_product/views/intrastat_product_declaration.xml index 21f1bb4..aee1eee 100644 --- a/intrastat_product/views/intrastat_product_declaration.xml +++ b/intrastat_product/views/intrastat_product_declaration.xml @@ -224,6 +224,7 @@ + + @@ -322,6 +324,7 @@ /> + - + +