From 4c08be7bdebb070346ac13a647bcc0ddbfbb18f1 Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Thu, 9 Feb 2023 23:03:08 +0100 Subject: [PATCH] intrastat_product: single XLSX export There is now a single XLSX export that has 2 tabs: 1) computation lines 2) declaration lines The declaration type is now mentionned in the XLSX export. Ability to export several declarations in 1 XLSX file. --- intrastat_product/__manifest__.py | 1 + .../data/intrastat_transaction.xml | 52 +++++++++---- .../migrations/14.0.1.0.0/pre-migration.py | 13 ---- .../models/intrastat_product_declaration.py | 29 ++++--- .../report/intrastat_product_report_xls.py | 78 ++++++++++--------- intrastat_product/report/report.xml | 21 +++++ intrastat_product/tests/common.py | 62 ++++++++------- .../tests/test_purchase_order.py | 6 +- intrastat_product/tests/test_sale_order.py | 6 +- .../views/intrastat_product_declaration.xml | 17 ++-- 10 files changed, 156 insertions(+), 129 deletions(-) delete mode 100644 intrastat_product/migrations/14.0.1.0.0/pre-migration.py create mode 100644 intrastat_product/report/report.xml diff --git a/intrastat_product/__manifest__.py b/intrastat_product/__manifest__.py index 097d34d..5e9a09c 100644 --- a/intrastat_product/__manifest__.py +++ b/intrastat_product/__manifest__.py @@ -25,6 +25,7 @@ "data": [ "security/intrastat_security.xml", "security/ir.model.access.csv", + "report/report.xml", "views/hs_code.xml", "views/intrastat_region.xml", "views/intrastat_unit.xml", diff --git a/intrastat_product/data/intrastat_transaction.xml b/intrastat_product/data/intrastat_transaction.xml index 7870588..98b4545 100644 --- a/intrastat_product/data/intrastat_transaction.xml +++ b/intrastat_product/data/intrastat_transaction.xml @@ -19,19 +19,27 @@ 22 - Replacement for returned goods (free of charge) + Replacement for returned goods (free of charge) 23 - Replacement (e.g. under warranty) for goods not being returned + Replacement (e.g. under warranty) for goods not being returned 31 - Movements to/from a warehouse (excluding call-off and consignment stock) + Movements to/from a warehouse (excluding call-off and consignment stock) 32 - Supply for sale on approval or after trial (including call-off and consignment stock) + Supply for sale on approval or after trial (including call-off and consignment stock) 33 @@ -39,42 +47,60 @@ 34 - Transactions involving transfer of ownership without financial compensation + Transactions involving transfer of ownership without financial compensation 41 - Transactions with a view to processing under contract (no change of ownership). Goods expected to return to the initial Member State/country of export + Transactions with a view to processing under contract (no change of ownership). Goods expected to return to the initial Member State/country of export 42 - Transactions with a view to processing under contract (no change of ownership). Goods not expected to return to the initial Member State/country of export + Transactions with a view to processing under contract (no change of ownership). Goods not expected to return to the initial Member State/country of export 51 - Transactions following processing under contract (not involving change of ownership). Goods returning to the initial Member State/country of export. + Transactions following processing under contract (not involving change of ownership). Goods returning to the initial Member State/country of export. 52 - Transactions following processing under contract (not involving change of ownership). Goods not returning to the initial Member State/country of export. + Transactions following processing under contract (not involving change of ownership). Goods not returning to the initial Member State/country of export. 71 - Release of goods for free circulation in a Member State with a subsequent export to another Member State + Release of goods for free circulation in a Member State with a subsequent export to another Member State 72 - Transportation of goods from one Member State to another Member State to place the goods under the export procedure + Transportation of goods from one Member State to another Member State to place the goods under the export procedure 80 - Transactions involving the supply of building materials and technical equipment under a general construction or civil engineering contract for which no separate invoicing of the goods is required and an invoice for the total contract is issued + Transactions involving the supply of building materials and technical equipment under a general construction or civil engineering contract for which no separate invoicing of the goods is required and an invoice for the total contract is issued 91 - Hire, loan, and operational leasing longer than 24 months + Hire, loan, and operational leasing longer than 24 months 99 diff --git a/intrastat_product/migrations/14.0.1.0.0/pre-migration.py b/intrastat_product/migrations/14.0.1.0.0/pre-migration.py deleted file mode 100644 index 1f6f9a8..0000000 --- a/intrastat_product/migrations/14.0.1.0.0/pre-migration.py +++ /dev/null @@ -1,13 +0,0 @@ -# Copyright 2020 Akretion France (http://www.akretion.com/) -# @author: Alexis de Lattre -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - - -def migrate(cr, version): - if not version: - return - - cr.execute( - 'ALTER TABLE "intrastat_product_declaration" RENAME "type" ' - 'TO "declaration_type"' - ) diff --git a/intrastat_product/models/intrastat_product_declaration.py b/intrastat_product/models/intrastat_product_declaration.py index 12910bc..30b4704 100644 --- a/intrastat_product/models/intrastat_product_declaration.py +++ b/intrastat_product/models/intrastat_product_declaration.py @@ -159,9 +159,9 @@ class IntrastatProductDeclaration(models.Model): arrivals = company.intrastat_arrivals dispatches = company.intrastat_dispatches if arrivals != "exempt": - res.append(("arrivals", _("Declaration for Arrivals"))) + res.append(("arrivals", _("Arrivals"))) if dispatches != "exempt": - res.append(("dispatches", _("Declaration for Dispatches"))) + res.append(("dispatches", _("Dispatches"))) return res @api.model @@ -233,6 +233,18 @@ class IntrastatProductDeclaration(models.Model): ) this.reporting_level = reporting_level + def name_get(self): + res = [] + type2label = dict( + self.fields_get("declaration_type", "selection")["declaration_type"][ + "selection" + ] + ) + for rec in self: + name = "%s %s" % (rec.year_month, type2label.get(rec.declaration_type)) + res.append((rec.id, name)) + return res + def copy(self, default=None): self.ensure_one() default = default or {} @@ -868,19 +880,6 @@ class IntrastatProductDeclaration(models.Model): self.ensure_one() self.xml_attachment_id and self.xml_attachment_id.unlink() - def create_xls(self): - if self.env.context.get("computation_lines"): - report_file = "intrastat_transactions" - else: - report_file = "intrastat_declaration_lines" - return { - "type": "ir.actions.report", - "report_type": "xlsx", - "report_name": "intrastat_product.product_declaration_xls", - "context": dict(self.env.context, report_file=report_file), - "data": {"dynamic_report": True}, - } - @api.model def _xls_computation_line_fields(self): """ diff --git a/intrastat_product/report/intrastat_product_report_xls.py b/intrastat_product/report/intrastat_product_report_xls.py index f8c602c..1c55542 100644 --- a/intrastat_product/report/intrastat_product_report_xls.py +++ b/intrastat_product/report/intrastat_product_report_xls.py @@ -200,36 +200,30 @@ class IntrastatProductDeclarationXlsx(models.AbstractModel): return template - def _get_ws_params(self, wb, data, declaration): - template = self._get_template(declaration) - if self.env.context.get("computation_lines"): - wl = declaration._xls_computation_line_fields() - report = "computation" - else: - wl = declaration._xls_declaration_line_fields() - report = "declaration" - - title = self._get_title(declaration, report, title_format="normal") - title_short = self._get_title(declaration, report, title_format="short") - sheet_name = title_short[:31].replace("/", "-") - - params = { - "ws_name": sheet_name, - "generate_ws_method": "_intrastat_report", - "title": title, - "wanted_list": wl, - "col_specs": template, - } - return [params] - - def _get_title(self, declaration, report, title_format="normal"): - title = declaration.year_month - if title_format == "normal": - if report == "computation": - title += " : " + _("Computation Lines") - else: - title += " : " + _("Declaration Lines") - return title + def _get_ws_params(self, wb, data, declarations): + template = self._get_template(declarations) + res = [] + wanted_list_computation = declarations._xls_computation_line_fields() + wanted_list_declaration = declarations._xls_declaration_line_fields() + for declaration in declarations: + dname = declaration.display_name + res += [ + { + "ws_name": "%s %s" % (dname, _("comput.")), + "generate_ws_method": "_intrastat_report_computation", + "title": "%s : %s" % (dname, _("Computation Lines")), + "wanted_list": wanted_list_computation, + "col_specs": template, + }, + { + "ws_name": "%s %s" % (dname, _("decl.")), + "generate_ws_method": "_intrastat_report_declaration", + "title": "%s : %s" % (dname, _("Declaration Lines")), + "wanted_list": wanted_list_declaration, + "col_specs": template, + }, + ] + return res def _report_title(self, ws, row_pos, ws_params, data, declaration): return self._write_ws_title(ws, row_pos, ws_params) @@ -244,8 +238,23 @@ class IntrastatProductDeclarationXlsx(models.AbstractModel): ) ws.write_string(row_pos, 0, no_entries, FORMATS["format_left_bold"]) - def _intrastat_report(self, workbook, ws, ws_params, data, declaration): + def _intrastat_report_computation(self, workbook, ws, ws_params, data, declaration): + report = "computation" + lines = declaration.computation_line_ids + self._intrastat_report( + workbook, ws, ws_params, data, declaration, lines, report + ) + def _intrastat_report_declaration(self, workbook, ws, ws_params, data, declaration): + report = "declaration" + lines = declaration.declaration_line_ids + self._intrastat_report( + workbook, ws, ws_params, data, declaration, lines, report + ) + + def _intrastat_report( + self, workbook, ws, ws_params, data, declaration, lines, report + ): ws.set_landscape() ws.fit_to_pages(1, 0) ws.set_header(XLS_HEADERS["xls_headers"]["standard"]) @@ -256,13 +265,6 @@ class IntrastatProductDeclarationXlsx(models.AbstractModel): row_pos = 0 row_pos = self._report_title(ws, row_pos, ws_params, data, declaration) - if self.env.context.get("computation_lines"): - report = "computation" - lines = declaration.computation_line_ids - else: - report = "declaration" - lines = declaration.declaration_line_ids - if not lines: return self._empty_report(ws, row_pos, ws_params, data, declaration, report) diff --git a/intrastat_product/report/report.xml b/intrastat_product/report/report.xml new file mode 100644 index 0000000..c217637 --- /dev/null +++ b/intrastat_product/report/report.xml @@ -0,0 +1,21 @@ + + + + + + Excel Export + intrastat.product.declaration + xlsx + intrastat_product.product_declaration_xls + intrastat_product.product_declaration_xls + 'intrastat-%s-%s%s' % (object.year_month, object.declaration_type, object.state == 'draft' and '-draft' or '') + + + + diff --git a/intrastat_product/tests/common.py b/intrastat_product/tests/common.py index 5d69c84..ea2aaca 100644 --- a/intrastat_product/tests/common.py +++ b/intrastat_product/tests/common.py @@ -99,10 +99,10 @@ class IntrastatProductCommon(IntrastatCommon): :return: The Excel file :rtype: bytes """ - report = cls.declaration.with_context( - active_ids=cls.declaration.ids - ).create_xls() - report_name = report.get("report_name") + report = cls.env.ref( + "intrastat_product.intrastat_product_xlsx_report" + ).with_context(active_ids=cls.declaration.ids) + report_name = report.report_name cls.report = cls.report_obj._get_report_from_name(report_name) datas = { "context": { @@ -122,7 +122,7 @@ class IntrastatProductCommon(IntrastatCommon): ).create_xlsx_report(None, datas) return file_data - def check_xls(self, xls, declaration=False): + def check_xls(self, xls): """ Check that the xls content correspond to computation/declaration lines values @@ -133,33 +133,39 @@ class IntrastatProductCommon(IntrastatCommon): :type declaration: bool, optional """ book = xlrd.open_workbook(file_contents=xls) - sheet = book.sheet_by_index(0) # Get the template used to build the Excel file lines template = self.xls_declaration._get_template(self.declaration) # Get the declaration lines or the computation ones - if not declaration: - declaration_lines = self.declaration.computation_line_ids - line_fields = self.declaration._xls_computation_line_fields() - else: - declaration_lines = self.declaration.declaration_line_ids - line_fields = self.declaration._xls_declaration_line_fields() - i = 0 + to_test = [ + ( + book.sheet_by_index(0), + self.declaration.computation_line_ids, + self.declaration._xls_computation_line_fields(), + ), + ( + book.sheet_by_index(1), + self.declaration.declaration_line_ids, + self.declaration._xls_declaration_line_fields(), + ), + ] # Iterate on each row beginning on third one (two headers) - for rx in range(3, sheet.nrows): - line = declaration_lines[i] - row = sheet.row(rx) - j = 0 - dict_compare = dict() - for line_field in line_fields: - column_spec = template.get(line_field) - dict_compare.update( - {row[j].value: column_spec.get("line").get("value")} - ) - j += 1 - for key, value in dict_compare.items(): - value_eval = self.xls_declaration._eval(value, {"line": line}) - self.assertEqual(key, value_eval) - i += 1 + for (sheet, lines, line_fields) in to_test: + i = 0 + for rx in range(3, sheet.nrows): + line = lines[i] + row = sheet.row(rx) + j = 0 + dict_compare = dict() + for line_field in line_fields: + column_spec = template.get(line_field) + dict_compare.update( + {row[j].value: column_spec.get("line").get("value")} + ) + j += 1 + for key, value in dict_compare.items(): + value_eval = self.xls_declaration._eval(value, {"line": line}) + self.assertEqual(key, value_eval) + i += 1 @classmethod def _create_region(cls, vals=None): diff --git a/intrastat_product/tests/test_purchase_order.py b/intrastat_product/tests/test_purchase_order.py index 3ae96de..3d0dc9a 100644 --- a/intrastat_product/tests/test_purchase_order.py +++ b/intrastat_product/tests/test_purchase_order.py @@ -48,14 +48,10 @@ class TestIntrastatProductPurchase(IntrastatPurchaseCommon): self.declaration.done() self._check_line_values(final=True) - # Check the Excel computation file + # Check the Excel file file_data = self._create_xls() self.check_xls(file_data[0]) - # Check the Excel declaration file - file_data = self._create_xls(True) - self.check_xls(file_data[0], True) - class TestIntrastatProductPurchaseCase(TestIntrastatProductPurchase, TransactionCase): """Test Intrastat Purchase""" diff --git a/intrastat_product/tests/test_sale_order.py b/intrastat_product/tests/test_sale_order.py index 58ca39c..d51e7c5 100644 --- a/intrastat_product/tests/test_sale_order.py +++ b/intrastat_product/tests/test_sale_order.py @@ -83,14 +83,10 @@ class TestIntrastatProductSale(IntrastatSaleCommon): self.declaration.done() self._check_line_values(final=True) - # Check the Excel computation file + # Check the Excel file file_data = self._create_xls() self.check_xls(file_data[0]) - # Check the Excel declaration file - file_data = self._create_xls(True) - self.check_xls(file_data[0], True) - class TestIntrastatProductSaleCase(TestIntrastatProductSale, TransactionCase): """Test Intrastat Sale""" diff --git a/intrastat_product/views/intrastat_product_declaration.xml b/intrastat_product/views/intrastat_product_declaration.xml index c2228e7..da4b22b 100644 --- a/intrastat_product/views/intrastat_product_declaration.xml +++ b/intrastat_product/views/intrastat_product_declaration.xml @@ -28,6 +28,11 @@ states="done" confirm="Are you sure you want to go back to draft?" /> +