Merge pull request #34022 from ruthra-kumar/gross_profit_memory_and_performance_issue
perf: Gross Profit report will fetch SLE's on demand and memoize
diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py
index e23265b..fde4de8 100644
--- a/erpnext/accounts/report/gross_profit/gross_profit.py
+++ b/erpnext/accounts/report/gross_profit/gross_profit.py
@@ -395,6 +395,7 @@
class GrossProfitGenerator(object):
def __init__(self, filters=None):
+ self.sle = {}
self.data = []
self.average_buying_rate = {}
self.filters = frappe._dict(filters)
@@ -404,7 +405,6 @@
if filters.group_by == "Invoice":
self.group_items_by_invoice()
- self.load_stock_ledger_entries()
self.load_product_bundle()
self.load_non_stock_items()
self.get_returned_invoice_items()
@@ -633,7 +633,7 @@
return flt(row.qty) * item_rate
else:
- my_sle = self.sle.get((item_code, row.warehouse))
+ my_sle = self.get_stock_ledger_entries(item_code, row.warehouse)
if (row.update_stock or row.dn_detail) and my_sle:
parenttype, parent = row.parenttype, row.parent
if row.dn_detail:
@@ -651,7 +651,7 @@
dn["item_row"],
dn["warehouse"],
)
- my_sle = self.sle.get((item_code, warehouse))
+ my_sle = self.get_stock_ledger_entries(item_code, row.warehouse)
return self.calculate_buying_amount_from_sle(
row, my_sle, parenttype, parent, item_row, item_code
)
@@ -667,15 +667,12 @@
def get_buying_amount_from_so_dn(self, sales_order, so_detail, item_code):
from frappe.query_builder.functions import Sum
- delivery_note = frappe.qb.DocType("Delivery Note")
delivery_note_item = frappe.qb.DocType("Delivery Note Item")
query = (
- frappe.qb.from_(delivery_note)
- .inner_join(delivery_note_item)
- .on(delivery_note.name == delivery_note_item.parent)
+ frappe.qb.from_(delivery_note_item)
.select(Sum(delivery_note_item.incoming_rate * delivery_note_item.stock_qty))
- .where(delivery_note.docstatus == 1)
+ .where(delivery_note_item.docstatus == 1)
.where(delivery_note_item.item_code == item_code)
.where(delivery_note_item.against_sales_order == sales_order)
.where(delivery_note_item.so_detail == so_detail)
@@ -947,24 +944,36 @@
"Item", item_code, ["item_name", "description", "item_group", "brand"]
)
- def load_stock_ledger_entries(self):
- res = frappe.db.sql(
- """select item_code, voucher_type, voucher_no,
- voucher_detail_no, stock_value, warehouse, actual_qty as qty
- from `tabStock Ledger Entry`
- where company=%(company)s and is_cancelled = 0
- order by
- item_code desc, warehouse desc, posting_date desc,
- posting_time desc, creation desc""",
- self.filters,
- as_dict=True,
- )
- self.sle = {}
- for r in res:
- if (r.item_code, r.warehouse) not in self.sle:
- self.sle[(r.item_code, r.warehouse)] = []
+ def get_stock_ledger_entries(self, item_code, warehouse):
+ if item_code and warehouse:
+ if (item_code, warehouse) not in self.sle:
+ sle = qb.DocType("Stock Ledger Entry")
+ res = (
+ qb.from_(sle)
+ .select(
+ sle.item_code,
+ sle.voucher_type,
+ sle.voucher_no,
+ sle.voucher_detail_no,
+ sle.stock_value,
+ sle.warehouse,
+ sle.actual_qty.as_("qty"),
+ )
+ .where(
+ (sle.company == self.filters.company)
+ & (sle.item_code == item_code)
+ & (sle.warehouse == warehouse)
+ & (sle.is_cancelled == 0)
+ )
+ .orderby(sle.item_code)
+ .orderby(sle.warehouse, sle.posting_date, sle.posting_time, sle.creation, order=Order.desc)
+ .run(as_dict=True)
+ )
- self.sle[(r.item_code, r.warehouse)].append(r)
+ self.sle[(item_code, warehouse)] = res
+
+ return self.sle[(item_code, warehouse)]
+ return []
def load_product_bundle(self):
self.product_bundles = {}