mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-22 10:49:59 +00:00
Convert raw `frappe.db.sql` in the Setup, Utilities, Templates and Regional areas to `frappe.qb` / the ORM so the same code runs on MariaDB and Postgres. Behaviour is preserved on MariaDB; the conversions also make these paths valid under Postgres' stricter SQL (GROUP BY, case-sensitivity, reserved words). Conversions of note (behaviour kept identical to the MariaDB original): - email_digest: ToDo ordering replicated with a CASE that mirrors MySQL `field(priority,'High','Medium','Low')` (unknown/NULL -> 0, sorts first), NULL-date-first and a `name` tie-break for a deterministic LIMIT. - company.get_all_transactions_annual_history: the cross-DocType UNION + GROUP BY is replaced by one grouped query per DocType merged with a Counter, so two different DocTypes sharing a transaction_date still collapse into one bucket. - templates/utils.send_message: contact lookup wraps both sides in LOWER() to keep MariaDB's case-insensitive email match on case-sensitive Postgres. - regional/irs_1099 & uae_vat_201: address ranking and emirate aggregation rebuilt with CASE/aggregate selects that satisfy Postgres GROUP BY, with a deterministic tie-break on the LIMIT-1 address lookups. - utilities/product.get_item_codes_by_attributes: numeric attribute values are cast with cstr() so Postgres doesn't reject `varchar = numeric`. Tests (run on both MariaDB and Postgres, --lightmode): - New: company merge test, authorization_rule duplicate-check, youtube report, templates/utils, and utilities/templates page reports (partners, rfq, material_request_info, product, utilities __init__). - Existing suites kept green: company, email_digest, transaction_deletion_record, irs_1099, uae_vat_201. Deferred (tracked separately): - setup/doctype/authorization_control.py still has raw `.format()` SELECTs; left for its own PR. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
58 lines
1.5 KiB
Python
58 lines
1.5 KiB
Python
## temp utility
|
|
|
|
from contextlib import contextmanager
|
|
|
|
import frappe
|
|
from frappe import _
|
|
from frappe.utils import cstr
|
|
|
|
from erpnext.utilities.activation import get_level
|
|
|
|
|
|
def update_doctypes():
|
|
df = frappe.qb.DocType("DocField")
|
|
dt_table = frappe.qb.DocType("DocType")
|
|
for d in (
|
|
frappe.qb.from_(df)
|
|
.inner_join(dt_table)
|
|
.on(df.parent == dt_table.name)
|
|
.select(df.parent, df.fieldname)
|
|
.where(df.fieldname.like("%description%") & (dt_table.istable == 1))
|
|
.run(as_dict=1)
|
|
):
|
|
dt = frappe.get_doc("DocType", d.parent)
|
|
|
|
for f in dt.fields:
|
|
if f.fieldname == d.fieldname and f.fieldtype in ("Text", "Small Text"):
|
|
f.fieldtype = "Text Editor"
|
|
dt.save()
|
|
break
|
|
|
|
|
|
def get_site_info(site_info):
|
|
# called via hook
|
|
company = frappe.db.get_single_value("Global Defaults", "default_company")
|
|
domain = None
|
|
|
|
if not company:
|
|
company = frappe.get_all("Company", order_by="creation asc", pluck="name")
|
|
company = company[0] if company else None
|
|
|
|
if company:
|
|
domain = frappe.get_cached_value("Company", cstr(company), "domain")
|
|
|
|
return {"company": company, "domain": domain, "activation": get_level(site_info)}
|
|
|
|
|
|
@contextmanager
|
|
def payment_app_import_guard():
|
|
marketplace_link = '<a href="https://frappecloud.com/marketplace/apps/payments">Marketplace</a>'
|
|
github_link = '<a href="https://github.com/frappe/payments/">GitHub</a>'
|
|
msg = _("payments app is not installed. Please install it from {} or {}").format(
|
|
marketplace_link, github_link
|
|
)
|
|
try:
|
|
yield
|
|
except ImportError:
|
|
frappe.throw(msg, title=_("Missing Payments App"))
|