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.
This commit is contained in:
Alexis de Lattre
2023-02-09 23:03:08 +01:00
parent c6fe5c7611
commit 4c08be7bde
10 changed files with 156 additions and 129 deletions

View File

@@ -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",

View File

@@ -19,19 +19,27 @@
</record>
<record id="intrastat_transaction_22" model="intrastat.transaction">
<field name="code">22</field>
<field name="description">Replacement for returned goods (free of charge)</field>
<field
name="description"
>Replacement for returned goods (free of charge)</field>
</record>
<record id="intrastat_transaction_23" model="intrastat.transaction">
<field name="code">23</field>
<field name="description">Replacement (e.g. under warranty) for goods not being returned</field>
<field
name="description"
>Replacement (e.g. under warranty) for goods not being returned</field>
</record>
<record id="intrastat_transaction_31" model="intrastat.transaction">
<field name="code">31</field>
<field name="description">Movements to/from a warehouse (excluding call-off and consignment stock)</field>
<field
name="description"
>Movements to/from a warehouse (excluding call-off and consignment stock)</field>
</record>
<record id="intrastat_transaction_32" model="intrastat.transaction">
<field name="code">32</field>
<field name="description">Supply for sale on approval or after trial (including call-off and consignment stock)</field>
<field
name="description"
>Supply for sale on approval or after trial (including call-off and consignment stock)</field>
</record>
<record id="intrastat_transaction_33" model="intrastat.transaction">
<field name="code">33</field>
@@ -39,42 +47,60 @@
</record>
<record id="intrastat_transaction_34" model="intrastat.transaction">
<field name="code">34</field>
<field name="description">Transactions involving transfer of ownership without financial compensation</field>
<field
name="description"
>Transactions involving transfer of ownership without financial compensation</field>
</record>
<record id="intrastat_transaction_41" model="intrastat.transaction">
<field name="code">41</field>
<field name="description">Transactions with a view to processing under contract (no change of ownership). Goods expected to return to the initial Member State/country of export</field>
<field
name="description"
>Transactions with a view to processing under contract (no change of ownership). Goods expected to return to the initial Member State/country of export</field>
</record>
<record id="intrastat_transaction_42" model="intrastat.transaction">
<field name="code">42</field>
<field name="description">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</field>
<field
name="description"
>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</field>
</record>
<record id="intrastat_transaction_51" model="intrastat.transaction">
<field name="code">51</field>
<field name="description">Transactions following processing under contract (not involving change of ownership). Goods returning to the initial Member State/country of export.</field>
<field
name="description"
>Transactions following processing under contract (not involving change of ownership). Goods returning to the initial Member State/country of export.</field>
</record>
<record id="intrastat_transaction_52" model="intrastat.transaction">
<field name="code">52</field>
<field name="description">Transactions following processing under contract (not involving change of ownership). Goods not returning to the initial Member State/country of export.</field>
<field
name="description"
>Transactions following processing under contract (not involving change of ownership). Goods not returning to the initial Member State/country of export.</field>
</record>
<!-- intrastat.transaction that start with "6" are
"Particular transactions recorded for national purposes"
so they should be supplied by coutry-specific modules -->
<record id="intrastat_transaction_71" model="intrastat.transaction">
<field name="code">71</field>
<field name="description">Release of goods for free circulation in a Member State with a subsequent export to another Member State</field>
<field
name="description"
>Release of goods for free circulation in a Member State with a subsequent export to another Member State</field>
</record>
<record id="intrastat_transaction_72" model="intrastat.transaction">
<field name="code">72</field>
<field name="description">Transportation of goods from one Member State to another Member State to place the goods under the export procedure</field>
<field
name="description"
>Transportation of goods from one Member State to another Member State to place the goods under the export procedure</field>
</record>
<record id="intrastat_transaction_80" model="intrastat.transaction">
<field name="code">80</field>
<field name="description">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</field>
<field
name="description"
>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</field>
</record>
<record id="intrastat_transaction_91" model="intrastat.transaction">
<field name="code">91</field>
<field name="description">Hire, loan, and operational leasing longer than 24 months</field>
<field
name="description"
>Hire, loan, and operational leasing longer than 24 months</field>
</record>
<record id="intrastat_transaction_99" model="intrastat.transaction">
<field name="code">99</field>

View File

@@ -1,13 +0,0 @@
# Copyright 2020 Akretion France (http://www.akretion.com/)
# @author: Alexis de Lattre <alexis.delattre@akretion.com>
# 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"'
)

View File

@@ -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):
"""

View File

@@ -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)

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
Copyright 2023 Akretion France (http://www.akretion.com/)
@author: Alexis de Lattre <alexis.delattre@akretion.com>
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
-->
<odoo>
<record id="intrastat_product_xlsx_report" model="ir.actions.report">
<field name="name">Excel Export</field>
<field name="model">intrastat.product.declaration</field>
<field name="report_type">xlsx</field>
<field name="report_name">intrastat_product.product_declaration_xls</field>
<field name="report_file">intrastat_product.product_declaration_xls</field>
<field
name="print_report_name"
>'intrastat-%s-%s%s' % (object.year_month, object.declaration_type, object.state == 'draft' and '-draft' or '')</field>
<field name="binding_model_id" ref="model_intrastat_product_declaration" />
</record>
</odoo>

View File

@@ -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):

View File

@@ -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"""

View File

@@ -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"""

View File

@@ -28,6 +28,11 @@
states="done"
confirm="Are you sure you want to go back to draft?"
/>
<button
name="%(intrastat_product.intrastat_product_xlsx_report)d"
type="action"
string="Excel Export"
/>
<field name="state" widget="statusbar" />
</header>
<sheet>
@@ -70,12 +75,6 @@
</group>
<notebook>
<page string="Transactions" name="computation_lines">
<button
name="create_xls"
type="object"
string="Excel Export"
context="{'computation_lines': 1}"
/>
<field
name="computation_line_ids"
context="{'declaration_type': declaration_type, 'reporting_level': reporting_level}"
@@ -83,12 +82,6 @@
/>
</page>
<page string="Declaration Lines" name="declaration_lines">
<button
name="create_xls"
type="object"
string="Excel Export"
context="{'declaration_lines': 1}"
/>
<field
name="declaration_line_ids"
context="{'declaration_type': declaration_type, 'reporting_level': reporting_level}"