Files
Odoo-18.0-20251222/partner_statement/report/detailed_activity_statement_xlsx.py
Miquel Raïch 693d25345a [IMP] partner_statement: remove "blocked" feature
If someone comes from v17 or older Odoo versions, and they still have "blocked" lines, there are three options for them:

- Develop glue module with another module that contains "blocked" field.
- Don't use this commit.
- Solve the "blocked" lines.

Good luck!
2025-07-25 14:29:19 +02:00

635 lines
25 KiB
Python

# Author: Miquel Raïch
# Copyright 2022 ForgeFlow S.L.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import _, models
from odoo.addons.report_xlsx_helper.report.report_xlsx_format import ( # type: ignore
FORMATS,
)
def copy_format(book, fmt):
properties = [f[4:] for f in dir(fmt) if f[0:4] == "set_"]
dft_fmt = book.add_format()
return book.add_format(
{
k: v
for k, v in fmt.__dict__.items()
if k in properties and dft_fmt.__dict__[k] != v
}
)
class DetailedActivityStatementXslx(models.AbstractModel):
_name = "report.p_s.report_detailed_activity_statement_xlsx"
_description = "Detailed Activity Statement XLSL Report"
_inherit = "report.p_s.report_activity_statement_xlsx"
def _get_report_name(self, report, data=False):
company_id = data.get("company_id", False)
report_name = _("Detailed Activity Statement")
if company_id:
company = self.env["res.company"].browse(company_id)
suffix = f" - {company.name} - {company.currency_id.name}"
report_name = report_name + suffix
return report_name
def _get_currency_subheader_row_data(self, partner, currency, data):
partner_data = data.get("data", {}).get(partner.id, {})
currency_data = partner_data.get("currencies", {}).get(currency.id)
return [
{
"col_pos": 1,
"sheet_func": "write",
"args": (
partner_data.get("prior_day"),
FORMATS["format_tcell_date_left"],
),
},
{
"col_pos": 2,
"sheet_func": "merge_range",
"span": 3,
"args": (_("Initial Balance"), FORMATS["format_tcell_left"]),
},
{
"col_pos": 6,
"sheet_func": "write",
"args": (
currency_data.get("balance_forward"),
FORMATS["current_money_format"],
),
},
]
def _get_currency_line_row_data(self, partner, currency, data, line):
if line.get("reconciled_line") and not line.get("outside-date-rank"):
format_tcell_left = FORMATS["format_tcell_left_reconciled"]
format_tcell_date_left = FORMATS["format_tcell_date_left_reconciled"]
format_distributed = FORMATS["format_distributed_reconciled"]
current_money_format = FORMATS["current_money_format_reconciled"]
elif line.get("reconciled_line") and line.get("outside-date-rank"):
format_tcell_left = FORMATS[
"format_tcell_left_reconciled_outside-date-rank"
]
format_tcell_date_left = FORMATS[
"format_tcell_date_left_reconciled_outside-date-rank"
]
format_distributed = FORMATS[
"format_distributed_reconciled_outside-date-rank"
]
current_money_format = FORMATS[
"current_money_format_reconciled_outside-date-rank"
]
else:
format_tcell_left = FORMATS["format_tcell_left"]
format_tcell_date_left = FORMATS["format_tcell_date_left"]
format_distributed = FORMATS["format_distributed"]
current_money_format = FORMATS["current_money_format"]
name_to_show = (
line.get("name", "") == "/" or not line.get("name", "")
) and line.get("ref", "")
if line.get("name", "") and line.get("name", "") != "/":
if not line.get("ref", ""):
name_to_show = line.get("name", "")
else:
if (line.get("name", "") in line.get("ref", "")) or (
line.get("name", "") == line.get("ref", "")
):
name_to_show = line.get("name", "")
elif line.get("ref", "") not in line.get("name", ""):
name_to_show = line.get("ref", "")
return (
[
{
"col_pos": col_pos,
"sheet_func": "write",
"args": args,
}
for col_pos, args in enumerate(
[
(line.get("move_id", ""), format_tcell_left),
(line.get("date", ""), format_tcell_date_left),
]
)
]
+ [
{
"col_pos": 2,
"sheet_func": "merge_range",
"span": 1,
"args": (name_to_show, format_distributed),
},
]
+ [
{
"col_pos": col_pos,
"sheet_func": "write",
"args": args,
}
for col_pos, args in enumerate(
[
(
line.get("amount", "")
if not line.get("reconciled_line")
else "",
current_money_format,
),
(line.get("applied_amount", ""), current_money_format),
(
line.get("open_amount", "")
if not line.get("reconciled_line")
else "",
current_money_format,
),
],
4,
)
]
)
def _write_currency_lines(self, row_pos, sheet, partner, currency, data):
partner_data = data.get("data", {}).get(partner.id, {})
currency_data = partner_data.get("currencies", {}).get(currency.id)
account_type = data.get("account_type", False)
row_pos += 2
statement_header = data["get_title"](
partner,
is_detailed=True,
account_type=account_type,
starting_date=partner_data.get("start"),
ending_date=partner_data.get("end"),
currency=currency.display_name,
)
sheet.merge_range(
row_pos, 0, row_pos, 6, statement_header, FORMATS["format_left_bold"]
)
row_pos += 1
row_pos = self._write_currency_header(row_pos, sheet, partner, currency, data)
row_pos += 1
row_pos = self._write_currency_subheader(
row_pos, sheet, partner, currency, data
)
for line in currency_data.get("lines"):
row_pos += 1
row_pos = self._write_currency_line(
row_pos, sheet, partner, currency, data, line
)
row_pos += 1
row_pos = self._write_currency_footer(row_pos, sheet, partner, currency, data)
return row_pos
def _get_currency_prior_header_row_data(self, partner, currency, data):
return [
{
"col_pos": col_pos,
"sheet_func": "write",
"args": args,
}
for col_pos, args in enumerate(
[
(_("Reference Number"), FORMATS["format_theader_yellow_center"]),
(_("Date"), FORMATS["format_theader_yellow_center"]),
(_("Due Date"), FORMATS["format_theader_yellow_center"]),
(_("Description"), FORMATS["format_theader_yellow_center"]),
(_("Original"), FORMATS["format_theader_yellow_center"]),
(_("Open Amount"), FORMATS["format_theader_yellow_center"]),
(_("Balance"), FORMATS["format_theader_yellow_center"]),
]
)
]
def _get_currency_prior_line_row_data(self, partner, currency, data, line):
if line.get("reconciled_line"):
format_tcell_left = FORMATS["format_tcell_left_reconciled"]
format_tcell_date_left = FORMATS["format_tcell_date_left_reconciled"]
format_distributed = FORMATS["format_distributed_reconciled"]
current_money_format = FORMATS["current_money_format_reconciled"]
else:
format_tcell_left = FORMATS["format_tcell_left"]
format_tcell_date_left = FORMATS["format_tcell_date_left"]
format_distributed = FORMATS["format_distributed"]
current_money_format = FORMATS["current_money_format"]
name_to_show = (
line.get("name", "") == "/" or not line.get("name", "")
) and line.get("ref", "")
if line.get("name", "") and line.get("name", "") != "/":
if not line.get("ref", ""):
name_to_show = line.get("name", "")
else:
if (line.get("ref", "") in line.get("name", "")) or (
line.get("name", "") == line.get("ref", "")
):
name_to_show = line.get("name", "")
else:
name_to_show = line.get("ref", "")
return [
{
"col_pos": col_pos,
"sheet_func": "write",
"args": args,
}
for col_pos, args in enumerate(
[
(line.get("move_id", ""), format_tcell_left),
(line.get("date", ""), format_tcell_date_left),
(line.get("date_maturity", ""), format_tcell_date_left),
(name_to_show, format_distributed),
(line.get("amount", ""), current_money_format),
(line.get("open_amount", ""), current_money_format),
(line.get("balance", ""), current_money_format),
]
)
]
def _get_currency_footer_prior_row_data(self, partner, currency, data):
partner_data = data.get("data", {}).get(partner.id, {})
currency_data = partner_data.get("currencies", {}).get(currency.id)
return [
{
"col_pos": 1,
"sheet_func": "write",
"args": (
partner_data.get("prior_day"),
FORMATS["format_tcell_date_left"],
),
},
{
"col_pos": 2,
"sheet_func": "merge_range",
"span": 3,
"args": (_("Ending Balance"), FORMATS["format_tcell_left"]),
},
{
"col_pos": 6,
"sheet_func": "write",
"args": (
currency_data.get("balance_forward"),
FORMATS["current_money_format"],
),
},
]
def _write_currency_prior_header(self, row_pos, sheet, partner, currency, data):
row_data = self._get_currency_prior_header_row_data(partner, currency, data)
self._write_row_data(sheet, row_pos, row_data)
return row_pos
def _write_currency_prior_line(self, row_pos, sheet, partner, currency, data, line):
row_data = self._get_currency_prior_line_row_data(partner, currency, data, line)
self._write_row_data(sheet, row_pos, row_data)
return row_pos
def _write_currency_prior_footer(self, row_pos, sheet, partner, currency, data):
row_data = self._get_currency_footer_prior_row_data(partner, currency, data)
self._write_row_data(sheet, row_pos, row_data)
return row_pos
def _write_currency_prior_lines(self, row_pos, sheet, partner, currency, data):
partner_data = data.get("data", {}).get(partner.id, {})
currency_data = partner_data.get("currencies", {}).get(currency.id)
account_type = data.get("account_type", False)
row_pos += 2
statement_header = data["get_title"](
partner,
is_detailed=False,
account_type=account_type,
starting_date=partner_data.get("start"),
ending_date=partner_data.get("end"),
currency=currency.display_name,
)
sheet.merge_range(
row_pos,
0,
row_pos,
6,
statement_header,
FORMATS["format_left_bold"],
)
row_pos += 1
row_pos = self._write_currency_prior_header(
row_pos, sheet, partner, currency, data
)
for line in currency_data.get("prior_lines"):
row_pos += 1
row_pos = self._write_currency_prior_line(
row_pos, sheet, partner, currency, data, line
)
row_pos += 1
row_pos = self._write_currency_prior_footer(
row_pos, sheet, partner, currency, data
)
return row_pos
def _get_currency_ending_header_row_data(self, partner, currency, data):
return [
{
"col_pos": col_pos,
"sheet_func": "write",
"args": args,
}
for col_pos, args in enumerate(
[
(_("Reference Number"), FORMATS["format_theader_yellow_center"]),
(_("Date"), FORMATS["format_theader_yellow_center"]),
(_("Due Date"), FORMATS["format_theader_yellow_center"]),
(_("Description"), FORMATS["format_theader_yellow_center"]),
(_("Original"), FORMATS["format_theader_yellow_center"]),
(_("Open Amount"), FORMATS["format_theader_yellow_center"]),
(_("Balance"), FORMATS["format_theader_yellow_center"]),
]
)
]
def _get_currency_ending_line_row_data(self, partner, currency, data, line):
if line.get("reconciled_line"):
format_tcell_left = FORMATS["format_tcell_left_reconciled"]
format_tcell_date_left = FORMATS["format_tcell_date_left_reconciled"]
format_distributed = FORMATS["format_distributed_reconciled"]
current_money_format = FORMATS["current_money_format_reconciled"]
else:
format_tcell_left = FORMATS["format_tcell_left"]
format_tcell_date_left = FORMATS["format_tcell_date_left"]
format_distributed = FORMATS["format_distributed"]
current_money_format = FORMATS["current_money_format"]
name_to_show = (
line.get("name", "") == "/" or not line.get("name", "")
) and line.get("ref", "")
if line.get("name", "") and line.get("name", "") != "/":
if not line.get("ref", ""):
name_to_show = line.get("name", "")
else:
if (line.get("ref", "") in line.get("name", "")) or (
line.get("name", "") == line.get("ref", "")
):
name_to_show = line.get("name", "")
else:
name_to_show = line.get("ref", "")
return [
{
"col_pos": col_pos,
"sheet_func": "write",
"args": args,
}
for col_pos, args in enumerate(
[
(line.get("move_id", ""), format_tcell_left),
(line.get("date", ""), format_tcell_date_left),
(line.get("date_maturity", ""), format_tcell_date_left),
(name_to_show, format_distributed),
(line.get("amount", ""), current_money_format),
(line.get("open_amount", ""), current_money_format),
(line.get("balance", ""), current_money_format),
]
)
]
def _get_currency_footer_ending_row_data(self, partner, currency, data):
partner_data = data.get("data", {}).get(partner.id, {})
currency_data = partner_data.get("currencies", {}).get(currency.id)
return [
{
"col_pos": 1,
"sheet_func": "write",
"args": (partner_data.get("end"), FORMATS["format_tcell_date_left"]),
},
{
"col_pos": 2,
"sheet_func": "merge_range",
"span": 3,
"args": (_("Ending Balance"), FORMATS["format_tcell_left"]),
},
{
"col_pos": 6,
"sheet_func": "write",
"args": (
currency_data.get("amount_due"),
FORMATS["current_money_format"],
),
},
]
def _write_currency_ending_header(self, row_pos, sheet, partner, currency, data):
row_data = self._get_currency_ending_header_row_data(partner, currency, data)
self._write_row_data(sheet, row_pos, row_data)
return row_pos
def _write_currency_ending_line(
self, row_pos, sheet, partner, currency, data, line
):
row_data = self._get_currency_ending_line_row_data(
partner, currency, data, line
)
self._write_row_data(sheet, row_pos, row_data)
return row_pos
def _write_currency_ending_footer(self, row_pos, sheet, partner, currency, data):
row_data = self._get_currency_footer_ending_row_data(partner, currency, data)
self._write_row_data(sheet, row_pos, row_data)
return row_pos
def _write_currency_ending_lines(self, row_pos, sheet, partner, currency, data):
partner_data = data.get("data", {}).get(partner.id, {})
currency_data = partner_data.get("currencies", {}).get(currency.id)
account_type = data.get("account_type", False)
row_pos += 2
statement_header = data["get_title"](
partner,
is_detailed=False,
account_type=account_type,
starting_date=partner_data.get("start"),
ending_date=partner_data.get("end"),
currency=currency.display_name,
)
sheet.merge_range(
row_pos,
0,
row_pos,
6,
statement_header,
FORMATS["format_left_bold"],
)
row_pos += 1
row_pos = self._write_currency_ending_header(
row_pos, sheet, partner, currency, data
)
for line in currency_data.get("ending_lines"):
row_pos += 1
row_pos = self._write_currency_ending_line(
row_pos, sheet, partner, currency, data, line
)
row_pos += 1
row_pos = self._write_currency_ending_footer(
row_pos, sheet, partner, currency, data
)
return row_pos
def _size_columns(self, sheet, data):
for i in range(7):
sheet.set_column(0, i, 20)
def generate_xlsx_report(self, workbook, data, objects):
lang = objects.lang or self.env.user.partner_id.lang
self = self.with_context(lang=lang)
report_model = self.env["report.partner_statement.detailed_activity_statement"]
self._define_formats(workbook)
FORMATS["format_distributed"] = workbook.add_format({"align": "vdistributed"})
company_id = data.get("company_id", False)
if company_id:
company = self.env["res.company"].browse(company_id)
else:
company = self.env.user.company_id
data.update(report_model._get_report_values(data.get("partner_ids"), data))
partners = self.env["res.partner"].browse(data.get("partner_ids"))
sheet = workbook.add_worksheet(_("Detailed Activity Statement"))
sheet.set_landscape()
row_pos = 0
sheet.merge_range(
row_pos,
0,
row_pos,
6,
_("Statement of Account from %s") % (company.display_name,),
FORMATS["format_ws_title"],
)
row_pos += 1
sheet.write(row_pos, 1, _("Date:"), FORMATS["format_theader_yellow_right"])
sheet.write(
row_pos,
2,
data.get("data", {}).get(partners.ids[0], {}).get("today"),
FORMATS["format_date_left"],
)
self._size_columns(sheet, data)
for partner in partners:
invoice_address = data.get(
"get_inv_addr", lambda x: self.env["res.partner"]
)(partner)
row_pos += 3
sheet.write(
row_pos, 1, _("Statement to:"), FORMATS["format_theader_yellow_right"]
)
sheet.merge_range(
row_pos,
2,
row_pos,
3,
invoice_address.display_name,
FORMATS["format_left"],
)
if invoice_address.vat:
sheet.write(
row_pos,
4,
_("VAT:"),
FORMATS["format_theader_yellow_right"],
)
sheet.write(
row_pos,
5,
invoice_address.vat,
FORMATS["format_left"],
)
row_pos += 1
sheet.write(
row_pos, 1, _("Statement from:"), FORMATS["format_theader_yellow_right"]
)
sheet.merge_range(
row_pos,
2,
row_pos,
3,
company.partner_id.display_name,
FORMATS["format_left"],
)
if company.vat:
sheet.write(
row_pos,
4,
_("VAT:"),
FORMATS["format_theader_yellow_right"],
)
sheet.write(
row_pos,
5,
company.vat,
FORMATS["format_left"],
)
partner_data = data.get("data", {}).get(partner.id)
currencies = partner_data.get("currencies", {}).keys()
if currencies:
row_pos += 1
for currency_id in currencies:
currency = self.env["res.currency"].browse(currency_id)
if currency.position == "after":
money_string = (
"#,##0.%s " % ("0" * currency.decimal_places)
+ f"[${currency.symbol}]"
)
elif currency.position == "before":
money_string = f"[${currency.symbol}]" + " #,##0.%s" % (
"0" * currency.decimal_places
)
FORMATS["current_money_format"] = workbook.add_format(
{"align": "right", "num_format": money_string}
)
fc_red = "#DC3545"
FORMATS["format_tcell_left_reconciled"] = copy_format(
workbook, FORMATS["format_tcell_left"]
)
FORMATS["format_tcell_left_reconciled"].set_italic(True)
FORMATS["format_tcell_left_reconciled"].set_font_size(10)
FORMATS["format_tcell_left_reconciled"].set_indent(1)
FORMATS["format_tcell_date_left_reconciled"] = copy_format(
workbook, FORMATS["format_tcell_date_left"]
)
FORMATS["format_tcell_date_left_reconciled"].set_italic(True)
FORMATS["format_tcell_date_left_reconciled"].set_font_size(10)
FORMATS["format_distributed_reconciled"] = copy_format(
workbook, FORMATS["format_distributed"]
)
FORMATS["format_distributed_reconciled"].set_italic(True)
FORMATS["format_distributed_reconciled"].set_font_size(10)
FORMATS["current_money_format_reconciled"] = copy_format(
workbook, FORMATS["current_money_format"]
)
FORMATS["current_money_format_reconciled"].set_italic(True)
FORMATS["current_money_format_reconciled"].set_font_size(10)
FORMATS["format_tcell_left_reconciled_outside-date-rank"] = copy_format(
workbook, FORMATS["format_tcell_left_reconciled"]
)
FORMATS[
"format_tcell_left_reconciled_outside-date-rank"
].set_font_color(fc_red)
FORMATS["format_tcell_date_left_reconciled_outside-date-rank"] = (
copy_format(workbook, FORMATS["format_tcell_date_left_reconciled"])
)
FORMATS[
"format_tcell_date_left_reconciled_outside-date-rank"
].set_font_color(fc_red)
FORMATS["format_distributed_reconciled_outside-date-rank"] = (
copy_format(workbook, FORMATS["format_distributed_reconciled"])
)
FORMATS[
"format_distributed_reconciled_outside-date-rank"
].set_font_color(fc_red)
FORMATS["current_money_format_reconciled_outside-date-rank"] = (
copy_format(workbook, FORMATS["current_money_format_reconciled"])
)
FORMATS[
"current_money_format_reconciled_outside-date-rank"
].set_font_color(fc_red)
row_pos = self._write_currency_prior_lines(
row_pos, sheet, partner, currency, data
)
row_pos = self._write_currency_lines(
row_pos, sheet, partner, currency, data
)
row_pos = self._write_currency_ending_lines(
row_pos, sheet, partner, currency, data
)
row_pos = self._write_currency_buckets(
row_pos, sheet, partner, currency, data
)