Skip to content

Commit

Permalink
Merge PR #394 into 15.0
Browse files Browse the repository at this point in the history
Signed-off-by thomaspaulb
  • Loading branch information
OCA-git-bot committed Oct 6, 2023
2 parents 45a13e3 + 1b8acd1 commit eda4112
Show file tree
Hide file tree
Showing 7 changed files with 265 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"]
for col_pos, column in report_data["columns"].items():
value = line[column["field"]]

# Write code and name
if column["field"] in ["code", "name"]:
report_format = (
report_data["formats"]["row_pair"]
if is_pair_line
else report_data["formats"]["row_odd"]
)
report_format = (
report_data["formats"]["header_left"] if is_group else report_format
)

report_data["sheet"].write_string(
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(
report_data["row_pos"],
col_pos,
column["header"],
report_data["formats"]["header_right"],
)
else:
to_display = (
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 = (
report_data["formats"]["row_amount_pair"]
if is_pair_line
else report_data["formats"]["row_amount_odd"]
)
report_data["sheet"].write_number(
report_data["row_pos"], col_pos, float(value), report_format
)
else:
report_format = (
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(
report_data["row_pos"],
col_pos,
"",
report_format,
)
report_data["row_pos"] += 1

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
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
33 changes: 33 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 @@ -163,6 +163,18 @@ def _create_test_invoice(self):
self.invoice_1 = invoice_form.save()
self.assertEqual(len(self.invoice_1.line_ids), 5)

def _check_export_xls(self, statement):
"""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(statement.ids, data=None)
self.assertTrue(res[0])
self.assertEqual(res[1], "xlsx")

def test_01_onchange(self):
daterange_type = self.env["date.range.type"].create({"name": "Type 1"})
daterange = self.env["date.range"].create(
Expand Down Expand Up @@ -192,6 +204,9 @@ def test_01_onchange(self):
self.assertEqual(statement.unreported_move_from_date, new_date)
self.assertEqual(statement.btw_total, 0.0)

# Export XLS without errors
self._check_export_xls(statement)

def test_02_post_final(self):
# in draft
self.assertEqual(self.statement_1.state, "draft")
Expand Down Expand Up @@ -231,6 +246,9 @@ def test_02_post_final(self):

self.assertEqual(self.statement_1.btw_total, 0.0)

# Export XLS without errors
self._check_export_xls(self.statement_1)

def test_03_reset(self):
self.statement_1.reset()
self.assertEqual(self.statement_1.state, "draft")
Expand All @@ -243,13 +261,19 @@ def test_03_reset(self):
self.assertTrue(line.view_base_lines())
self.assertTrue(line.view_tax_lines())

# Export XLS without errors
self._check_export_xls(self.statement_1)

def test_04_write(self):
self.statement_1.post()
with self.assertRaises(UserError):
self.statement_1.write({"name": "Test Name"})

self.assertEqual(self.statement_1.btw_total, 0.0)

# Export XLS without errors
self._check_export_xls(self.statement_1)

def test_05_unlink_exception(self):
self.statement_1.post()
with self.assertRaises(UserError):
Expand Down Expand Up @@ -288,6 +312,9 @@ def test_09_update_working(self):
self.assertEqual(self.statement_1.btw_total, 21.0)
self.assertEqual(self.statement_1.format_btw_total, "21.00")

# Export XLS without errors
self._check_export_xls(self.statement_1)

def test_10_line_unlink_exception(self):
self.assertEqual(len(self.statement_1.line_ids.ids), 0)
self.assertEqual(self.statement_1.btw_total, 0.0)
Expand Down Expand Up @@ -333,6 +360,9 @@ def test_12_undeclared_invoice(self):
self.assertTrue(line.view_base_lines())
self.assertTrue(line.view_tax_lines())

# Export XLS without errors
self._check_export_xls(self.statement_1)

invoice2 = self.invoice_1.copy()
invoice2.action_post()
statement2 = self.env["l10n.nl.vat.statement"].create({"name": "Statement 2"})
Expand Down Expand Up @@ -362,6 +392,9 @@ def test_12_undeclared_invoice(self):
with self.assertRaises(UserError):
invoice_lines[0].date = fields.Date.today()

# Export XLS without errors
self._check_export_xls(statement2)

def test_13_no_previous_statement_posted(self):
statement2 = self.env["l10n.nl.vat.statement"].create({"name": "Statement 2"})
statement2.statement_update()
Expand Down
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 eda4112

Please sign in to comment.