[IMP] partner_statement: Allow to exclude accounts
This commit is contained in:
committed by
Miquel Raïch
parent
965ea7d6ff
commit
795f9c60fe
@@ -52,6 +52,9 @@ class ActivityStatement(models.AbstractModel):
|
||||
return title
|
||||
|
||||
def _initial_balance_sql_q1(self, partners, date_start, account_type):
|
||||
excluded_accounts_ids = tuple(
|
||||
self.env.context.get("excluded_accounts_ids", [])
|
||||
) or (-1,)
|
||||
return str(
|
||||
self._cr.mogrify(
|
||||
"""
|
||||
@@ -84,6 +87,7 @@ class ActivityStatement(models.AbstractModel):
|
||||
WHERE l2.date < %(date_start)s
|
||||
) as pc ON pc.credit_move_id = l.id
|
||||
WHERE l.partner_id IN %(partners)s
|
||||
AND aa.id not in %(excluded_accounts_ids)s
|
||||
AND l.date < %(date_start)s AND not l.blocked
|
||||
AND m.state IN ('posted')
|
||||
AND aa.account_type = %(account_type)s
|
||||
@@ -156,6 +160,9 @@ class ActivityStatement(models.AbstractModel):
|
||||
def _display_activity_lines_sql_q1(
|
||||
self, partners, date_start, date_end, account_type
|
||||
):
|
||||
excluded_accounts_ids = tuple(
|
||||
self.env.context.get("excluded_accounts_ids", [])
|
||||
) or (-1,)
|
||||
payment_ref = _("Payment")
|
||||
return str(
|
||||
self._cr.mogrify(
|
||||
@@ -191,6 +198,7 @@ class ActivityStatement(models.AbstractModel):
|
||||
JOIN account_move m ON (l.move_id = m.id)
|
||||
JOIN account_journal aj ON (l.journal_id = aj.id)
|
||||
WHERE l.partner_id IN %(partners)s
|
||||
AND aa.id not in %(excluded_accounts_ids)s
|
||||
AND %(date_start)s <= l.date
|
||||
AND l.date <= %(date_end)s
|
||||
AND m.state IN ('posted')
|
||||
|
||||
@@ -26,6 +26,9 @@ class OutstandingStatement(models.AbstractModel):
|
||||
|
||||
def _display_outstanding_lines_sql_q1(self, partners, date_end, account_type):
|
||||
partners = tuple(partners)
|
||||
excluded_accounts_ids = tuple(
|
||||
self.env.context.get("excluded_accounts_ids", [])
|
||||
) or (-1,)
|
||||
return str(
|
||||
self._cr.mogrify(
|
||||
"""
|
||||
@@ -71,6 +74,7 @@ class OutstandingStatement(models.AbstractModel):
|
||||
WHERE l2.date <= %(date_end)s
|
||||
) as pc ON pc.credit_move_id = l.id
|
||||
WHERE l.partner_id IN %(partners)s
|
||||
AND aa.id not in %(excluded_accounts_ids)s
|
||||
AND (
|
||||
(pd.id IS NOT NULL AND
|
||||
pd.max_date <= %(date_end)s) OR
|
||||
|
||||
@@ -59,6 +59,9 @@ class ReportStatementCommon(models.AbstractModel):
|
||||
return {}
|
||||
|
||||
def _show_buckets_sql_q1(self, partners, date_end, account_type):
|
||||
excluded_accounts_ids = tuple(
|
||||
self.env.context.get("excluded_accounts_ids", [])
|
||||
) or (-1,)
|
||||
return str(
|
||||
self._cr.mogrify(
|
||||
"""
|
||||
@@ -91,18 +94,18 @@ class ReportStatementCommon(models.AbstractModel):
|
||||
WHERE l2.date <= %(date_end)s
|
||||
) as pc ON pc.credit_move_id = l.id
|
||||
WHERE l.partner_id IN %(partners)s
|
||||
AND (
|
||||
(pd.id IS NOT NULL AND
|
||||
pd.max_date <= %(date_end)s) OR
|
||||
(pc.id IS NOT NULL AND
|
||||
pc.max_date <= %(date_end)s) OR
|
||||
(pd.id IS NULL AND pc.id IS NULL)
|
||||
) AND l.date <= %(date_end)s AND not l.blocked
|
||||
AND m.state IN ('posted')
|
||||
AND aa.account_type = %(account_type)s
|
||||
AND aa.id not in %(excluded_accounts_ids)s
|
||||
AND (
|
||||
(pd.id IS NOT NULL AND
|
||||
pd.max_date <= %(date_end)s) OR
|
||||
(pc.id IS NOT NULL AND
|
||||
pc.max_date <= %(date_end)s) OR
|
||||
(pd.id IS NULL AND pc.id IS NULL)
|
||||
) AND l.date <= %(date_end)s AND not l.blocked
|
||||
AND m.state IN ('posted')
|
||||
AND aa.account_type = %(account_type)s
|
||||
GROUP BY l.partner_id, l.currency_id, l.date, l.date_maturity,
|
||||
l.amount_currency, l.balance, l.move_id,
|
||||
l.company_id, l.id
|
||||
l.amount_currency, l.balance, l.move_id, l.company_id, l.id
|
||||
""",
|
||||
locals(),
|
||||
),
|
||||
@@ -358,6 +361,11 @@ class ReportStatementCommon(models.AbstractModel):
|
||||
if isinstance(date_end, str):
|
||||
date_end = datetime.strptime(date_end, DEFAULT_SERVER_DATE_FORMAT).date()
|
||||
account_type = data["account_type"]
|
||||
excluded_accounts_ids = data["excluded_accounts_ids"]
|
||||
if excluded_accounts_ids:
|
||||
self = self.with_context(
|
||||
excluded_accounts_ids=excluded_accounts_ids,
|
||||
)
|
||||
aging_type = data["aging_type"]
|
||||
is_activity = data.get("is_activity")
|
||||
is_detailed = data.get("is_detailed")
|
||||
@@ -583,6 +591,7 @@ class ReportStatementCommon(models.AbstractModel):
|
||||
"company": self.env["res.company"].browse(company_id),
|
||||
"Currencies": currencies,
|
||||
"account_type": account_type,
|
||||
"excluded_accounts_ids": excluded_accounts_ids,
|
||||
"is_detailed": is_detailed,
|
||||
"bucket_labels": bucket_labels,
|
||||
"get_inv_addr": self._get_invoice_address,
|
||||
|
||||
@@ -92,3 +92,59 @@ class TestOutstandingStatement(TransactionCase):
|
||||
self.assertIn(
|
||||
"bucket_labels", report, "There was an error while compiling the report."
|
||||
)
|
||||
|
||||
def test_exclude_accounts(self):
|
||||
"""Accounts can be excluded with a code selector."""
|
||||
# Arrange
|
||||
partners = self.partner1 | self.partner2
|
||||
wizard = self.wiz.with_context(
|
||||
active_ids=partners.ids,
|
||||
).create({})
|
||||
|
||||
# Edit one invoice
|
||||
# including a new account
|
||||
# that will be the only one not excluded
|
||||
partner_invoice = self.env["account.move"].search(
|
||||
[
|
||||
("partner_id", "in", partners.ids),
|
||||
("state", "=", "posted"),
|
||||
],
|
||||
limit=1,
|
||||
)
|
||||
account = partner_invoice.line_ids.account_id.filtered(
|
||||
lambda a: a.account_type == wizard.account_type
|
||||
)
|
||||
copy_account = account.copy()
|
||||
partner_invoice.line_ids.filtered(
|
||||
lambda line: line.account_id == account
|
||||
).account_id = copy_account
|
||||
partner_invoice.line_ids.flush_recordset()
|
||||
wizard_accounts = self.env["account.account"].search(
|
||||
[
|
||||
("id", "!=", copy_account.id),
|
||||
("account_type", "=", wizard.account_type),
|
||||
],
|
||||
)
|
||||
wizard.excluded_accounts_selector = ", ".join(
|
||||
[account.code for account in wizard_accounts]
|
||||
)
|
||||
# pre-condition
|
||||
self.assertTrue(wizard.excluded_accounts_selector)
|
||||
|
||||
# Act
|
||||
data = wizard._prepare_statement()
|
||||
report = self.statement_model._get_report_values(partners.ids, data)
|
||||
|
||||
# Assert
|
||||
# Only the new invoice is shown
|
||||
invoice_partner = partner_invoice.partner_id
|
||||
invoice_partner_data = report["data"][invoice_partner.id]["currencies"]
|
||||
invoice_partner_move_lines = invoice_partner_data[
|
||||
partner_invoice.currency_id.id
|
||||
]["lines"]
|
||||
self.assertEqual(len(invoice_partner_move_lines), 1)
|
||||
self.assertEqual(invoice_partner_move_lines[0]["name"], partner_invoice.name)
|
||||
|
||||
other_partner = partners - invoice_partner
|
||||
other_partner_data = report["data"].get(other_partner.id)
|
||||
self.assertFalse(other_partner_data)
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
from dateutil.relativedelta import relativedelta
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo.osv import expression
|
||||
|
||||
|
||||
class StatementCommon(models.AbstractModel):
|
||||
@@ -38,6 +39,44 @@ class StatementCommon(models.AbstractModel):
|
||||
[("asset_receivable", "Receivable"), ("liability_payable", "Payable")],
|
||||
default="asset_receivable",
|
||||
)
|
||||
excluded_accounts_selector = fields.Char(
|
||||
string="Accounts to exclude",
|
||||
help="Select account codes to be excluded "
|
||||
"with a comma-separated list of expressions like 70%.",
|
||||
)
|
||||
|
||||
@api.model
|
||||
def _get_excluded_accounts_domain(self, selector):
|
||||
"""Convert an account codes selector to a domain to search accounts.
|
||||
|
||||
The selector is a comma-separated list of expressions like 70%.
|
||||
The algorithm is the same as
|
||||
AccountingExpressionProcessor._account_codes_to_domain
|
||||
of `mis_builder` module.
|
||||
"""
|
||||
if not selector:
|
||||
selector = ""
|
||||
domains = []
|
||||
for account_code in selector.split(","):
|
||||
account_code = account_code.strip()
|
||||
if "%" in account_code:
|
||||
domains.append(
|
||||
[
|
||||
("code", "=like", account_code),
|
||||
]
|
||||
)
|
||||
else:
|
||||
domains.append(
|
||||
[
|
||||
("code", "=", account_code),
|
||||
]
|
||||
)
|
||||
return expression.OR(domains)
|
||||
|
||||
def _get_excluded_accounts(self):
|
||||
self.ensure_one()
|
||||
domain = self._get_excluded_accounts_domain(self.excluded_accounts_selector)
|
||||
return self.env["account.account"].search(domain)
|
||||
|
||||
@api.onchange("aging_type")
|
||||
def onchange_aging_type(self):
|
||||
@@ -59,6 +98,7 @@ class StatementCommon(models.AbstractModel):
|
||||
"account_type": self.account_type,
|
||||
"aging_type": self.aging_type,
|
||||
"filter_negative_balances": self.filter_negative_balances,
|
||||
"excluded_accounts_ids": self._get_excluded_accounts().ids,
|
||||
}
|
||||
|
||||
def button_export_html(self):
|
||||
|
||||
@@ -63,6 +63,7 @@
|
||||
/>
|
||||
<label for="account_type" />
|
||||
<field name="account_type" nolabel="1" widget="radio" />
|
||||
<field name="excluded_accounts_selector" />
|
||||
</group>
|
||||
<group name="aging_report">
|
||||
<field name="show_aging_buckets" />
|
||||
|
||||
Reference in New Issue
Block a user