perf: Use lazy loaded documents (#48017)

* perf: Use lazy docs for status updaters and similar use cases

* perf: lazy load documents while reposting
This commit is contained in:
Ankush Menat
2025-06-11 15:57:08 +05:30
committed by GitHub
parent 76982fe133
commit d16a6d42a5
17 changed files with 44 additions and 47 deletions

View File

@@ -498,7 +498,7 @@ class PaymentEntry(AccountsController):
def delink_advance_entry_references(self):
for reference in self.references:
if reference.reference_doctype in ("Sales Invoice", "Purchase Invoice"):
doc = frappe.get_doc(reference.reference_doctype, reference.reference_name)
doc = frappe.get_lazy_doc(reference.reference_doctype, reference.reference_name)
doc.delink_advance_entries(self.name)
def set_missing_values(self):
@@ -661,7 +661,7 @@ class PaymentEntry(AccountsController):
if not frappe.db.exists(d.reference_doctype, d.reference_name):
frappe.throw(_("{0} {1} does not exist").format(d.reference_doctype, d.reference_name))
ref_doc = frappe.get_doc(d.reference_doctype, d.reference_name)
ref_doc = frappe.get_lazy_doc(d.reference_doctype, d.reference_name)
if d.reference_doctype != "Journal Entry":
if self.party != ref_doc.get(scrub(self.party_type)):
@@ -1785,7 +1785,7 @@ class PaymentEntry(AccountsController):
)
for d in self.get("references"):
if d.allocated_amount and d.reference_doctype in advance_payment_doctypes:
frappe.get_doc(
frappe.get_lazy_doc(
d.reference_doctype, d.reference_name, for_update=True
).set_total_advance_paid()
@@ -2865,7 +2865,7 @@ def get_reference_details(
):
total_amount = outstanding_amount = exchange_rate = account = None
ref_doc = frappe.get_doc(reference_doctype, reference_name)
ref_doc = frappe.get_lazy_doc(reference_doctype, reference_name)
company_currency = ref_doc.get("company_currency") or erpnext.get_company_currency(ref_doc.company)
# Only applies for Reverse Payment Entries

View File

@@ -749,7 +749,7 @@ class PurchaseInvoice(BuyingController):
self.update_status_updater_args()
self.update_prevdoc_status()
frappe.get_doc("Authorization Control").validate_approving_authority(
frappe.get_cached_doc("Authorization Control").validate_approving_authority(
self.doctype, self.company, self.base_grand_total
)
@@ -1718,7 +1718,7 @@ class PurchaseInvoice(BuyingController):
res = frappe.qb.from_(pj).select(pj.total_purchase_cost).where(pj.name == proj).for_update().run()
current_purchase_cost = res and res[0][0] or 0
# frappe.db.set_value("Project", proj, "total_purchase_cost", current_purchase_cost + value)
project_doc = frappe.get_doc("Project", proj)
project_doc = frappe.get_lazy_doc("Project", proj)
project_doc.total_purchase_cost = current_purchase_cost + value
project_doc.calculate_gross_margin()
project_doc.db_update()
@@ -1790,7 +1790,7 @@ class PurchaseInvoice(BuyingController):
for pr in set(updated_pr):
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_billing_percentage
pr_doc = frappe.get_doc("Purchase Receipt", pr)
pr_doc = frappe.get_lazy_doc("Purchase Receipt", pr)
update_billing_percentage(
pr_doc, update_modified=update_modified, adjust_incoming_rate=adjust_incoming_rate
)
@@ -2046,21 +2046,21 @@ def make_stock_entry(source_name, target_doc=None):
@frappe.whitelist()
def change_release_date(name, release_date=None):
if frappe.db.exists("Purchase Invoice", name):
pi = frappe.get_doc("Purchase Invoice", name)
pi = frappe.get_lazy_doc("Purchase Invoice", name)
pi.db_set("release_date", release_date)
@frappe.whitelist()
def unblock_invoice(name):
if frappe.db.exists("Purchase Invoice", name):
pi = frappe.get_doc("Purchase Invoice", name)
pi = frappe.get_lazy_doc("Purchase Invoice", name)
pi.unblock_invoice()
@frappe.whitelist()
def block_invoice(name, release_date, hold_comment=None):
if frappe.db.exists("Purchase Invoice", name):
pi = frappe.get_doc("Purchase Invoice", name)
pi = frappe.get_lazy_doc("Purchase Invoice", name)
pi.block_invoice(hold_comment, release_date)

View File

@@ -448,7 +448,7 @@ class SalesInvoice(SellingController):
self.validate_pos_paid_amount()
if not self.auto_repeat:
frappe.get_doc("Authorization Control").validate_approving_authority(
frappe.get_cached_doc("Authorization Control").validate_approving_authority(
self.doctype, self.company, self.base_grand_total, self
)

View File

@@ -236,7 +236,7 @@ def get_balance_on(
report_type = ""
if cost_center and report_type == "Profit and Loss":
cc = frappe.get_doc("Cost Center", cost_center)
cc = frappe.get_lazy_doc("Cost Center", cost_center)
if cc.is_group:
cond.append(
f""" exists (
@@ -555,7 +555,7 @@ def reconcile_against_document(
# update advance paid in Advance Receivable/Payable doctypes
if update_advance_paid:
for t, n in update_advance_paid:
frappe.get_doc(t, n).set_total_advance_paid()
frappe.get_lazy_doc(t, n).set_total_advance_paid()
frappe.flags.ignore_party_validation = False
@@ -1467,7 +1467,7 @@ def repost_gle_for_stock_vouchers(
for voucher_type, voucher_no in stock_vouchers_chunk:
existing_gle = gle.get((voucher_type, voucher_no), [])
voucher_obj = frappe.get_doc(voucher_type, voucher_no)
voucher_obj = frappe.get_lazy_doc(voucher_type, voucher_no)
# Some transactions post credit as negative debit, this is handled while posting GLE
# but while comparing we need to make sure it's flipped so comparisons are accurate
expected_gle = toggle_debit_credit_if_negative(voucher_obj.get_gl_entries(warehouse_account))
@@ -1891,7 +1891,7 @@ def update_voucher_outstanding(voucher_type, voucher_no, account, party_type, pa
and voucher_outstanding
):
outstanding = voucher_outstanding[0]
ref_doc = frappe.get_doc(voucher_type, voucher_no)
ref_doc = frappe.get_lazy_doc(voucher_type, voucher_no)
outstanding_amount = flt(
outstanding["outstanding_in_account_currency"], ref_doc.precision("outstanding_amount")
)

View File

@@ -498,7 +498,7 @@ class PurchaseOrder(BuyingController):
self.validate_budget()
self.update_reserved_qty_for_subcontract()
frappe.get_doc("Authorization Control").validate_approving_authority(
frappe.get_cached_doc("Authorization Control").validate_approving_authority(
self.doctype, self.company, self.base_grand_total
)
@@ -594,7 +594,7 @@ class PurchaseOrder(BuyingController):
sales_orders_to_update.append(item.sales_order)
for so_name in sales_orders_to_update:
so = frappe.get_doc("Sales Order", so_name)
so = frappe.get_lazy_doc("Sales Order", so_name)
so.update_delivery_status()
so.set_status(update=True)
so.notify_update()
@@ -725,7 +725,7 @@ def close_or_unclose_purchase_orders(names, status):
names = json.loads(names)
for name in names:
po = frappe.get_doc("Purchase Order", name)
po = frappe.get_lazy_doc("Purchase Order", name)
if po.docstatus == 1:
if status == "Closed":
if po.status not in ("Cancelled", "Closed") and (
@@ -902,7 +902,7 @@ def get_list_context(context=None):
@frappe.whitelist()
def update_status(status, name):
po = frappe.get_doc("Purchase Order", name)
po = frappe.get_lazy_doc("Purchase Order", name)
po.update_status(status)
po.update_delivered_qty_in_sales_order()

View File

@@ -150,10 +150,7 @@ class AccountsController(TransactionBase):
supplier = None
if supplier_name:
supplier = frappe.get_doc(
"Supplier",
supplier_name,
)
supplier = frappe.get_lazy_doc("Supplier", supplier_name)
if supplier and supplier.on_hold:
if (is_buying_invoice and supplier.hold_type in ["All", "Invoices"]) or (
@@ -3918,7 +3915,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
if parent_doctype == "Sales Order":
make_packing_list(parent)
parent.set_gross_profit()
frappe.get_doc("Authorization Control").validate_approving_authority(
frappe.get_cached_doc("Authorization Control").validate_approving_authority(
parent.doctype, parent.company, parent.base_grand_total
)

View File

@@ -154,7 +154,7 @@ class BudgetValidation:
def get_dimensions(self):
self.dimensions = []
for _x in frappe.db.get_all("Accounting Dimension"):
self.dimensions.append(frappe.get_doc("Accounting Dimension", _x.name))
self.dimensions.append(frappe.get_lazy_doc("Accounting Dimension", _x.name))
self.dimensions.extend(
[
{"fieldname": "cost_center", "document_type": "Cost Center"},

View File

@@ -793,7 +793,7 @@ class BuyingController(SubcontractingController):
for po, po_item_rows in po_map.items():
if po and po_item_rows:
po_obj = frappe.get_doc("Purchase Order", po)
po_obj = frappe.get_lazy_doc("Purchase Order", po)
if po_obj.status in ["Closed", "Cancelled"]:
frappe.throw(

View File

@@ -123,7 +123,7 @@ class SellingController(StockController):
def remove_shipping_charge(self):
if self.shipping_rule:
shipping_rule = frappe.get_doc("Shipping Rule", self.shipping_rule)
shipping_rule = frappe.get_last_doc("Shipping Rule", self.shipping_rule)
existing_shipping_charge = self.get(
"taxes",
{
@@ -463,7 +463,7 @@ class SellingController(StockController):
for so, so_item_rows in so_map.items():
if so and so_item_rows:
sales_order = frappe.get_doc("Sales Order", so)
sales_order = frappe.get_lazy_doc("Sales Order", so)
if (sales_order.status == "Closed" and not self.is_return) or sales_order.status in [
"Cancelled"

View File

@@ -559,7 +559,7 @@ class StatusUpdater(Document):
)
if update_data:
target = frappe.get_doc(args["target_parent_dt"], args["name"])
target = frappe.get_lazy_doc(args["target_parent_dt"], args["name"])
target.update(update_data) # status calculus might depend on it
status = target.get_status()
if status.get("status"):
@@ -619,7 +619,7 @@ class StatusUpdater(Document):
per_billed = safe_div(min(ref_doc_qty, billed_qty), ref_doc_qty) * 100
ref_doc = frappe.get_doc(ref_dt, ref_dn)
ref_doc = frappe.get_lazy_doc(ref_dt, ref_dn)
ref_doc.db_set("per_billed", per_billed)

View File

@@ -1444,7 +1444,7 @@ class StockController(AccountsController):
@frappe.whitelist()
def show_accounting_ledger_preview(company, doctype, docname):
filters = frappe._dict(company=company, include_dimensions=1)
doc = frappe.get_doc(doctype, docname)
doc = frappe.get_lazy_doc(doctype, docname)
doc.run_method("before_gl_preview")
gl_columns, gl_data = get_accounting_ledger_preview(doc, filters)
@@ -1457,7 +1457,7 @@ def show_accounting_ledger_preview(company, doctype, docname):
@frappe.whitelist()
def show_stock_ledger_preview(company, doctype, docname):
filters = frappe._dict(company=company)
doc = frappe.get_doc(doctype, docname)
doc = frappe.get_lazy_doc(doctype, docname)
doc.run_method("before_sl_preview")
sl_columns, sl_data = get_stock_ledger_preview(doc, filters)

View File

@@ -284,7 +284,7 @@ class Quotation(SellingController):
def on_submit(self):
# Check for Approving Authority
frappe.get_doc("Authorization Control").validate_approving_authority(
frappe.get_cached_doc("Authorization Control").validate_approving_authority(
self.doctype, self.company, self.base_grand_total, self
)

View File

@@ -435,7 +435,7 @@ class SalesOrder(SellingController):
self.check_credit_limit()
self.update_reserved_qty()
frappe.get_doc("Authorization Control").validate_approving_authority(
frappe.get_cached_doc("Authorization Control").validate_approving_authority(
self.doctype, self.company, self.base_grand_total, self
)
self.update_project()
@@ -487,7 +487,7 @@ class SalesOrder(SellingController):
return
if self.project:
project = frappe.get_doc("Project", self.project)
project = frappe.get_lazy_doc("Project", self.project)
project.update_sales_amount()
project.db_update()
@@ -825,7 +825,7 @@ def close_or_unclose_sales_orders(names, status):
names = json.loads(names)
for name in names:
so = frappe.get_doc("Sales Order", name)
so = frappe.get_lazy_doc("Sales Order", name)
if so.docstatus == 1:
if status == "Closed":
if so.status not in ("Cancelled", "Closed") and (

View File

@@ -639,7 +639,7 @@ class DeliveryNote(SellingController):
updated_delivery_notes += update_billed_amount_based_on_so(d.so_detail, update_modified)
for dn in set(updated_delivery_notes):
dn_doc = self if (dn == self.name) else frappe.get_doc("Delivery Note", dn)
dn_doc = self if (dn == self.name) else frappe.get_lazy_doc("Delivery Note", dn)
dn_doc.update_billing_percentage(update_modified=update_modified)
self.load_from_db()
@@ -1102,7 +1102,7 @@ def make_sales_return(source_name, target_doc=None):
@frappe.whitelist()
def update_delivery_note_status(docname, status):
dn = frappe.get_doc("Delivery Note", docname)
dn = frappe.get_lazy_doc("Delivery Note", docname)
dn.update_status(status)

View File

@@ -365,7 +365,7 @@ class PurchaseReceipt(BuyingController):
super().on_submit()
# Check for Approving Authority
frappe.get_doc("Authorization Control").validate_approving_authority(
frappe.get_cached_doc("Authorization Control").validate_approving_authority(
self.doctype, self.company, self.base_grand_total
)
@@ -942,7 +942,7 @@ class PurchaseReceipt(BuyingController):
updated_pr += update_billed_amount_based_on_po(po_details, update_modified, self)
for pr in set(updated_pr):
pr_doc = self if (pr == self.name) else frappe.get_doc("Purchase Receipt", pr)
pr_doc = self if (pr == self.name) else frappe.get_lazy_doc("Purchase Receipt", pr)
update_billing_percentage(pr_doc, update_modified=update_modified)
def reserve_stock(self):
@@ -980,7 +980,7 @@ class PurchaseReceipt(BuyingController):
)
for so, items_details in so_items_details_map.items():
so_doc = frappe.get_doc("Sales Order", so)
so_doc = frappe.get_lazy_doc("Sales Order", so)
so_doc.create_stock_reservation_entries(
items_details=items_details,
from_voucher_type="Purchase Receipt",
@@ -1463,7 +1463,7 @@ def make_purchase_return(source_name, target_doc=None):
@frappe.whitelist()
def update_purchase_receipt_status(docname, status):
pr = frappe.get_doc("Purchase Receipt", docname)
pr = frappe.get_lazy_doc("Purchase Receipt", docname)
pr.update_status(status)

View File

@@ -390,7 +390,7 @@ def _get_directly_dependent_vouchers(doc):
warehouses = set()
if doc.based_on == "Transaction":
ref_doc = frappe.get_doc(doc.voucher_type, doc.voucher_no)
ref_doc = frappe.get_lazy_doc(doc.voucher_type, doc.voucher_no)
doc_items, doc_warehouses = ref_doc.get_items_and_warehouses()
items.update(doc_items)
warehouses.update(doc_warehouses)

View File

@@ -1225,7 +1225,7 @@ class update_entries_after:
return False
def recalculate_amounts_in_stock_entry(self, voucher_no, voucher_detail_no):
stock_entry = frappe.get_doc("Stock Entry", voucher_no, for_update=True)
stock_entry = frappe.get_lazy_doc("Stock Entry", voucher_no, for_update=True)
stock_entry.calculate_rate_and_amount(reset_outgoing_rate=False, raise_error_if_no_rate=False)
stock_entry.db_update()
for d in stock_entry.items:
@@ -1268,7 +1268,7 @@ class update_entries_after:
# Recalculate subcontracted item's rate in case of subcontracted purchase receipt/invoice
if frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "is_subcontracted"):
doc = frappe.get_doc(sle.voucher_type, sle.voucher_no)
doc = frappe.get_lazy_doc(sle.voucher_type, sle.voucher_no)
doc.update_valuation_rate(reset_outgoing_rate=False)
for d in doc.items + doc.supplied_items:
d.db_update()
@@ -1283,7 +1283,7 @@ class update_entries_after:
{"rate": outgoing_rate, "amount": abs(sle.actual_qty) * outgoing_rate},
)
scr = frappe.get_doc("Subcontracting Receipt", sle.voucher_no, for_update=True)
scr = frappe.get_lazy_doc("Subcontracting Receipt", sle.voucher_no, for_update=True)
scr.calculate_items_qty_and_amount()
scr.db_update()
for d in scr.items:
@@ -1291,7 +1291,7 @@ class update_entries_after:
def update_rate_on_stock_reconciliation(self, sle):
if not sle.serial_no and not sle.batch_no:
sr = frappe.get_doc("Stock Reconciliation", sle.voucher_no, for_update=True)
sr = frappe.get_lazy_doc("Stock Reconciliation", sle.voucher_no, for_update=True)
for item in sr.items:
# Skip for Serial and Batch Items