refactor(setup): make Authorization Control Postgres-valid (ifnull→coalesce, raw SQL→qb)

authorization_control.py used MySQL-only `ifnull()` in its raw rule
lookups (invalid on Postgres) and several raw `frappe.db.sql` selects.

- Replace every `ifnull(...)` with the portable `coalesce(...)` in the
  rule-lookup statements that remain raw (they interpolate dynamic
  conditions and rely on Frappe's Postgres backtick translation).
- Convert the user/role based_on lookups in validate_approving_authority
  and the four value-based lookups in get_value_based_rule to frappe.qb
  (Coalesce, isin, and a fresh Employee-designation subquery per use).

Behaviour is unchanged on MariaDB; the queries now run on Postgres.

Adds a test (no test file existed): a not-authorized case that exercises
the based_on + coalesce rule lookups (run as a non-admin user, since
Administrator implicitly holds every role), and a get_value_based_rule
call that exercises all four query-builder lookups.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Mihir Kandoi
2026-06-21 04:55:59 +05:30
parent 4255059846
commit 52d7f56922
3 changed files with 176 additions and 74 deletions

View File

@@ -34,28 +34,35 @@ def boot_session(bootinfo):
)
# if no company, show a dialog box to create a new company
bootinfo.customer_count = frappe.db.sql("""SELECT count(*) FROM `tabCustomer`""")[0][0]
bootinfo.customer_count = frappe.db.count("Customer")
if not bootinfo.customer_count:
bootinfo.setup_complete = (
frappe.db.sql(
"""SELECT `name`
FROM `tabCompany`
LIMIT 1"""
)
and "Yes"
or "No"
)
bootinfo.setup_complete = "Yes" if frappe.db.get_all("Company", limit=1) else "No"
bootinfo.docs += frappe.db.sql(
"""select name, default_currency, cost_center, default_selling_terms, default_buying_terms,
default_letter_head, default_letter_head_report, default_bank_account, enable_perpetual_inventory, country, exchange_gain_loss_account from `tabCompany`""",
as_dict=1,
update={"doctype": ":Company"},
companies = frappe.get_all(
"Company",
fields=[
"name",
"default_currency",
"cost_center",
"default_selling_terms",
"default_buying_terms",
"default_letter_head",
"default_letter_head_report",
"default_bank_account",
"enable_perpetual_inventory",
"country",
"exchange_gain_loss_account",
],
)
for company in companies:
company.doctype = ":Company"
bootinfo.docs += companies
party_account_types = frappe.db.sql(""" select name, ifnull(account_type, '') from `tabParty Type`""")
bootinfo.party_account_types = frappe._dict(party_account_types)
party_account_types = frappe.get_all("Party Type", fields=["name", "account_type"], as_list=True)
bootinfo.party_account_types = frappe._dict(
(name, account_type or "") for name, account_type in party_account_types
)
fiscal_year = erpnext.accounts.utils.get_fiscal_years(
frappe.utils.nowdate(), company=get_user_default("company"), raise_on_missing=False
)