fix: Purchase Receipt timeout error
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 882a374..ba4d1cd 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -1463,6 +1463,7 @@
def update_billing_status_in_pr(self, update_modified=True):
updated_pr = []
+ po_details = []
for d in self.get("items"):
if d.pr_detail:
billed_amt = frappe.db.sql(
@@ -1480,7 +1481,10 @@
)
updated_pr.append(d.purchase_receipt)
elif d.po_detail:
- updated_pr += update_billed_amount_based_on_po(d.po_detail, update_modified)
+ po_details.append(d.po_detail)
+
+ if po_details:
+ updated_pr += update_billed_amount_based_on_po(po_details, update_modified)
for pr in set(updated_pr):
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_billing_percentage
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index f85c478..a5986eb 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -6,7 +6,9 @@
from frappe import _, throw
from frappe.desk.notifications import clear_doctype_notifications
from frappe.model.mapper import get_mapped_doc
+from frappe.query_builder.functions import CombineDatetime
from frappe.utils import cint, flt, getdate, nowdate
+from pypika import functions as fn
import erpnext
from erpnext.accounts.utils import get_account_currency
@@ -750,48 +752,38 @@
def update_billing_status(self, update_modified=True):
updated_pr = [self.name]
+ po_details = []
for d in self.get("items"):
if d.get("purchase_invoice") and d.get("purchase_invoice_item"):
d.db_set("billed_amt", d.amount, update_modified=update_modified)
elif d.purchase_order_item:
- updated_pr += update_billed_amount_based_on_po(d.purchase_order_item, update_modified)
+ po_details.append(d.purchase_order_item)
+
+ if po_details:
+ updated_pr += update_billed_amount_based_on_po(po_details, update_modified)
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_cached_doc("Purchase Receipt", pr)
update_billing_percentage(pr_doc, update_modified=update_modified)
self.load_from_db()
-def update_billed_amount_based_on_po(po_detail, update_modified=True):
- # Billed against Sales Order directly
- billed_against_po = frappe.db.sql(
- """select sum(amount) from `tabPurchase Invoice Item`
- where po_detail=%s and (pr_detail is null or pr_detail = '') and docstatus=1""",
- po_detail,
- )
- billed_against_po = billed_against_po and billed_against_po[0][0] or 0
+def update_billed_amount_based_on_po(po_details, update_modified=True):
+ po_billed_amt_details = get_billed_amount_against_po(po_details)
- # Get all Purchase Receipt Item rows against the Purchase Order Item row
- pr_details = frappe.db.sql(
- """select pr_item.name, pr_item.amount, pr_item.parent
- from `tabPurchase Receipt Item` pr_item, `tabPurchase Receipt` pr
- where pr.name=pr_item.parent and pr_item.purchase_order_item=%s
- and pr.docstatus=1 and pr.is_return = 0
- order by pr.posting_date asc, pr.posting_time asc, pr.name asc""",
- po_detail,
- as_dict=1,
- )
+ # Get all Purchase Receipt Item rows against the Purchase Order Items
+ pr_details = get_purchase_receipts_against_po_details(po_details)
+
+ pr_items = [pr_detail.name for pr_detail in pr_details]
+ pr_items_billed_amount = get_billed_amount_against_pr(pr_items)
updated_pr = []
for pr_item in pr_details:
+ billed_against_po = flt(po_billed_amt_details.get(pr_item.purchase_order_item))
+
# Get billed amount directly against Purchase Receipt
- billed_amt_agianst_pr = frappe.db.sql(
- """select sum(amount) from `tabPurchase Invoice Item`
- where pr_detail=%s and docstatus=1""",
- pr_item.name,
- )
- billed_amt_agianst_pr = billed_amt_agianst_pr and billed_amt_agianst_pr[0][0] or 0
+ billed_amt_agianst_pr = pr_items_billed_amount.get(pr_item.name, 0)
# Distribute billed amount directly against PO between PRs based on FIFO
if billed_against_po and billed_amt_agianst_pr < pr_item.amount:
@@ -803,19 +795,85 @@
billed_amt_agianst_pr += billed_against_po
billed_against_po = 0
- frappe.db.set_value(
- "Purchase Receipt Item",
- pr_item.name,
- "billed_amt",
- billed_amt_agianst_pr,
- update_modified=update_modified,
- )
+ po_billed_amt_details[pr_item.purchase_order_item] = billed_against_po
- updated_pr.append(pr_item.parent)
+ if pr_item.billed_amt != billed_amt_agianst_pr:
+ frappe.db.set_value(
+ "Purchase Receipt Item",
+ pr_item.name,
+ "billed_amt",
+ billed_amt_agianst_pr,
+ update_modified=update_modified,
+ )
+
+ updated_pr.append(pr_item.parent)
return updated_pr
+def get_purchase_receipts_against_po_details(po_details):
+ # Get Purchase Receipts against Purchase Order Items
+
+ purchase_receipt = frappe.qb.DocType("Purchase Receipt")
+ purchase_receipt_item = frappe.qb.DocType("Purchase Receipt Item")
+
+ query = (
+ frappe.qb.from_(purchase_receipt)
+ .inner_join(purchase_receipt_item)
+ .on(purchase_receipt.name == purchase_receipt_item.parent)
+ .select(
+ purchase_receipt_item.name,
+ purchase_receipt_item.parent,
+ purchase_receipt_item.amount,
+ purchase_receipt_item.billed_amt,
+ purchase_receipt_item.purchase_order_item,
+ )
+ .where(
+ (purchase_receipt_item.purchase_order_item.isin(po_details))
+ & (purchase_receipt.docstatus == 1)
+ & (purchase_receipt.is_return == 0)
+ )
+ .orderby(CombineDatetime(purchase_receipt.posting_date, purchase_receipt.posting_time))
+ .orderby(purchase_receipt.name)
+ )
+
+ return query.run(as_dict=True)
+
+
+def get_billed_amount_against_pr(pr_items):
+ # Get billed amount directly against Purchase Receipt
+
+ purchase_invoice_item = frappe.qb.DocType("Purchase Invoice Item")
+
+ query = (
+ frappe.qb.from_(purchase_invoice_item)
+ .select(fn.Sum(purchase_invoice_item.amount).as_("billed_amt"), purchase_invoice_item.pr_detail)
+ .where((purchase_invoice_item.pr_detail.isin(pr_items)) & (purchase_invoice_item.docstatus == 1))
+ .groupby(purchase_invoice_item.pr_detail)
+ ).run(as_dict=1)
+
+ return {d.pr_detail: flt(d.billed_amt) for d in query}
+
+
+def get_billed_amount_against_po(po_items):
+ # Get billed amount directly against Purchase Order
+
+ purchase_invoice_item = frappe.qb.DocType("Purchase Invoice Item")
+
+ query = (
+ frappe.qb.from_(purchase_invoice_item)
+ .select(fn.Sum(purchase_invoice_item.amount).as_("billed_amt"), purchase_invoice_item.po_detail)
+ .where(
+ (purchase_invoice_item.po_detail.isin(po_items))
+ & (purchase_invoice_item.docstatus == 1)
+ & (purchase_invoice_item.pr_detail.isnull())
+ )
+ .groupby(purchase_invoice_item.po_detail)
+ ).run(as_dict=1)
+
+ return {d.po_detail: flt(d.billed_amt) for d in query}
+
+
def update_billing_percentage(pr_doc, update_modified=True):
# Reload as billed amount was set in db directly
pr_doc.load_from_db()