diff --git a/partner_statement/report/activity_statement.py b/partner_statement/report/activity_statement.py
index be8fe29d..6bb391b3 100644
--- a/partner_statement/report/activity_statement.py
+++ b/partner_statement/report/activity_statement.py
@@ -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')
diff --git a/partner_statement/report/outstanding_statement.py b/partner_statement/report/outstanding_statement.py
index 987734ed..67b60f88 100644
--- a/partner_statement/report/outstanding_statement.py
+++ b/partner_statement/report/outstanding_statement.py
@@ -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
diff --git a/partner_statement/report/report_statement_common.py b/partner_statement/report/report_statement_common.py
index 6ac95bb0..f70ad9d9 100644
--- a/partner_statement/report/report_statement_common.py
+++ b/partner_statement/report/report_statement_common.py
@@ -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,
diff --git a/partner_statement/tests/test_outstanding_statement.py b/partner_statement/tests/test_outstanding_statement.py
index 582586ba..f68533e6 100644
--- a/partner_statement/tests/test_outstanding_statement.py
+++ b/partner_statement/tests/test_outstanding_statement.py
@@ -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)
diff --git a/partner_statement/wizard/statement_common.py b/partner_statement/wizard/statement_common.py
index 37d23909..77ff6325 100644
--- a/partner_statement/wizard/statement_common.py
+++ b/partner_statement/wizard/statement_common.py
@@ -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):
diff --git a/partner_statement/wizard/statement_wizard.xml b/partner_statement/wizard/statement_wizard.xml
index c740e3d9..b97be724 100644
--- a/partner_statement/wizard/statement_wizard.xml
+++ b/partner_statement/wizard/statement_wizard.xml
@@ -63,6 +63,7 @@
/>
+