mirror of
https://github.com/OCA/intrastat-extrastat.git
synced 2025-02-16 17:13:41 +02:00
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:
@@ -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",
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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"'
|
||||
)
|
||||
@@ -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):
|
||||
"""
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
21
intrastat_product/report/report.xml
Normal file
21
intrastat_product/report/report.xml
Normal 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>
|
||||
@@ -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):
|
||||
|
||||
@@ -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"""
|
||||
|
||||
@@ -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"""
|
||||
|
||||
@@ -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}"
|
||||
|
||||
Reference in New Issue
Block a user