From 4082149f0e7d301941e0b2374d2fbbb28cef1cd9 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 8 Nov 2022 18:19:20 +0530 Subject: [PATCH 1/2] fix: Purchase Receipt timeout error --- .../purchase_invoice/purchase_invoice.py | 6 +- .../purchase_receipt/purchase_receipt.py | 126 +++++++++++++----- 2 files changed, 97 insertions(+), 35 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 882a374046..ba4d1cd5ea 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1463,6 +1463,7 @@ class PurchaseInvoice(BuyingController): 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 @@ class PurchaseInvoice(BuyingController): ) 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 f85c478a72..a5986eb5fc 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -6,7 +6,9 @@ import frappe 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 @@ class PurchaseReceipt(BuyingController): 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 @@ def update_billed_amount_based_on_po(po_detail, update_modified=True): 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() From 727838787978e6f8410525531dcf650d3aa48ae6 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 9 Nov 2022 01:28:40 +0530 Subject: [PATCH 2/2] fix: test cases --- erpnext/stock/doctype/purchase_receipt/purchase_receipt.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index a5986eb5fc..673fcb526d 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -783,7 +783,7 @@ def update_billed_amount_based_on_po(po_details, update_modified=True): 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 = pr_items_billed_amount.get(pr_item.name, 0) + billed_amt_agianst_pr = flt(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: @@ -843,6 +843,9 @@ def get_purchase_receipts_against_po_details(po_details): def get_billed_amount_against_pr(pr_items): # Get billed amount directly against Purchase Receipt + if not pr_items: + return {} + purchase_invoice_item = frappe.qb.DocType("Purchase Invoice Item") query = ( @@ -857,6 +860,8 @@ def get_billed_amount_against_pr(pr_items): def get_billed_amount_against_po(po_items): # Get billed amount directly against Purchase Order + if not po_items: + return {} purchase_invoice_item = frappe.qb.DocType("Purchase Invoice Item")