Skip to content

Commit

Permalink
[IMP] l10n_nl_tax_statement: add XLS export
Browse files Browse the repository at this point in the history
  • Loading branch information
astirpe committed Jun 30, 2023
1 parent 91b7e08 commit 45f4dd5
Show file tree
Hide file tree
Showing 7 changed files with 244 additions and 1 deletion.
1 change: 1 addition & 0 deletions l10n_nl_tax_statement/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from . import models
from . import report
2 changes: 1 addition & 1 deletion l10n_nl_tax_statement/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"license": "AGPL-3",
"author": "Onestein, Odoo Community Association (OCA)",
"website": "https://github.com/OCA/l10n-netherlands",
"depends": ["account", "date_range"],
"depends": ["account", "date_range", "report_xlsx"],
"data": [
"security/ir.model.access.csv",
"security/tax_statement_security_rule.xml",
Expand Down
4 changes: 4 additions & 0 deletions l10n_nl_tax_statement/readme/USAGE.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ Printing a PDF report:

#. If you need to print the report in PDF, open a statement form and click: `Print -> NL Tax Statement`

Exporting a XLS report:

#. If you need to export the report in XLSX, open a statement form and click: `Print -> NL Tax Statement XLS export`

Multicompany fiscal unit:

#. According the Dutch Tax Authority, for all the companies belonging to a
Expand Down
1 change: 1 addition & 0 deletions l10n_nl_tax_statement/report/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import l10n_nl_tax_statement_xlsx
208 changes: 208 additions & 0 deletions l10n_nl_tax_statement/report/l10n_nl_tax_statement_xlsx.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
# Copyright 2023 Onestein (<https://www.onestein.eu>)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import _, models


class NLTaxStatementXlsx(models.AbstractModel):
_name = "report.l10n_nl_tax_statement.report_tax_statement_xlsx"
_description = "NL Tax Statement XLSX report"
_inherit = "report.report_xlsx.abstract"

def generate_xlsx_report(self, workbook, data, objects):
# Initialize common report variables
report_data = {
"workbook": workbook,
"sheet": None, # main sheet which will contains report
"columns": self._get_report_columns(), # columns of the report
"row_pos": None, # row_pos must be incremented at each writing lines
"formats": None,
}

# One sheet per statement
for obj in objects:
# Initialize per-sheet variables
report_name = obj.name
self._define_formats(obj, workbook, report_data)
report_data["sheet"] = workbook.add_worksheet(report_name)
self._set_column_width(report_data)

# Fill report
report_data["row_pos"] = 0
self._write_report_title(report_name, report_data)
self._generate_report_content(obj, report_data)

def _get_report_filters(self, report):
return [
[_("Date from"), report.from_date.strftime("%d/%m/%Y")],
[_("Date to"), report.to_date.strftime("%d/%m/%Y")],
]

def _set_column_width(self, report_data):
"""Set width for all defined columns.
Columns are defined with `_get_report_columns` method.
"""
for position, column in report_data["columns"].items():
report_data["sheet"].set_column(position, position, column["width"])

def _define_formats(self, obj, workbook, report_data):
"""Add cell formats to current workbook.
Those formats can be used on all cell.
Available formats are :
* bold
* header_left
* header_right
* row_odd
* row_pair
* row_amount_odd
* row_amount_pair
"""
color_row_odd = "#FFFFFF"
color_row_pair = "#EEEEEE"
color_row_header = "#FFFFCC"
num_format = "#,##0." + "0" * obj.currency_id.decimal_places

report_data["formats"] = {
"bold": workbook.add_format({"bold": True}),
"header_left": workbook.add_format(
{
"bold": True,
"align": "left",
"border": False,
"bg_color": color_row_header,
}
),
"header_right": workbook.add_format(
{
"bold": True,
"align": "right",
"border": False,
"bg_color": color_row_header,
}
),
"row_odd": workbook.add_format(
{"border": False, "bg_color": color_row_odd}
),
"row_pair": workbook.add_format(
{"border": False, "bg_color": color_row_pair}
),
"row_amount_odd": workbook.add_format(
{"border": False, "bg_color": color_row_odd}
),
"row_amount_pair": workbook.add_format(
{"border": False, "bg_color": color_row_pair}
),
}
report_data["formats"]["row_amount_odd"].set_num_format(num_format)
report_data["formats"]["row_amount_pair"].set_num_format(num_format)

def _get_report_columns(self):
"""Define the report columns used to generate report"""
return {
0: {"header": _("Code"), "field": "code", "width": 5},
1: {"header": _("Name"), "field": "name", "width": 60},
2: {"header": _("Turnover"), "field": "omzet", "width": 14},
3: {"header": _("VAT"), "field": "btw", "width": 14},
}

def _write_report_title(self, title, report_data):
"""Write report title on current line using all defined columns width.
Columns are defined with `_get_report_columns` method.
"""
report_data["sheet"].merge_range(
report_data["row_pos"],
0,
report_data["row_pos"],
len(report_data["columns"]) - 1,
title,
report_data["formats"]["bold"],
)
report_data["row_pos"] += 2

def _write_filters(self, filters, report_data, sep=" "):
"""Write one line per filter, starting on current row"""
for title, value in filters:
report_data["sheet"].write_string(
report_data["row_pos"],
1,
title + sep + value,
)
report_data["row_pos"] += 1
report_data["row_pos"] += 2

def format_line_from_obj(self, line, report_data, is_pair_line):
"""Write statement line on current row"""
is_group = line["is_group"]

Check warning on line 135 in l10n_nl_tax_statement/report/l10n_nl_tax_statement_xlsx.py

View check run for this annotation

Codecov / codecov/patch

l10n_nl_tax_statement/report/l10n_nl_tax_statement_xlsx.py#L135

Added line #L135 was not covered by tests
for col_pos, column in report_data["columns"].items():
value = line[column["field"]]

Check warning on line 137 in l10n_nl_tax_statement/report/l10n_nl_tax_statement_xlsx.py

View check run for this annotation

Codecov / codecov/patch

l10n_nl_tax_statement/report/l10n_nl_tax_statement_xlsx.py#L137

Added line #L137 was not covered by tests

# Write code and name
if column["field"] in ["code", "name"]:
report_format = (

Check warning on line 141 in l10n_nl_tax_statement/report/l10n_nl_tax_statement_xlsx.py

View check run for this annotation

Codecov / codecov/patch

l10n_nl_tax_statement/report/l10n_nl_tax_statement_xlsx.py#L141

Added line #L141 was not covered by tests
report_data["formats"]["row_pair"]
if is_pair_line
else report_data["formats"]["row_odd"]
)
report_format = (

Check warning on line 146 in l10n_nl_tax_statement/report/l10n_nl_tax_statement_xlsx.py

View check run for this annotation

Codecov / codecov/patch

l10n_nl_tax_statement/report/l10n_nl_tax_statement_xlsx.py#L146

Added line #L146 was not covered by tests
report_data["formats"]["header_left"] if is_group else report_format
)

report_data["sheet"].write_string(

Check warning on line 150 in l10n_nl_tax_statement/report/l10n_nl_tax_statement_xlsx.py

View check run for this annotation

Codecov / codecov/patch

l10n_nl_tax_statement/report/l10n_nl_tax_statement_xlsx.py#L150

Added line #L150 was not covered by tests
report_data["row_pos"],
col_pos,
value or "",
report_format,
)

# Write amount values
if column["field"] in ["omzet", "btw"]:
if is_group:
report_data["sheet"].write_string(

Check warning on line 160 in l10n_nl_tax_statement/report/l10n_nl_tax_statement_xlsx.py

View check run for this annotation

Codecov / codecov/patch

l10n_nl_tax_statement/report/l10n_nl_tax_statement_xlsx.py#L160

Added line #L160 was not covered by tests
report_data["row_pos"],
col_pos,
column["header"],
report_data["formats"]["header_right"],
)
else:
to_display = (

Check warning on line 167 in l10n_nl_tax_statement/report/l10n_nl_tax_statement_xlsx.py

View check run for this annotation

Codecov / codecov/patch

l10n_nl_tax_statement/report/l10n_nl_tax_statement_xlsx.py#L167

Added line #L167 was not covered by tests
column["field"] == "omzet" and line.format_omzet is not False
) or (column["field"] == "btw" and line.format_btw is not False)
if to_display:
report_format = (

Check warning on line 171 in l10n_nl_tax_statement/report/l10n_nl_tax_statement_xlsx.py

View check run for this annotation

Codecov / codecov/patch

l10n_nl_tax_statement/report/l10n_nl_tax_statement_xlsx.py#L171

Added line #L171 was not covered by tests
report_data["formats"]["row_amount_pair"]
if is_pair_line
else report_data["formats"]["row_amount_odd"]
)
report_data["sheet"].write_number(

Check warning on line 176 in l10n_nl_tax_statement/report/l10n_nl_tax_statement_xlsx.py

View check run for this annotation

Codecov / codecov/patch

l10n_nl_tax_statement/report/l10n_nl_tax_statement_xlsx.py#L176

Added line #L176 was not covered by tests
report_data["row_pos"], col_pos, float(value), report_format
)
else:
report_format = (

Check warning on line 180 in l10n_nl_tax_statement/report/l10n_nl_tax_statement_xlsx.py

View check run for this annotation

Codecov / codecov/patch

l10n_nl_tax_statement/report/l10n_nl_tax_statement_xlsx.py#L180

Added line #L180 was not covered by tests
report_data["formats"]["row_pair"]
if is_pair_line
else report_data["formats"]["row_odd"]
)
# Nothing to be displayed: empty cell
report_data["sheet"].write_string(

Check warning on line 186 in l10n_nl_tax_statement/report/l10n_nl_tax_statement_xlsx.py

View check run for this annotation

Codecov / codecov/patch

l10n_nl_tax_statement/report/l10n_nl_tax_statement_xlsx.py#L186

Added line #L186 was not covered by tests
report_data["row_pos"],
col_pos,
"",
report_format,
)
report_data["row_pos"] += 1

Check warning on line 192 in l10n_nl_tax_statement/report/l10n_nl_tax_statement_xlsx.py

View check run for this annotation

Codecov / codecov/patch

l10n_nl_tax_statement/report/l10n_nl_tax_statement_xlsx.py#L192

Added line #L192 was not covered by tests

def _generate_report_content(self, obj, report_data):
"""Write statement content"""
# Write filters
filters = self._get_report_filters(obj)
self._write_filters(filters, report_data)

# Case no lines found
if not obj.line_ids:
return

# Write report lines
is_pair_line = 0

Check warning on line 205 in l10n_nl_tax_statement/report/l10n_nl_tax_statement_xlsx.py

View check run for this annotation

Codecov / codecov/patch

l10n_nl_tax_statement/report/l10n_nl_tax_statement_xlsx.py#L205

Added line #L205 was not covered by tests
for line in obj.line_ids:
self.format_line_from_obj(line, report_data, is_pair_line)
is_pair_line = 1 if not is_pair_line and not line.is_group else 0

Check warning on line 208 in l10n_nl_tax_statement/report/l10n_nl_tax_statement_xlsx.py

View check run for this annotation

Codecov / codecov/patch

l10n_nl_tax_statement/report/l10n_nl_tax_statement_xlsx.py#L207-L208

Added lines #L207 - L208 were not covered by tests
12 changes: 12 additions & 0 deletions l10n_nl_tax_statement/tests/test_l10n_nl_vat_statement.py
Original file line number Diff line number Diff line change
Expand Up @@ -567,3 +567,15 @@ def test_20_multicompany(self):

company_ids_full_list = statement_parent._get_company_ids_full_list()
self.assertEqual(len(company_ids_full_list), 3)

def test_21_action_xls(self):
"""Generate XLS report from action"""
report = "l10n_nl_tax_statement.action_report_tax_statement_xls_export"
self.report_action = self.env.ref(report)
self.assertEqual(self.report_action.report_type, "xlsx")
model = self.env["report.%s" % self.report_action["report_name"]].with_context(
active_model="l10n.nl.vat.statement"
)
res = model.create_xlsx_report(self.statement_1.ids, data=None)
self.assertTrue(res[0])
self.assertEqual(res[1], "xlsx")
17 changes: 17 additions & 0 deletions l10n_nl_tax_statement/views/report_tax_statement.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,21 @@
<field name="binding_type">report</field>
<field name="paperformat_id" ref="paperformat_nl_tax_statement" />
</record>

<record id="action_report_tax_statement_xls_export" model="ir.actions.report">
<field name="name">NL Tax Statement XLS export</field>
<field name="model">l10n.nl.vat.statement</field>
<field name="report_type">xlsx</field>
<field
name="report_name"
>l10n_nl_tax_statement.report_tax_statement_xlsx</field>
<field
name="report_file"
>l10n_nl_tax_statement.report_tax_statement_xlsx</field>
<field
name="binding_model_id"
ref="l10n_nl_tax_statement.model_l10n_nl_vat_statement"
/>
<field name="binding_type">report</field>
</record>
</odoo>

0 comments on commit 45f4dd5

Please sign in to comment.