fix(accounts): apply permission restrictions to opening and closing balances

This commit is contained in:
Nabin Hait
2026-10-22 12:22:25 +05:30
parent 8912bdcfec
commit ab1b5a0358
4 changed files with 41 additions and 5 deletions
@@ -17,7 +17,11 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
get_dimension_with_children,
)
from erpnext.accounts.report.utils import convert_to_presentation_currency, get_currency
from erpnext.accounts.report.utils import (
convert_to_presentation_currency,
get_currency,
has_gl_entry_restrictions,
)
from erpnext.accounts.utils import get_fiscal_year, get_zero_cutoff
@@ -449,6 +453,7 @@ def set_gl_entries_by_account(
ignore_closing_balances = frappe.db.get_single_value(
"Accounts Settings", "ignore_account_closing_balance"
)
ignore_closing_balances = ignore_closing_balances or has_gl_entry_restrictions()
if not from_date and not ignore_closing_balances:
last_period_closing_voucher = frappe.db.get_all(
"Period Closing Voucher",
@@ -18,6 +18,9 @@ class TestGeneralLedger(FrappeTestCase):
from erpnext.tests.permission_test_utils import as_user, make_fenced_user
class SnapshotReached(Exception):
pass
restricted = make_fenced_user(
"gl-snapshot-fenced@example.com", ["Accounts User"], [("Company", "_Test Company")]
)
@@ -29,15 +32,15 @@ class TestGeneralLedger(FrappeTestCase):
as_user(user),
patch.object(module, "execute", return_value="live") as live,
patch(
"frappe.database.duckdb.database.get_latest_sync", side_effect=frappe.ValidationError
),
"frappe.database.duckdb.database.get_latest_sync", side_effect=SnapshotReached
) as sync,
):
try:
result = module.execute_snapshot_report(frappe._dict(company="_Test Company"))
except frappe.ValidationError:
except SnapshotReached:
result = "snapshot"
self.assertEqual(result, "live" if expect_live else "snapshot", f"{report} as {user}")
self.assertEqual(live.called, expect_live)
self.assertEqual((live.called, sync.called), (expect_live, not expect_live))
def setUp(self):
self.company = "_Test Company"
@@ -28,6 +28,28 @@ class TestTrialBalance(FrappeTestCase):
self.fiscal_year = get_fiscal_year(today(), company="Trial Balance Company")[0]
create_accounting_dimension()
def test_opening_balances_follow_user_permissions(self):
from frappe.utils import add_days
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
from erpnext.accounts.utils import get_fiscal_year
from erpnext.tests.permission_test_utils import as_user, make_fenced_user
fiscal_year, year_start, year_end = get_fiscal_year(today(), company="_Test Company")
make_journal_entry("_Test Bank - _TC", "_Test Cash - _TC", 500, posting_date=year_start, submit=True)
filters = frappe._dict(
company="_Test Company",
fiscal_year=fiscal_year,
from_date=add_days(year_start, 5),
to_date=year_end,
)
user = make_fenced_user(
"tb-opening@example.com", ["Accounts User"], [("Account", "_Test Write Off - _TC")]
)
with as_user(user):
total_row = execute(frappe._dict(filters))[1][-1]
self.assertEqual((total_row["opening_debit"], total_row["opening_credit"]), (0, 0))
def test_offsetting_entries_for_accounting_dimensions(self):
"""
Checks if Trial Balance Report is balanced when filtered using a particular Accounting Dimension
@@ -4,8 +4,10 @@
import frappe
from frappe import _
from frappe.desk.reportview import build_match_conditions
from frappe.query_builder.functions import Sum
from frappe.utils import add_days, cstr, flt, formatdate, getdate
from pypika.terms import Bracket, LiteralValue
import erpnext
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
@@ -153,6 +155,7 @@ def get_rootwise_opening_balances(filters, report_type, ignore_is_opening):
ignore_closing_balances = frappe.db.get_single_value(
"Accounts Settings", "ignore_account_closing_balance"
)
ignore_closing_balances = ignore_closing_balances or has_gl_entry_restrictions()
if not ignore_closing_balances:
last_period_closing_voucher = frappe.db.get_all(
@@ -312,6 +315,9 @@ def get_opening_balance(
closing_balance[dimension.fieldname].isin(filters[dimension.fieldname])
)
if match_conditions := build_match_conditions(doctype):
opening_balance = opening_balance.where(Bracket(LiteralValue(match_conditions)))
gle = opening_balance.run(as_dict=1)
if filters and filters.get("presentation_currency"):