[IMP] partner_statement: add Detailed Activity report

pull/967/head
Miquel Raïch 2022-12-16 10:29:17 +01:00
parent d114f43de6
commit 51d9c17b2a
22 changed files with 1498 additions and 327 deletions

View File

@ -7,6 +7,7 @@
"category": "Accounting & Finance",
"summary": "OCA Financial Reports",
"author": "ForgeFlow, Odoo Community Association (OCA)",
"maintainers": ["MiquelRForgeFlow"],
"website": "https://github.com/OCA/account-financial-reporting",
"license": "AGPL-3",
"depends": ["account", "report_xlsx", "report_xlsx_helper"],
@ -15,6 +16,7 @@
"security/statement_security.xml",
"views/activity_statement.xml",
"views/outstanding_statement.xml",
"views/detailed_activity_statement.xml",
"views/assets.xml",
"views/aging_buckets.xml",
"views/res_config_settings.xml",

View File

@ -1,6 +1,6 @@
This module extends the functionality of Invoicing to support the printing of customer and vendor statements.
There are two types of statements, Activity and Outstanding. Aging details can be shown in the reports, expressed in aging buckets,
so the customer or vendor can review how much is open, due or overdue.
There are three types of statements: Activity, Detailed Activity, and Outstanding. Aging details can be shown
in the reports, expressed in aging buckets, so the customer or vendor can review how much is open, due or overdue.
The activity statement provides details of all activity on the partner receivables or payables
between two selected dates. This includes all invoices, refunds and payments.
@ -8,6 +8,9 @@ Any outstanding balance dated prior to the chosen statement period will appear
as a forward balance at the top of the statement. The list is displayed in chronological
order and is split by currencies.
The detailed activity statement is an extension of the previous statement, and intends to explain the transactions
that have happened during the period, also providing with a Prior Balance section and an Ending Balance section.
The outstanding statement provides details of all outstanding partner receivables or payables
up to a particular date. This includes all unpaid invoices, unclaimed refunds and
outstanding payments. The list is displayed in chronological order and is split by currencies.

View File

@ -5,3 +5,8 @@
* [ADD] New features.
* Age by months or days
* Filter negative balances
14.0.2.0.0 (2022-12-16)
~~~~~~~~~~~~~~~~~~~~~~~
* [ADD] Detailed Activity Statement.

View File

@ -1,5 +1,7 @@
from . import report_statement_common
from . import activity_statement
from . import detailed_activity_statement
from . import outstanding_statement
from . import activity_statement_xlsx
from . import detailed_activity_statement_xlsx
from . import outstanting_statement_xlsx

View File

@ -75,11 +75,14 @@ class ActivityStatement(models.AbstractModel):
balance_start[row.pop("partner_id")].append(row)
return balance_start
def _display_lines_sql_q1(self, partners, date_start, date_end, account_type):
def _display_activity_lines_sql_q1(
self, partners, date_start, date_end, account_type
):
return str(
self._cr.mogrify(
"""
SELECT m.name AS move_id, l.partner_id, l.date,
array_agg(l.id ORDER BY l.id) as ids,
CASE WHEN (aj.type IN ('sale', 'purchase'))
THEN l.name
ELSE '/'
@ -92,7 +95,7 @@ class ActivityStatement(models.AbstractModel):
WHEN (aj.type in ('bank', 'cash'))
THEN 'Payment'
ELSE ''
END as ref,
END as case_ref,
l.blocked, l.currency_id, l.company_id,
sum(CASE WHEN (l.currency_id is not null AND l.amount_currency > 0.0)
THEN l.amount_currency
@ -116,37 +119,24 @@ class ActivityStatement(models.AbstractModel):
AND %(date_start)s <= l.date
AND l.date <= %(date_end)s
AND m.state IN ('posted')
GROUP BY l.partner_id, m.name, l.date, l.date_maturity,
CASE WHEN (aj.type IN ('sale', 'purchase'))
THEN l.name
ELSE '/'
END,
CASE
WHEN (aj.type IN ('sale', 'purchase')) AND l.name IS NOT NULL
THEN l.ref
WHEN aj.type IN ('sale', 'purchase') AND l.name IS NULL
THEN m.ref
WHEN (aj.type in ('bank', 'cash'))
THEN 'Payment'
ELSE ''
END,
l.blocked, l.currency_id, l.company_id
GROUP BY l.partner_id, m.name, l.date, l.date_maturity, l.name,
aj.type, case_ref, l.blocked, l.currency_id, l.company_id
""",
locals(),
),
"utf-8",
)
def _display_lines_sql_q2(self, company_id):
def _display_activity_lines_sql_q2(self, sub, company_id):
return str(
self._cr.mogrify(
"""
SELECT Q1.partner_id, Q1.move_id, Q1.date, Q1.date_maturity,
Q1.name, Q1.ref, Q1.debit, Q1.credit,
Q1.debit-Q1.credit as amount, Q1.blocked,
COALESCE(Q1.currency_id, c.currency_id) AS currency_id
FROM Q1
JOIN res_company c ON (c.id = Q1.company_id)
f"""
SELECT {sub}.partner_id, {sub}.move_id, {sub}.date, {sub}.date_maturity,
{sub}.name, {sub}.case_ref as ref, {sub}.debit, {sub}.credit, {sub}.ids,
{sub}.debit-{sub}.credit as amount, {sub}.blocked,
COALESCE({sub}.currency_id, c.currency_id) AS currency_id
FROM {sub}
JOIN res_company c ON (c.id = {sub}.company_id)
WHERE c.id = %(company_id)s
""",
locals(),
@ -166,14 +156,14 @@ class ActivityStatement(models.AbstractModel):
WITH Q1 AS (%s),
Q2 AS (%s)
SELECT partner_id, move_id, date, date_maturity, name, ref, debit,
credit, amount, blocked, currency_id
ids, credit, amount, blocked, currency_id
FROM Q2
ORDER BY date, date_maturity, move_id"""
% (
self._display_lines_sql_q1(
self._display_activity_lines_sql_q1(
partners, date_start, date_end, account_type
),
self._display_lines_sql_q2(company_id),
self._display_activity_lines_sql_q2("Q1", company_id),
)
)
for row in self.env.cr.dictfetchall():

View File

@ -2,11 +2,23 @@
# Copyright 2021 ForgeFlow S.L.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import _, fields, models
from odoo import _, models
from odoo.addons.report_xlsx_helper.report.report_xlsx_format import 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 ActivityStatementXslx(models.AbstractModel):
_name = "report.p_s.report_activity_statement_xlsx"
_description = "Activity Statement XLSL Report"
@ -26,17 +38,14 @@ class ActivityStatementXslx(models.AbstractModel):
currency_data = partner_data.get("currencies", {}).get(currency.id)
account_type = data.get("account_type", False)
row_pos += 2
statement_header = _(
"%sStatement between %s and %s in %s"
% (
account_type == "payable" and _("Supplier ") or "",
partner_data.get("start"),
partner_data.get("end"),
currency.display_name,
)
statement_header = _("%sStatement between %s and %s in %s") % (
account_type == "payable" and _("Supplier ") or "",
partner_data.get("start"),
partner_data.get("end"),
currency.display_name,
)
sheet.merge_range(
row_pos, 0, row_pos, 6, statement_header, FORMATS["format_right_bold"]
row_pos, 0, row_pos, 6, statement_header, FORMATS["format_left_bold"]
)
row_pos += 1
sheet.write(
@ -51,13 +60,11 @@ class ActivityStatementXslx(models.AbstractModel):
_("Description"),
FORMATS["format_theader_yellow_center"],
)
sheet.write(
row_pos, 5, _("Open Amount"), FORMATS["format_theader_yellow_center"]
)
sheet.write(row_pos, 5, _("Amount"), FORMATS["format_theader_yellow_center"])
sheet.write(row_pos, 6, _("Balance"), FORMATS["format_theader_yellow_center"])
row_pos += 1
sheet.write(
row_pos, 1, partner_data.get("start"), FORMATS["format_tcell_date_left"]
row_pos, 1, partner_data.get("prior_day"), FORMATS["format_tcell_date_left"]
)
sheet.merge_range(
row_pos, 2, row_pos, 4, _("Balance Forward"), FORMATS["format_tcell_left"]
@ -68,7 +75,16 @@ class ActivityStatementXslx(models.AbstractModel):
currency_data.get("balance_forward"),
FORMATS["current_money_format"],
)
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"]
for line in currency_data.get("lines"):
if line.get("blocked"):
format_tcell_left = FORMATS["format_tcell_left_blocked"]
format_tcell_date_left = FORMATS["format_tcell_date_left_blocked"]
format_distributed = FORMATS["format_distributed_blocked"]
current_money_format = FORMATS["current_money_format_blocked"]
row_pos += 1
name_to_show = (
line.get("name", "") == "/" or not line.get("name", "")
@ -83,21 +99,11 @@ class ActivityStatementXslx(models.AbstractModel):
name_to_show = line.get("name", "")
elif line.get("ref", "") not in line.get("name", ""):
name_to_show = line.get("ref", "")
sheet.write(
row_pos, 0, line.get("move_id", ""), FORMATS["format_tcell_left"]
)
sheet.write(
row_pos, 1, line.get("date", ""), FORMATS["format_tcell_date_left"]
)
sheet.merge_range(
row_pos, 2, row_pos, 4, name_to_show, FORMATS["format_distributed"]
)
sheet.write(
row_pos, 5, line.get("amount", ""), FORMATS["current_money_format"]
)
sheet.write(
row_pos, 6, line.get("balance", ""), FORMATS["current_money_format"]
)
sheet.write(row_pos, 0, line.get("move_id", ""), format_tcell_left)
sheet.write(row_pos, 1, line.get("date", ""), format_tcell_date_left)
sheet.merge_range(row_pos, 2, row_pos, 4, name_to_show, format_distributed)
sheet.write(row_pos, 5, line.get("amount", ""), current_money_format)
sheet.write(row_pos, 6, line.get("balance", ""), current_money_format)
row_pos += 1
sheet.write(
row_pos, 1, partner_data.get("end"), FORMATS["format_tcell_date_left"]
@ -180,7 +186,7 @@ class ActivityStatementXslx(models.AbstractModel):
)
return row_pos
def _size_columns(self, sheet):
def _size_columns(self, sheet, data):
for i in range(7):
sheet.set_column(0, i, 20)
@ -203,7 +209,7 @@ class ActivityStatementXslx(models.AbstractModel):
0,
row_pos,
6,
_("Statement of Account from %s" % (company.display_name)),
_("Statement of Account from %s") % (company.display_name,),
FORMATS["format_ws_title"],
)
row_pos += 1
@ -211,10 +217,10 @@ class ActivityStatementXslx(models.AbstractModel):
sheet.write(
row_pos,
2,
fields.Date.from_string(data.get("date_end")),
data.get("data", {}).get(partners.ids[0], {}).get("today"),
FORMATS["format_date_left"],
)
self._size_columns(sheet)
self._size_columns(sheet, data)
for partner in partners:
invoice_address = data.get(
"get_inv_addr", lambda x: self.env["res.partner"]
@ -286,6 +292,23 @@ class ActivityStatementXslx(models.AbstractModel):
FORMATS["current_money_format"] = workbook.add_format(
{"align": "right", "num_format": money_string}
)
bg_grey = "#CCCCCC"
FORMATS["format_tcell_left_blocked"] = copy_format(
workbook, FORMATS["format_tcell_left"]
)
FORMATS["format_tcell_left_blocked"].set_bg_color(bg_grey)
FORMATS["format_tcell_date_left_blocked"] = copy_format(
workbook, FORMATS["format_tcell_date_left"]
)
FORMATS["format_tcell_date_left_blocked"].set_bg_color(bg_grey)
FORMATS["format_distributed_blocked"] = copy_format(
workbook, FORMATS["format_distributed"]
)
FORMATS["format_distributed_blocked"].set_bg_color(bg_grey)
FORMATS["current_money_format_blocked"] = copy_format(
workbook, FORMATS["current_money_format"]
)
FORMATS["current_money_format_blocked"].set_bg_color(bg_grey)
row_pos = self._write_currency_lines(
row_pos, sheet, partner, currency, data
)

View File

@ -0,0 +1,149 @@
# Copyright 2022 ForgeFlow, S.L. (https://www.forgeflow.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import models
from .outstanding_statement import OutstandingStatement
class DetailedActivityStatement(models.AbstractModel):
"""Model of Detailed Activity Statement"""
_inherit = "report.partner_statement.activity_statement"
_name = "report.partner_statement.detailed_activity_statement"
_description = "Partner Detailed Activity Statement"
def _get_account_display_prior_lines(
self, company_id, partner_ids, date_start, date_end, account_type
):
return self._get_account_display_lines2(
company_id, partner_ids, date_start, date_end, account_type
)
def _display_activity_reconciled_lines_sql_q1(self, sub):
return str(
self._cr.mogrify(
f"""
SELECT unnest(ids) as id
FROM {sub}
""",
locals(),
),
"utf-8",
)
def _display_activity_reconciled_lines_sql_q2(self, sub, date_end):
return str(
self._cr.mogrify(
f"""
SELECT l.id as rel_id, m.name AS move_id, l.partner_id, l.date, l.name,
l.blocked, l.currency_id, l.company_id, {sub}.id,
CASE WHEN l.ref IS NOT NULL
THEN l.ref
ELSE m.ref
END as ref,
CASE WHEN (l.currency_id is not null AND l.amount_currency > 0.0)
THEN avg(l.amount_currency)
ELSE avg(l.debit)
END as debit,
CASE WHEN (l.currency_id is not null AND l.amount_currency < 0.0)
THEN avg(l.amount_currency * (-1))
ELSE avg(l.credit)
END as credit,
CASE WHEN l.balance > 0.0
THEN sum(coalesce(pc.amount, 0.0))
ELSE -sum(coalesce(pd.amount, 0.0))
END AS open_amount,
CASE WHEN l.balance > 0.0
THEN sum(coalesce(pc.debit_amount_currency, 0.0))
ELSE -sum(coalesce(pd.credit_amount_currency, 0.0))
END AS open_amount_currency,
CASE WHEN l.date_maturity is null
THEN l.date
ELSE l.date_maturity
END as date_maturity
FROM {sub}
LEFT JOIN account_partial_reconcile pd ON (
pd.debit_move_id = {sub}.id AND pd.max_date <= %(date_end)s)
LEFT JOIN account_partial_reconcile pc ON (
pc.credit_move_id = {sub}.id AND pc.max_date <= %(date_end)s)
LEFT JOIN account_move_line l ON (
pd.credit_move_id = l.id OR pc.debit_move_id = l.id)
LEFT JOIN account_move m ON (l.move_id = m.id)
WHERE l.date <= %(date_end)s AND m.state IN ('posted')
GROUP BY l.id, l.partner_id, m.name, l.date, l.date_maturity, l.name,
CASE WHEN l.ref IS NOT NULL
THEN l.ref
ELSE m.ref
END, {sub}.id,
l.blocked, l.currency_id, l.balance, l.amount_currency, l.company_id
""",
locals(),
),
"utf-8",
)
def _get_account_display_reconciled_lines(
self, company_id, partner_ids, date_start, date_end, account_type
):
res = dict(map(lambda x: (x, []), partner_ids))
partners = tuple(partner_ids)
# pylint: disable=E8103
self.env.cr.execute(
"""
WITH Q1 AS (%s),
Q2 AS (%s),
Q3 AS (%s),
Q4 AS (%s),
Q5 AS (%s),
Q6 AS (%s)
SELECT partner_id, currency_id, move_id, date, date_maturity, debit,
credit, amount, open_amount, name, ref, blocked, id
FROM Q6
ORDER BY date, date_maturity, move_id"""
% (
self._display_activity_lines_sql_q1(
partners, date_start, date_end, account_type
),
self._display_activity_lines_sql_q2("Q1", company_id),
self._display_activity_reconciled_lines_sql_q1("Q2"),
self._display_activity_reconciled_lines_sql_q2("Q3", date_end),
self._display_outstanding_lines_sql_q2("Q4"),
self._display_outstanding_lines_sql_q3("Q5", company_id),
)
)
for row in self.env.cr.dictfetchall():
res[row.pop("partner_id")].append(row)
return res
def _get_account_display_ending_lines(
self, company_id, partner_ids, date_start, date_end, account_type
):
return self._get_account_display_lines2(
company_id, partner_ids, date_start, date_end, account_type
)
def _add_currency_prior_line(self, line, currency):
return self._add_currency_line2(line, currency)
def _add_currency_reconciled_line(self, line, currency):
return self._add_currency_line2(line, currency)
def _add_currency_ending_line(self, line, currency):
return self._add_currency_line2(line, currency)
DetailedActivityStatement._get_account_display_lines2 = (
OutstandingStatement._get_account_display_lines
)
DetailedActivityStatement._display_outstanding_lines_sql_q1 = (
OutstandingStatement._display_outstanding_lines_sql_q1
)
DetailedActivityStatement._display_outstanding_lines_sql_q2 = (
OutstandingStatement._display_outstanding_lines_sql_q2
)
DetailedActivityStatement._display_outstanding_lines_sql_q3 = (
OutstandingStatement._display_outstanding_lines_sql_q3
)
DetailedActivityStatement._add_currency_line2 = OutstandingStatement._add_currency_line

View File

@ -0,0 +1,585 @@
# 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 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 = " - {} - {}".format(company.name, company.currency_id.name)
report_name = report_name + suffix
return report_name
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)
show_balance = data.get("show_balance", True)
row_pos += 2
statement_header = _("Detailed %sStatement between %s and %s in %s") % (
account_type == "payable" and _("Supplier ") or "",
partner_data.get("start"),
partner_data.get("end"),
currency.display_name,
)
sheet.merge_range(
row_pos,
0,
row_pos,
7 if show_balance else 6,
statement_header,
FORMATS["format_left_bold"],
)
row_pos += 1
sheet.write(
row_pos, 0, _("Reference Number"), FORMATS["format_theader_yellow_center"]
)
sheet.write(row_pos, 1, _("Date"), FORMATS["format_theader_yellow_center"])
sheet.merge_range(
row_pos,
2,
row_pos,
3,
_("Description"),
FORMATS["format_theader_yellow_center"],
)
sheet.write(row_pos, 4, _("Original"), FORMATS["format_theader_yellow_center"])
sheet.write(
row_pos, 5, _("Applied Amount"), FORMATS["format_theader_yellow_center"]
)
sheet.write(
row_pos, 6, _("Open Amount"), FORMATS["format_theader_yellow_center"]
)
if show_balance:
sheet.write(
row_pos, 7, _("Balance"), FORMATS["format_theader_yellow_center"]
)
row_pos += 1
sheet.write(
row_pos, 1, partner_data.get("prior_day"), FORMATS["format_tcell_date_left"]
)
sheet.merge_range(
row_pos,
2,
row_pos,
6 if show_balance else 5,
_("Initial Balance"),
FORMATS["format_tcell_left"],
)
sheet.write(
row_pos,
7 if show_balance else 6,
currency_data.get("balance_forward"),
FORMATS["current_money_format"],
)
for line in currency_data.get("lines"):
if line.get("blocked") and not line.get("reconciled_line"):
format_tcell_left = FORMATS["format_tcell_left_blocked"]
format_tcell_date_left = FORMATS["format_tcell_date_left_blocked"]
format_distributed = FORMATS["format_distributed_blocked"]
current_money_format = FORMATS["current_money_format_blocked"]
elif line.get("reconciled_line") and not line.get("blocked"):
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("blocked") and line.get("reconciled_line"):
format_tcell_left = FORMATS["format_tcell_left_blocked_reconciled"]
format_tcell_date_left = FORMATS[
"format_tcell_date_left_blocked_reconciled"
]
format_distributed = FORMATS["format_distributed_blocked_reconciled"]
current_money_format = FORMATS[
"current_money_format_blocked_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"]
row_pos += 1
name_to_show = (
line.get("name", "") == "/" or not line.get("name", "")
) and line.get("ref", "")
if 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", "")
sheet.write(row_pos, 0, line.get("move_id", ""), format_tcell_left)
sheet.write(row_pos, 1, line.get("date", ""), format_tcell_date_left)
sheet.merge_range(row_pos, 2, row_pos, 3, name_to_show, format_distributed)
sheet.write(
row_pos,
4,
line.get("amount", "") if not line.get("reconciled_line") else "",
current_money_format,
)
sheet.write(
row_pos, 5, line.get("applied_amount", ""), current_money_format
)
sheet.write(
row_pos,
6,
line.get("open_amount", "") if not line.get("reconciled_line") else "",
current_money_format,
)
if show_balance:
sheet.write(row_pos, 7, line.get("balance", ""), current_money_format)
row_pos += 1
sheet.write(
row_pos, 1, partner_data.get("end"), FORMATS["format_tcell_date_left"]
)
sheet.merge_range(
row_pos,
2,
row_pos,
6 if show_balance else 5,
_("Ending Balance"),
FORMATS["format_tcell_left"],
)
sheet.write(
row_pos,
7 if show_balance else 6,
currency_data.get("amount_due"),
FORMATS["current_money_format"],
)
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)
show_balance = data.get("show_balance", True)
row_pos += 2
statement_header = _("%sStatement up to %s in %s") % (
account_type == "payable" and _("Supplier ") or "",
partner_data.get("prior_day"),
currency.display_name,
)
sheet.merge_range(
row_pos,
0,
row_pos,
7 if show_balance else 6,
statement_header,
FORMATS["format_left_bold"],
)
row_pos += 1
sheet.write(
row_pos, 0, _("Reference Number"), FORMATS["format_theader_yellow_center"]
)
sheet.write(row_pos, 1, _("Date"), FORMATS["format_theader_yellow_center"])
sheet.write(row_pos, 2, _("Due Date"), FORMATS["format_theader_yellow_center"])
sheet.merge_range(
row_pos,
3,
row_pos,
4,
_("Description"),
FORMATS["format_theader_yellow_center"],
)
sheet.write(row_pos, 5, _("Original"), FORMATS["format_theader_yellow_center"])
sheet.write(
row_pos, 6, _("Open Amount"), FORMATS["format_theader_yellow_center"]
)
if show_balance:
sheet.write(
row_pos, 7, _("Balance"), FORMATS["format_theader_yellow_center"]
)
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"]
for line in currency_data.get("prior_lines"):
if line.get("blocked") and not line.get("reconciled_line"):
format_tcell_left = FORMATS["format_tcell_left_blocked"]
format_tcell_date_left = FORMATS["format_tcell_date_left_blocked"]
format_distributed = FORMATS["format_distributed_blocked"]
current_money_format = FORMATS["current_money_format_blocked"]
elif line.get("reconciled_line") and not line.get("blocked"):
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("blocked") and line.get("reconciled_line"):
format_tcell_left = FORMATS["format_tcell_left_blocked_reconciled"]
format_tcell_date_left = FORMATS[
"format_tcell_date_left_blocked_reconciled"
]
format_distributed = FORMATS["format_distributed_blocked_reconciled"]
current_money_format = FORMATS[
"current_money_format_blocked_reconciled"
]
row_pos += 1
name_to_show = (
line.get("name", "") == "/" or not line.get("name", "")
) and line.get("ref", "")
if 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", "")
sheet.write(row_pos, 0, line.get("move_id", ""), format_tcell_left)
sheet.write(row_pos, 1, line.get("date", ""), format_tcell_date_left)
sheet.write(
row_pos,
2,
line.get("date_maturity", ""),
format_tcell_date_left,
)
sheet.merge_range(row_pos, 3, row_pos, 4, name_to_show, format_distributed)
sheet.write(row_pos, 5, line.get("amount", ""), current_money_format)
sheet.write(row_pos, 6, line.get("open_amount", ""), current_money_format)
if show_balance:
sheet.write(row_pos, 7, line.get("balance", ""), current_money_format)
row_pos += 1
sheet.write(
row_pos, 1, partner_data.get("prior_day"), FORMATS["format_tcell_date_left"]
)
sheet.merge_range(
row_pos,
2,
row_pos,
6 if show_balance else 5,
_("Ending Balance"),
FORMATS["format_tcell_left"],
)
sheet.write(
row_pos,
7 if show_balance else 6,
currency_data.get("amount_due"),
FORMATS["current_money_format"],
)
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)
show_balance = data.get("show_balance", True)
row_pos += 2
statement_header = _("%sStatement up to %s in %s") % (
account_type == "payable" and _("Supplier ") or "",
partner_data.get("end"),
currency.display_name,
)
sheet.merge_range(
row_pos,
0,
row_pos,
7 if show_balance else 6,
statement_header,
FORMATS["format_left_bold"],
)
row_pos += 1
sheet.write(
row_pos, 0, _("Reference Number"), FORMATS["format_theader_yellow_center"]
)
sheet.write(row_pos, 1, _("Date"), FORMATS["format_theader_yellow_center"])
sheet.write(row_pos, 2, _("Due Date"), FORMATS["format_theader_yellow_center"])
sheet.merge_range(
row_pos,
3,
row_pos,
4,
_("Description"),
FORMATS["format_theader_yellow_center"],
)
sheet.write(row_pos, 5, _("Original"), FORMATS["format_theader_yellow_center"])
sheet.write(
row_pos, 6, _("Open Amount"), FORMATS["format_theader_yellow_center"]
)
if show_balance:
sheet.write(
row_pos, 7, _("Balance"), FORMATS["format_theader_yellow_center"]
)
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"]
for line in currency_data.get("ending_lines"):
if line.get("blocked") and not line.get("reconciled_line"):
format_tcell_left = FORMATS["format_tcell_left_blocked"]
format_tcell_date_left = FORMATS["format_tcell_date_left_blocked"]
format_distributed = FORMATS["format_distributed_blocked"]
current_money_format = FORMATS["current_money_format_blocked"]
elif line.get("reconciled_line") and not line.get("blocked"):
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("blocked") and line.get("reconciled_line"):
format_tcell_left = FORMATS["format_tcell_left_blocked_reconciled"]
format_tcell_date_left = FORMATS[
"format_tcell_date_left_blocked_reconciled"
]
format_distributed = FORMATS["format_distributed_blocked_reconciled"]
current_money_format = FORMATS[
"current_money_format_blocked_reconciled"
]
row_pos += 1
name_to_show = (
line.get("name", "") == "/" or not line.get("name", "")
) and line.get("ref", "")
if 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", "")
sheet.write(row_pos, 0, line.get("move_id", ""), format_tcell_left)
sheet.write(row_pos, 1, line.get("date", ""), format_tcell_date_left)
sheet.write(
row_pos,
2,
line.get("date_maturity", ""),
format_tcell_date_left,
)
sheet.merge_range(row_pos, 3, row_pos, 4, name_to_show, format_distributed)
sheet.write(row_pos, 5, line.get("amount", ""), current_money_format)
sheet.write(row_pos, 6, line.get("open_amount", ""), current_money_format)
if show_balance:
sheet.write(row_pos, 7, line.get("balance", ""), current_money_format)
row_pos += 1
sheet.write(
row_pos, 1, partner_data.get("end"), FORMATS["format_tcell_date_left"]
)
sheet.merge_range(
row_pos,
2,
row_pos,
6 if show_balance else 5,
_("Ending Balance"),
FORMATS["format_tcell_left"],
)
sheet.write(
row_pos,
7 if show_balance else 6,
currency_data.get("amount_due"),
FORMATS["current_money_format"],
)
return row_pos
def _size_columns(self, sheet, data):
show_balance = data.get("show_balance", True)
for i in range(8 if show_balance else 7):
sheet.set_column(0, i, 20)
def generate_xlsx_report(self, workbook, data, objects):
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))
show_balance = data.get("show_balance", True)
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,
7 if show_balance else 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
) + "[${}]".format(currency.symbol)
elif currency.position == "before":
money_string = "[${}]".format(currency.symbol) + " #,##0.%s" % (
"0" * currency.decimal_places
)
FORMATS["current_money_format"] = workbook.add_format(
{"align": "right", "num_format": money_string}
)
bg_grey = "#CCCCCC"
FORMATS["format_tcell_left_blocked"] = copy_format(
workbook, FORMATS["format_tcell_left"]
)
FORMATS["format_tcell_left_blocked"].set_bg_color(bg_grey)
FORMATS["format_tcell_date_left_blocked"] = copy_format(
workbook, FORMATS["format_tcell_date_left"]
)
FORMATS["format_tcell_date_left_blocked"].set_bg_color(bg_grey)
FORMATS["format_distributed_blocked"] = copy_format(
workbook, FORMATS["format_distributed"]
)
FORMATS["format_distributed_blocked"].set_bg_color(bg_grey)
FORMATS["current_money_format_blocked"] = copy_format(
workbook, FORMATS["current_money_format"]
)
FORMATS["current_money_format_blocked"].set_bg_color(bg_grey)
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_blocked_reconciled"] = copy_format(
workbook, FORMATS["format_tcell_left"]
)
FORMATS["format_tcell_left_blocked_reconciled"].set_bg_color(bg_grey)
FORMATS["format_tcell_left_blocked_reconciled"].set_italic(True)
FORMATS["format_tcell_left_blocked_reconciled"].set_font_size(10)
FORMATS["format_tcell_left_blocked_reconciled"].set_indent(1)
FORMATS["format_tcell_date_left_blocked_reconciled"] = copy_format(
workbook, FORMATS["format_tcell_date_left"]
)
FORMATS["format_tcell_date_left_blocked_reconciled"].set_bg_color(
bg_grey
)
FORMATS["format_tcell_date_left_blocked_reconciled"].set_italic(True)
FORMATS["format_tcell_date_left_blocked_reconciled"].set_font_size(10)
FORMATS["format_distributed_blocked_reconciled"] = copy_format(
workbook, FORMATS["format_distributed"]
)
FORMATS["format_distributed_blocked_reconciled"].set_bg_color(bg_grey)
FORMATS["format_distributed_blocked_reconciled"].set_italic(True)
FORMATS["format_distributed_blocked_reconciled"].set_font_size(10)
FORMATS["current_money_format_blocked_reconciled"] = copy_format(
workbook, FORMATS["current_money_format"]
)
FORMATS["current_money_format_blocked_reconciled"].set_bg_color(bg_grey)
FORMATS["current_money_format_blocked_reconciled"].set_italic(True)
FORMATS["current_money_format_blocked_reconciled"].set_font_size(10)
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
)

View File

@ -12,13 +12,13 @@ class OutstandingStatement(models.AbstractModel):
_name = "report.partner_statement.outstanding_statement"
_description = "Partner Outstanding Statement"
def _display_lines_sql_q1(self, partners, date_end, account_type):
def _display_outstanding_lines_sql_q1(self, partners, date_end, account_type):
partners = tuple(partners)
return str(
self._cr.mogrify(
"""
SELECT l.id, m.name AS move_id, l.partner_id, l.date, l.name,
l.blocked, l.currency_id, l.company_id,
l.blocked, l.currency_id, l.company_id,
CASE WHEN l.ref IS NOT NULL
THEN l.ref
ELSE m.ref
@ -79,36 +79,36 @@ class OutstandingStatement(models.AbstractModel):
"utf-8",
)
def _display_lines_sql_q2(self):
def _display_outstanding_lines_sql_q2(self, sub):
return str(
self._cr.mogrify(
"""
SELECT Q1.partner_id, Q1.currency_id, Q1.move_id,
Q1.date, Q1.date_maturity, Q1.debit, Q1.credit,
Q1.name, Q1.ref, Q1.blocked, Q1.company_id,
CASE WHEN Q1.currency_id is not null
THEN Q1.open_amount_currency
ELSE Q1.open_amount
END as open_amount
FROM Q1
f"""
SELECT {sub}.partner_id, {sub}.currency_id, {sub}.move_id,
{sub}.date, {sub}.date_maturity, {sub}.debit, {sub}.credit,
{sub}.name, {sub}.ref, {sub}.blocked, {sub}.company_id,
CASE WHEN {sub}.currency_id is not null
THEN {sub}.open_amount_currency
ELSE {sub}.open_amount
END as open_amount, {sub}.id
FROM {sub}
""",
locals(),
),
"utf-8",
)
def _display_lines_sql_q3(self, company_id):
def _display_outstanding_lines_sql_q3(self, sub, company_id):
return str(
self._cr.mogrify(
"""
SELECT Q2.partner_id, Q2.move_id, Q2.date, Q2.date_maturity,
Q2.name, Q2.ref, Q2.debit, Q2.credit,
Q2.debit-Q2.credit AS amount, blocked,
COALESCE(Q2.currency_id, c.currency_id) AS currency_id,
Q2.open_amount
FROM Q2
JOIN res_company c ON (c.id = Q2.company_id)
WHERE c.id = %(company_id)s AND Q2.open_amount != 0.0
f"""
SELECT {sub}.partner_id, {sub}.move_id, {sub}.date, {sub}.date_maturity,
{sub}.name, {sub}.ref, {sub}.debit, {sub}.credit,
{sub}.debit-{sub}.credit AS amount, blocked,
COALESCE({sub}.currency_id, c.currency_id) AS currency_id,
{sub}.open_amount, {sub}.id
FROM {sub}
JOIN res_company c ON (c.id = {sub}.company_id)
WHERE c.id = %(company_id)s AND {sub}.open_amount != 0.0
""",
locals(),
),
@ -127,13 +127,15 @@ class OutstandingStatement(models.AbstractModel):
Q2 AS (%s),
Q3 AS (%s)
SELECT partner_id, currency_id, move_id, date, date_maturity, debit,
credit, amount, open_amount, name, ref, blocked
credit, amount, open_amount, name, ref, blocked, id
FROM Q3
ORDER BY date, date_maturity, move_id"""
% (
self._display_lines_sql_q1(partners, date_end, account_type),
self._display_lines_sql_q2(),
self._display_lines_sql_q3(company_id),
self._display_outstanding_lines_sql_q1(
partners, date_end, account_type
),
self._display_outstanding_lines_sql_q2("Q1"),
self._display_outstanding_lines_sql_q3("Q2", company_id),
)
)
for row in self.env.cr.dictfetchall():

View File

@ -2,11 +2,23 @@
# Copyright 2021 ForgeFlow S.L.
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import _, fields, models
from odoo import _, models
from odoo.addons.report_xlsx_helper.report.report_xlsx_format import 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 OutstandingStatementXslx(models.AbstractModel):
_name = "report.p_s.report_outstanding_statement_xlsx"
_description = "Outstanding Statement XLSL Report"
@ -26,16 +38,13 @@ class OutstandingStatementXslx(models.AbstractModel):
currency_data = partner_data.get("currencies", {}).get(currency.id)
account_type = data.get("account_type", False)
row_pos += 2
statement_header = _(
"%sStatement up to %s in %s"
% (
account_type == "payable" and _("Supplier ") or "",
partner_data.get("end"),
currency.display_name,
)
statement_header = _("%sStatement up to %s in %s") % (
account_type == "payable" and _("Supplier ") or "",
partner_data.get("end"),
currency.display_name,
)
sheet.merge_range(
row_pos, 0, row_pos, 6, statement_header, FORMATS["format_right_bold"]
row_pos, 0, row_pos, 6, statement_header, FORMATS["format_left_bold"]
)
row_pos += 1
sheet.write(
@ -51,7 +60,16 @@ class OutstandingStatementXslx(models.AbstractModel):
row_pos, 5, _("Open Amount"), FORMATS["format_theader_yellow_center"]
)
sheet.write(row_pos, 6, _("Balance"), FORMATS["format_theader_yellow_center"])
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"]
for line in currency_data.get("lines"):
if line.get("blocked"):
format_tcell_left = FORMATS["format_tcell_left_blocked"]
format_tcell_date_left = FORMATS["format_tcell_date_left_blocked"]
format_distributed = FORMATS["format_distributed_blocked"]
current_money_format = FORMATS["current_money_format_blocked"]
row_pos += 1
name_to_show = (
line.get("name", "") == "/" or not line.get("name", "")
@ -66,28 +84,18 @@ class OutstandingStatementXslx(models.AbstractModel):
name_to_show = line.get("name", "")
else:
name_to_show = line.get("ref", "")
sheet.write(
row_pos, 0, line.get("move_id", ""), FORMATS["format_tcell_left"]
)
sheet.write(
row_pos, 1, line.get("date", ""), FORMATS["format_tcell_date_left"]
)
sheet.write(row_pos, 0, line.get("move_id", ""), format_tcell_left)
sheet.write(row_pos, 1, line.get("date", ""), format_tcell_date_left)
sheet.write(
row_pos,
2,
line.get("date_maturity", ""),
FORMATS["format_tcell_date_left"],
)
sheet.write(row_pos, 3, name_to_show, FORMATS["format_distributed"])
sheet.write(
row_pos, 4, line.get("amount", ""), FORMATS["current_money_format"]
)
sheet.write(
row_pos, 5, line.get("open_amount", ""), FORMATS["current_money_format"]
)
sheet.write(
row_pos, 6, line.get("balance", ""), FORMATS["current_money_format"]
format_tcell_date_left,
)
sheet.write(row_pos, 3, name_to_show, format_distributed)
sheet.write(row_pos, 4, line.get("amount", ""), current_money_format)
sheet.write(row_pos, 5, line.get("open_amount", ""), current_money_format)
sheet.write(row_pos, 6, line.get("balance", ""), current_money_format)
row_pos += 1
sheet.write(
row_pos, 1, partner_data.get("end"), FORMATS["format_tcell_date_left"]
@ -170,7 +178,7 @@ class OutstandingStatementXslx(models.AbstractModel):
)
return row_pos
def _size_columns(self, sheet):
def _size_columns(self, sheet, data):
for i in range(7):
sheet.set_column(0, i, 20)
@ -193,7 +201,7 @@ class OutstandingStatementXslx(models.AbstractModel):
0,
row_pos,
6,
_("Statement of Account from %s" % (company.display_name)),
_("Statement of Account from %s") % (company.display_name,),
FORMATS["format_ws_title"],
)
row_pos += 1
@ -201,10 +209,10 @@ class OutstandingStatementXslx(models.AbstractModel):
sheet.write(
row_pos,
2,
fields.Date.from_string(data.get("date_end")),
data.get("data", {}).get(partners.ids[0], {}).get("today"),
FORMATS["format_date_left"],
)
self._size_columns(sheet)
self._size_columns(sheet, data)
for partner in partners:
invoice_address = data.get(
"get_inv_addr", lambda x: self.env["res.partner"]
@ -276,6 +284,23 @@ class OutstandingStatementXslx(models.AbstractModel):
FORMATS["current_money_format"] = workbook.add_format(
{"align": "right", "num_format": money_string}
)
bg_grey = "#CCCCCC"
FORMATS["format_tcell_left_blocked"] = copy_format(
workbook, FORMATS["format_tcell_left"]
)
FORMATS["format_tcell_left_blocked"].set_bg_color(bg_grey)
FORMATS["format_tcell_date_left_blocked"] = copy_format(
workbook, FORMATS["format_tcell_date_left"]
)
FORMATS["format_tcell_date_left_blocked"].set_bg_color(bg_grey)
FORMATS["format_distributed_blocked"] = copy_format(
workbook, FORMATS["format_distributed"]
)
FORMATS["format_distributed_blocked"].set_bg_color(bg_grey)
FORMATS["current_money_format_blocked"] = copy_format(
workbook, FORMATS["current_money_format"]
)
FORMATS["current_money_format_blocked"].set_bg_color(bg_grey)
row_pos = self._write_currency_lines(
row_pos, sheet, partner, currency, data
)

View File

@ -34,6 +34,21 @@ class ReportStatementCommon(models.AbstractModel):
):
return {}
def _get_account_display_prior_lines(
self, company_id, partner_ids, date_start, date_end, account_type
):
return {}
def _get_account_display_reconciled_lines(
self, company_id, partner_ids, date_start, date_end, account_type
):
return {}
def _get_account_display_ending_lines(
self, company_id, partner_ids, date_start, date_end, account_type
):
return {}
def _show_buckets_sql_q1(self, partners, date_end, account_type):
return str(
self._cr.mogrify(
@ -273,16 +288,20 @@ class ReportStatementCommon(models.AbstractModel):
_("Total"),
]
def _get_line_currency_defaults(self, currency_id, currencies, balance_forward):
def _get_line_currency_defaults(
self, currency_id, currencies, balance_forward, amount_due
):
if currency_id not in currencies:
# This will only happen if currency is inactive
currencies[currency_id] = self.env["res.currency"].browse(currency_id)
return (
{
"prior_lines": [],
"lines": [],
"ending_lines": [],
"buckets": [],
"balance_forward": balance_forward,
"amount_due": balance_forward,
"amount_due": amount_due,
},
currencies,
)
@ -290,6 +309,15 @@ class ReportStatementCommon(models.AbstractModel):
def _add_currency_line(self, line, currency):
return [line]
def _add_currency_prior_line(self, line, currency):
return [line]
def _add_currency_reconciled_line(self, line, currency):
return [line]
def _add_currency_ending_line(self, line, currency):
return [line]
@api.model
def _get_report_values(self, docids, data=None):
# flake8: noqa: C901
@ -325,6 +353,9 @@ class ReportStatementCommon(models.AbstractModel):
date_end = datetime.strptime(date_end, DEFAULT_SERVER_DATE_FORMAT).date()
account_type = data["account_type"]
aging_type = data["aging_type"]
show_balance = data.get("show_balance", True)
is_detailed = data.get("is_detailed") and data.get("is_activity")
# because detailed outstanding doesn't exist (yet)
today = fields.Date.today()
amount_field = data.get("amount_field", "amount")
@ -344,9 +375,31 @@ class ReportStatementCommon(models.AbstractModel):
res = {}
# get base data
prior_day = date_start - timedelta(days=1) if date_start else None
prior_lines = (
self._get_account_display_prior_lines(
company_id, partner_ids, prior_day, prior_day, account_type
)
if is_detailed
else {}
)
lines = self._get_account_display_lines(
company_id, partner_ids, date_start, date_end, account_type
)
ending_lines = (
self._get_account_display_ending_lines(
company_id, partner_ids, date_start, date_end, account_type
)
if is_detailed
else {}
)
reconciled_lines = (
self._get_account_display_reconciled_lines(
company_id, partner_ids, date_start, date_end, account_type
)
if is_detailed
else {}
)
balances_forward = self._get_account_initial_balance(
company_id, partner_ids, date_start, account_type
)
@ -369,6 +422,9 @@ class ReportStatementCommon(models.AbstractModel):
date_start, date_formats.get(partner_id, default_fmt)
),
"end": format_date(date_end, date_formats.get(partner_id, default_fmt)),
"prior_day": format_date(
prior_day, date_formats.get(partner_id, default_fmt)
),
"currencies": {},
}
currency_dict = res[partner_id]["currencies"]
@ -378,7 +434,32 @@ class ReportStatementCommon(models.AbstractModel):
currency_dict[line["currency_id"]],
currencies,
) = self._get_line_currency_defaults(
line["currency_id"], currencies, line["balance"]
line["currency_id"],
currencies,
line["balance"],
0.0 if is_detailed else line["balance"],
)
for line in prior_lines.get(partner_id, []):
if line["currency_id"] not in currency_dict:
(
currency_dict[line["currency_id"]],
currencies,
) = self._get_line_currency_defaults(
line["currency_id"], currencies, 0.0, 0.0
)
line_currency = currency_dict[line["currency_id"]]
if not line["blocked"]:
line_currency["amount_due"] += line["open_amount"]
line["balance"] = line_currency["amount_due"]
line["date"] = format_date(
line["date"], date_formats.get(partner_id, default_fmt)
)
line["date_maturity"] = format_date(
line["date_maturity"], date_formats.get(partner_id, default_fmt)
)
line_currency["prior_lines"].extend(
self._add_currency_prior_line(line, currencies[line["currency_id"]])
)
for line in lines[partner_id]:
@ -387,7 +468,7 @@ class ReportStatementCommon(models.AbstractModel):
currency_dict[line["currency_id"]],
currencies,
) = self._get_line_currency_defaults(
line["currency_id"], currencies, 0.0
line["currency_id"], currencies, 0.0, 0.0
)
line_currency = currency_dict[line["currency_id"]]
if not line["blocked"]:
@ -399,9 +480,73 @@ class ReportStatementCommon(models.AbstractModel):
line["date_maturity"] = format_date(
line["date_maturity"], date_formats.get(partner_id, default_fmt)
)
line["reconciled_line"] = False
if is_detailed:
line["open_amount"] = 0.0
line_currency["lines"].extend(
self._add_currency_line(line, currencies[line["currency_id"]])
)
for line2 in reconciled_lines.get(partner_id, []):
if line2["id"] in line["ids"]:
line2["date"] = format_date(
line2["date"], date_formats.get(partner_id, default_fmt)
)
line2["date_maturity"] = format_date(
line2["date_maturity"],
date_formats.get(partner_id, default_fmt),
)
line2["reconciled_line"] = True
line2["applied_amount"] = line2["open_amount"]
line_currency["lines"].extend(
self._add_currency_reconciled_line(
line2, currencies[line["currency_id"]]
)
)
for line in ending_lines.get(partner_id, []):
line_currency = currency_dict[line["currency_id"]]
ending_balance = 0.0
for line2 in prior_lines.get(partner_id, []):
if line["id"] == line2["id"]:
if not line["blocked"]:
ending_balance += line["open_amount"]
line["balance"] = ending_balance
line["date"] = format_date(
line["date"], date_formats.get(partner_id, default_fmt)
)
line["date_maturity"] = format_date(
line["date_maturity"],
date_formats.get(partner_id, default_fmt),
)
line_currency["ending_lines"].extend(
self._add_currency_ending_line(
line, currencies[line["currency_id"]]
)
)
for line in ending_lines.get(partner_id, []):
line_currency = currency_dict[line["currency_id"]]
for line2 in lines.get(partner_id, []):
if line2["reconciled_line"]:
continue
if line["id"] == line2["ids"][0]:
same_lines = [
ln
for ln in ending_lines[partner_id]
if ln["id"] in set(line2["ids"])
]
line2["open_amount"] = sum(
ln["open_amount"] for ln in same_lines
)
line_currency["ending_lines"].extend(
self._add_currency_ending_line(
line2, currencies[line["currency_id"]]
)
)
if is_detailed:
for line in lines[partner_id]:
if line["reconciled_line"]:
continue
line["applied_amount"] = line["open_amount"] - line["amount"]
if data["show_aging_buckets"]:
for line in buckets[partner_id]:
@ -410,7 +555,7 @@ class ReportStatementCommon(models.AbstractModel):
currency_dict[line["currency_id"]],
currencies,
) = self._get_line_currency_defaults(
line["currency_id"], currencies, 0.0
line["currency_id"], currencies, 0.0, 0.0
)
line_currency = currency_dict[line["currency_id"]]
line_currency["buckets"] = line
@ -439,6 +584,8 @@ class ReportStatementCommon(models.AbstractModel):
"company": self.env["res.company"].browse(company_id),
"Currencies": currencies,
"account_type": account_type,
"is_detailed": is_detailed,
"show_balance": show_balance,
"bucket_labels": bucket_labels,
"get_inv_addr": self._get_invoice_address,
}

View File

@ -1,3 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_activity_statement_wizard,access_activity_statement_wizard,model_activity_statement_wizard,account.group_account_invoice,1,1,1,0
access_outstanding_statement_wizard,access_outstanding_statement_wizard,model_outstanding_statement_wizard,account.group_account_invoice,1,1,1,0
access_detailed_activity_statement_wizard,access_detailed_activity_statement_wizard,model_detailed_activity_statement_wizard,account.group_account_invoice,1,1,1,0

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_activity_statement_wizard access_activity_statement_wizard model_activity_statement_wizard account.group_account_invoice 1 1 1 0
3 access_outstanding_statement_wizard access_outstanding_statement_wizard model_outstanding_statement_wizard account.group_account_invoice 1 1 1 0
4 access_detailed_activity_statement_wizard access_detailed_activity_statement_wizard model_detailed_activity_statement_wizard account.group_account_invoice 1 1 1 0

View File

@ -20,3 +20,11 @@
background-color: $gray-500 !important;
}
}
.statement-reconciled {
font-size: smaller;
font-style: italic !important;
td:last-child {
font-style: italic !important;
}
}

View File

@ -2,6 +2,131 @@
<!-- Copyright 2018 ForgeFlow, S.L. (https://www.forgeflow.com)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo>
<template id="activity_balance">
<p>
<span t-if="is_detailed">Detailed </span>
<span
t-if="account_type == 'payable'"
>Supplier </span>Statement between <span t-esc="d['start']" /> and <span
t-esc="d['end']"
/> in <span t-esc="display_currency.name" />
</p>
<table class="table table-condensed table-statement">
<thead>
<tr>
<th>Reference number</th>
<th>Date</th>
<th>Description</th>
<th class="amount"><span t-if="is_detailed">Original</span><span
t-if="not is_detailed"
>Amount</span></th>
<th t-if="is_detailed">Applied Amount</th>
<th t-if="is_detailed">Open Amount</th>
<th class="amount" t-if="show_balance">Balance</th>
</tr>
</thead>
<tbody>
<tr>
<td />
<td>
<span t-esc="d['prior_day']" />
</td>
<td t-if="is_detailed">Initial Balance</td>
<td t-else="">Balance Forward</td>
<td t-if="show_balance" />
<td t-if="is_detailed" />
<td t-if="is_detailed" />
<td class="amount">
<span
t-esc="currency['balance_forward']"
t-options="{'widget': 'monetary', 'display_currency': display_currency}"
/>
</td>
</tr>
<tr
t-foreach="currency['lines']"
t-as="line"
t-att-class="('statement-blocked ' if line['blocked'] else '') + ('statement-reconciled' if line['reconciled_line'] else '')"
>
<td>
<span
t-if="line['reconciled_line']"
style="padding-left: 11px;"
/><span t-esc="line['move_id']" />
</td>
<td>
<span t-esc="line['date']" />
</td>
<td>
<t t-if="line['name'] != '/'">
<t t-if="not line['ref'] and line['name']">
<span t-esc="line['name']" />
</t>
<t t-if="line['ref'] and not line['name']">
<span t-esc="line['ref']" />
</t>
<t t-if="line['ref'] and line['name']">
<t
t-if="line['name'] not in line['ref'] or line['name'] == line['ref']"
>
<span t-esc="line['name']" />
</t>
<t t-if="line['ref'] not in line['name']">
<span t-esc="line['ref']" />
</t>
</t>
</t>
<t t-if="line['name'] == '/'">
<span t-esc="line['ref']" />
</t>
</td>
<td class="amount">
<span
t-esc="line['amount']"
t-options="{'widget': 'monetary', 'display_currency': display_currency}"
t-if="not line['reconciled_line']"
/>
</td>
<td class="amount" t-if="is_detailed">
<span
t-esc="line['applied_amount']"
t-options="{'widget': 'monetary', 'display_currency': display_currency}"
/>
</td>
<td class="amount" t-if="is_detailed">
<span
t-esc="line['open_amount']"
t-options="{'widget': 'monetary', 'display_currency': display_currency}"
t-if="not line['reconciled_line']"
/>
</td>
<td class="amount" t-if="show_balance">
<span
t-esc="line['balance']"
t-options="{'widget': 'monetary', 'display_currency': display_currency}"
t-if="not line['reconciled_line']"
/>
</td>
</tr>
<tr>
<td />
<td>
<span t-esc="d['end']" />
</td>
<td>Ending Balance</td>
<td t-if="show_balance" />
<td t-if="is_detailed" />
<td t-if="is_detailed" />
<td class="amount">
<span
t-esc="currency['amount_due']"
t-options="{'widget': 'monetary', 'display_currency': display_currency}"
/>
</td>
</tr>
</tbody>
</table>
</template>
<template id="partner_statement.activity_statement_document">
<t t-call="web.external_layout">
<t t-set="o" t-value="o.with_context({'lang': lang})" />
@ -35,109 +160,10 @@
<t t-foreach="d['currencies'].items()" t-as="currency">
<t t-set="display_currency" t-value="Currencies[currency[0]]" />
<t t-set="currency" t-value="currency[1]" />
<p>
<span
t-if="account_type == 'payable'"
>Supplier </span>Statement between <span
t-esc="d['start']"
/> and <span t-esc="d['end']" /> in <span
t-esc="display_currency.name"
/>
</p>
<table class="table table-condensed table-statement">
<thead>
<tr>
<th>Reference number</th>
<th>Date</th>
<th>Description</th>
<th class="amount">Amount</th>
<th class="amount">Balance</th>
</tr>
</thead>
<tbody>
<tr>
<td />
<td>
<span t-esc="d['start']" />
</td>
<td>
Balance Forward
</td>
<td />
<td class="amount">
<span
t-esc="currency['balance_forward']"
t-options="{'widget': 'monetary', 'display_currency': display_currency}"
/>
</td>
</tr>
<tr
t-foreach="currency['lines']"
t-as="line"
t-att-class="'statement-blocked' if line['blocked'] else ''"
>
<td>
<span t-esc="line['move_id']" />
</td>
<td>
<span t-esc="line['date']" />
</td>
<td>
<t t-if="line['name'] != '/'">
<t t-if="not line['ref'] and line['name']">
<span t-esc="line['name']" />
</t>
<t t-if="line['ref'] and not line['name']">
<span t-esc="line['ref']" />
</t>
<t t-if="line['ref'] and line['name']">
<t
t-if="line['name'] not in line['ref'] or line['name'] == line['ref']"
>
<span t-esc="line['name']" />
</t>
<t
t-if="line['ref'] not in line['name']"
>
<span t-esc="line['ref']" />
</t>
</t>
</t>
<t t-if="line['name'] == '/'">
<span t-esc="line['ref']" />
</t>
</td>
<td class="amount">
<span
t-esc="line['amount']"
t-options="{'widget': 'monetary', 'display_currency': display_currency}"
/>
</td>
<td class="amount">
<span
t-esc="line['balance']"
t-options="{'widget': 'monetary', 'display_currency': display_currency}"
/>
</td>
</tr>
<tr>
<td />
<td>
<span t-esc="d['end']" />
</td>
<td>
Ending Balance
</td>
<td />
<td class="amount">
<span
t-esc="currency['amount_due']"
t-options="{'widget': 'monetary', 'display_currency': display_currency}"
/>
</td>
</tr>
</tbody>
</table>
<t
t-call="partner_statement.activity_balance"
name="activity"
/>
<t
t-call="partner_statement.aging_buckets"
t-if="currency['buckets']"

View File

@ -0,0 +1,112 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2022 ForgeFlow, S.L. (https://www.forgeflow.com)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo>
<template id="detailed_activity_statement_document">
<t t-call="web.external_layout">
<t t-set="o" t-value="o.with_context({'lang': lang})" />
<t t-set="address">
<address
t-esc="get_inv_addr(o)"
t-options='{"widget": "contact", "fields": ["address", "name"], "no_marker": True}'
/>
<div t-if="o.vat" class="mt16"><t
t-esc="company.country_id.vat_label or 'Tax ID'"
/>: <span t-field="o.vat" /></div>
</t>
<div class="page">
<h2>Statement of Account</h2>
<div id="informations" class="row mt32 mb32">
<div class="col-3 bm-2">
<strong>Date:</strong>
<p t-esc="d['today']" class="m-0" />
</div>
<div class="col-3 bm-2">
<strong>Partner Name:</strong>
<p t-field="o.name" class="m-0" />
</div>
<div t-if="o.ref" class="col-3 bm-2">
<strong>Partner Code:</strong>
<p t-field="o.ref" class="m-0" />
</div>
</div>
<t t-if="d['currencies']">
<br />
<t t-foreach="d['currencies'].items()" t-as="currency">
<t t-set="display_currency" t-value="Currencies[currency[0]]" />
<t t-set="currency" t-value="currency[1]" />
<t t-set="line_type" t-value="'prior_lines'" />
<t t-set="title_name" t-value="'Prior Balance'" />
<t
t-set="ending_amount"
t-value="currency['balance_forward']"
/>
<t t-set="ending_date" t-value="d['prior_day']" />
<t
t-call="partner_statement.outstanding_balance"
name="prior_balance"
/>
<t
t-call="partner_statement.activity_balance"
name="detailed_activity"
/>
<t t-set="line_type" t-value="'ending_lines'" />
<t t-set="title_name" t-value="'Ending Balance'" />
<t t-set="ending_amount" t-value="currency['amount_due']" />
<t t-set="ending_date" t-value="d['end']" />
<t
t-call="partner_statement.outstanding_balance"
name="ending_balance"
/>
<t
t-call="partner_statement.aging_buckets"
t-if="currency['buckets']"
/>
</t>
</t>
<p t-if="d.get('no_entries')">
<strong>The partner doesn't have due entries.</strong>
</p>
</div>
</t>
</template>
<template id="detailed_activity_statement">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="o">
<t t-set="d" t-value="data.get(o.id)" />
<t
t-call="partner_statement.detailed_activity_statement_document"
t-lang="o.lang"
/>
</t>
</t>
</template>
<record id="action_print_detailed_activity_statement" model="ir.actions.report">
<field name="name">Detailed Activity Statement</field>
<field name="model">res.partner</field>
<field name="report_name">partner_statement.detailed_activity_statement</field>
<field name="report_type">qweb-pdf</field>
<field name="report_file">partner_statement.detailed_activity_statement</field>
</record>
<record
id="action_print_detailed_activity_statement_html"
model="ir.actions.report"
>
<field name="name">Detailed Activity Statement</field>
<field name="model">res.partner</field>
<field name="report_name">partner_statement.detailed_activity_statement</field>
<field name="report_type">qweb-html</field>
<field name="report_file">partner_statement.detailed_activity_statement</field>
</record>
<record
id="action_print_detailed_activity_statement_xlsx"
model="ir.actions.report"
>
<field name="name">Detailed Activity Statement XLSX</field>
<field name="model">res.partner</field>
<field name="type">ir.actions.report</field>
<field name="report_name">p_s.report_detailed_activity_statement_xlsx</field>
<field name="report_type">xlsx</field>
<field name="report_file">report_detailed_activity_statement</field>
</record>
</odoo>

View File

@ -2,6 +2,102 @@
<!-- Copyright 2018 ForgeFlow, S.L. (https://www.forgeflow.com)
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<odoo>
<template id="outstanding_balance">
<p>
<span t-esc="'' if account_type == 'receivable' else 'Supplier '" /><span
t-esc="title_name"
/> up to <span t-esc="d['end']" /> in <span t-esc="display_currency.name" />
</p>
<table class="table table-sm table-statement">
<thead>
<tr>
<th>Reference number</th>
<th>Date</th>
<th>Due Date</th>
<th>Description</th>
<th class="amount">Original</th>
<th class="amount">Open Amount</th>
<th class="amount" t-if="show_balance">Balance</th>
</tr>
</thead>
<tbody>
<tr
t-foreach="currency[line_type]"
t-as="line"
t-att-class="'statement-blocked' if line['blocked'] else ''"
>
<td>
<span t-esc="line['move_id']" />
</td>
<td>
<span t-esc="line['date']" />
</td>
<td>
<span t-esc="line['date_maturity']" />
</td>
<td>
<t t-if="line['name'] != '/'">
<t t-if="not line['ref']">
<span t-esc="line['name']" />
</t>
<t t-if="line['ref'] and not line['name']">
<span t-esc="line['ref']" />
</t>
<t t-if="line['ref'] and line['name']">
<t
t-if="line['name'] not in line['ref'] or line['name'] == line['ref']"
>
<span t-esc="line['name']" />
</t>
<t t-if="line['ref'] not in line['name']">
<span t-esc="line['ref']" />
</t>
</t>
</t>
<t t-if="line['name'] == '/'">
<span t-esc="line['ref']" />
</t>
</td>
<td class="amount">
<span
t-esc="line['amount']"
t-options="{'widget': 'monetary', 'display_currency': display_currency}"
/>
</td>
<td class="amount">
<span
t-esc="line['open_amount']"
t-options="{'widget': 'monetary', 'display_currency': display_currency}"
/>
</td>
<td class="amount" t-if="show_balance">
<span
t-esc="line['balance']"
t-options="{'widget': 'monetary', 'display_currency': display_currency}"
/>
</td>
</tr>
<tr>
<td />
<td>
<span t-esc="ending_date" />
</td>
<td>
Ending Balance
</td>
<td />
<td />
<td t-if="show_balance" />
<td class="amount">
<span
t-esc="ending_amount"
t-options="{'widget': 'monetary', 'display_currency': display_currency}"
/>
</td>
</tr>
</tbody>
</table>
</template>
<template id="partner_statement.outstanding_statement_document">
<t t-call="web.external_layout">
<t t-set="o" t-value="o.with_context({'lang': lang})" />
@ -36,104 +132,14 @@
<t t-foreach="d['currencies'].items()" t-as="currency">
<t t-set="display_currency" t-value="Currencies[currency[0]]" />
<t t-set="currency" t-value="currency[1]" />
<p>
<span
t-esc="'' if account_type == 'receivable' else 'Supplier '"
/>Statement up to <span t-esc="d['end']" /> in <span
t-esc="display_currency.name"
/>
</p>
<table class="table table-sm table-statement">
<thead>
<tr>
<th>Reference number</th>
<th>Date</th>
<th>Due Date</th>
<th>Description</th>
<th class="amount">Original</th>
<th class="amount">Open Amount</th>
<th class="amount">Balance</th>
</tr>
</thead>
<tbody>
<tr
t-foreach="currency['lines']"
t-as="line"
t-att-class="'statement-blocked' if line['blocked'] else ''"
>
<td>
<span t-esc="line['move_id']" />
</td>
<td>
<span t-esc="line['date']" />
</td>
<td>
<span t-esc="line['date_maturity']" />
</td>
<td>
<t t-if="line['name'] != '/'">
<t t-if="not line['ref']">
<span t-esc="line['name']" />
</t>
<t t-if="line['ref'] and not line['name']">
<span t-esc="line['ref']" />
</t>
<t t-if="line['ref'] and line['name']">
<t
t-if="line['name'] not in line['ref'] or line['name'] == line['ref']"
>
<span t-esc="line['name']" />
</t>
<t
t-if="line['ref'] not in line['name']"
>
<span t-esc="line['ref']" />
</t>
</t>
</t>
<t t-if="line['name'] == '/'">
<span t-esc="line['ref']" />
</t>
</td>
<td class="amount">
<span
t-esc="line['amount']"
t-options="{'widget': 'monetary', 'display_currency': display_currency}"
/>
</td>
<td class="amount">
<span
t-esc="line['open_amount']"
t-options="{'widget': 'monetary', 'display_currency': display_currency}"
/>
</td>
<td class="amount">
<span
t-esc="line['balance']"
t-options="{'widget': 'monetary', 'display_currency': display_currency}"
/>
</td>
</tr>
<tr>
<td />
<td>
<span t-esc="d['end']" />
</td>
<td>
Ending Balance
</td>
<td />
<td />
<td />
<td class="amount">
<span
t-esc="currency['amount_due']"
t-options="{'widget': 'monetary', 'display_currency': display_currency}"
/>
</td>
</tr>
</tbody>
</table>
<t t-set="line_type" t-value="'lines'" />
<t t-set="title_name" t-value="'Statement'" />
<t t-set="ending_amount" t-value="currency['amount_due']" />
<t t-set="ending_date" t-value="d['end']" />
<t
t-call="partner_statement.outstanding_balance"
name="outstanding"
/>
<t
t-call="partner_statement.aging_buckets"
t-if="currency['buckets']"

View File

@ -1,4 +1,5 @@
from . import statement_common
from . import activity_statement_wizard
from . import detailed_activity_statement_wizard
from . import outstanding_statement_wizard
from . import res_config_settings

View File

@ -31,7 +31,12 @@ class ActivityStatementWizard(models.TransientModel):
def _prepare_statement(self):
res = super()._prepare_statement()
res.update({"date_start": self.date_start})
res.update(
{
"date_start": self.date_start,
"is_activity": True,
}
)
return res
def _print_report(self, report_type):

View File

@ -0,0 +1,41 @@
# Copyright 2018 ForgeFlow, S.L. (http://www.forgeflow.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import fields, models
class DetailedActivityStatementWizard(models.TransientModel):
"""Detailed Activity Statement wizard."""
_inherit = "activity.statement.wizard"
_name = "detailed.activity.statement.wizard"
_description = "Detailed Activity Statement Wizard"
show_aging_buckets = fields.Boolean(default=False)
show_balance = fields.Boolean(string="Show Balance column")
def _prepare_statement(self):
res = super()._prepare_statement()
res.update(
{
"show_balance": self.show_balance,
"is_detailed": True,
}
)
return res
def _print_report(self, report_type):
self.ensure_one()
data = self._prepare_statement()
if report_type == "xlsx":
report_name = "p_s.report_detailed_activity_statement_xlsx"
else:
report_name = "partner_statement.detailed_activity_statement"
return (
self.env["ir.actions.report"]
.search(
[("report_name", "=", report_name), ("report_type", "=", report_type)],
limit=1,
)
.report_action(self, data=data)
)

View File

@ -11,6 +11,15 @@ class OutstandingStatementWizard(models.TransientModel):
_inherit = "statement.common.wizard"
_description = "Outstanding Statement Wizard"
def _prepare_statement(self):
res = super()._prepare_statement()
res.update(
{
"is_outstanding": True,
}
)
return res
def _print_report(self, report_type):
self.ensure_one()
data = self._prepare_statement()

View File

@ -5,7 +5,7 @@ class ResConfigSettings(models.TransientModel):
_inherit = "res.config.settings"
group_activity_statement = fields.Boolean(
"Enable OCA Activity Statements",
"Enable OCA Activity & Detailed Activity Statements",
group="account.group_account_invoice",
implied_group="partner_statement.group_activity_statement",
)
@ -55,4 +55,5 @@ class ResConfigSettings(models.TransientModel):
value = self[name]
IrDefault.set("activity.statement.wizard", name[8:], value)
IrDefault.set("outstanding.statement.wizard", name[8:], value)
IrDefault.set("detailed.activity.statement.wizard", name[8:], value)
return super().set_values()

View File

@ -25,6 +25,20 @@
/>
<field name="target">new</field>
</record>
<record
id="detailed_activity_statement_wizard_action"
model="ir.actions.act_window"
>
<field name="name">Partner Detailed Activity Statement</field>
<field name="binding_model_id" ref="base.model_res_partner" />
<field name="res_model">detailed.activity.statement.wizard</field>
<field name="view_mode">form</field>
<field
name="groups_id"
eval="[(4, ref('partner_statement.group_activity_statement'))]"
/>
<field name="target">new</field>
</record>
<!-- wizard view -->
<record id="statement_common_view" model="ir.ui.view">
<field name="name">Statement Common Wizard View</field>
@ -138,4 +152,18 @@
</xpath>
</field>
</record>
<record id="detailed_activity_statement_wizard_view" model="ir.ui.view">
<field name="name">Detailed Activity Statement Wizard</field>
<field name="model">detailed.activity.statement.wizard</field>
<field
name="inherit_id"
ref="partner_statement.activity_statement_wizard_view"
/>
<field name="mode">primary</field>
<field name="arch" type="xml">
<group name="aging_report" position="inside">
<field name="show_balance" />
</group>
</field>
</record>
</odoo>