Files
erpnext/erpnext/startup/boot.py
Mihir Kandoi 52d7f56922 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>
2026-06-21 04:55:59 +05:30

100 lines
3.6 KiB
Python

# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt"
import frappe
from frappe.defaults import get_user_default
from frappe.utils import cint
import erpnext.accounts.utils
def boot_session(bootinfo):
"""boot session - send website info if guest"""
if frappe.session["user"] != "Guest":
update_page_info(bootinfo)
bootinfo.sysdefaults.territory = frappe.get_single_value("Selling Settings", "territory")
bootinfo.sysdefaults.customer_group = frappe.get_single_value("Selling Settings", "customer_group")
bootinfo.sysdefaults.use_legacy_js_reactivity = cint(
frappe.get_single_value("Selling Settings", "use_legacy_js_reactivity")
)
bootinfo.sysdefaults.allow_stale = cint(frappe.get_single_value("Accounts Settings", "allow_stale"))
bootinfo.sysdefaults.over_billing_allowance = frappe.get_single_value(
"Accounts Settings", "over_billing_allowance"
)
bootinfo.sysdefaults.quotation_valid_till = cint(
frappe.db.get_single_value("CRM Settings", "default_valid_till")
)
bootinfo.sysdefaults.allow_sales_order_creation_for_expired_quotation = cint(
frappe.get_single_value("Selling Settings", "allow_sales_order_creation_for_expired_quotation")
)
# if no company, show a dialog box to create a new company
bootinfo.customer_count = frappe.db.count("Customer")
if not bootinfo.customer_count:
bootinfo.setup_complete = "Yes" if frappe.db.get_all("Company", limit=1) else "No"
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.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
)
if fiscal_year:
bootinfo.current_fiscal_year = fiscal_year[0]
bootinfo.sysdefaults.demo_company = frappe.db.get_single_value("Global Defaults", "demo_company")
bootinfo.sysdefaults.default_ageing_range = frappe.db.get_single_value(
"Accounts Settings", "default_ageing_range"
)
bootinfo.sysdefaults.repost_allowed_doctypes = frappe.get_hooks("repost_allowed_doctypes")
def update_page_info(bootinfo):
bootinfo.page_info.update(
{
"Chart of Accounts": {"title": "Chart of Accounts", "route": "Tree/Account"},
"Chart of Cost Centers": {"title": "Chart of Cost Centers", "route": "Tree/Cost Center"},
"Item Group Tree": {"title": "Item Group Tree", "route": "Tree/Item Group"},
"Customer Group Tree": {"title": "Customer Group Tree", "route": "Tree/Customer Group"},
"Territory Tree": {"title": "Territory Tree", "route": "Tree/Territory"},
"Sales Person Tree": {"title": "Sales Person Tree", "route": "Tree/Sales Person"},
}
)
def bootinfo(bootinfo):
if bootinfo.get("user") and bootinfo["user"].get("name"):
bootinfo["user"]["employee"] = ""
frappe.session.data.employee = ""
employee = frappe.db.get_value("Employee", {"user_id": bootinfo["user"]["name"]}, "name")
if employee:
bootinfo["user"]["employee"] = employee
frappe.session.data.employee = employee