perf: single update per Sales Order.
For each SO item the sales order picking status was being updated, this
isn't required and wasteful.
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index 1d172ad..a35e16f 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -385,6 +385,16 @@
if tot_qty != 0:
self.db_set("per_delivered", flt(delivered_qty / tot_qty) * 100, update_modified=False)
+ def update_picking_status(self):
+ total_picked_qty = 0.0
+ total_qty = 0.0
+ for so_item in self.items:
+ total_picked_qty += flt(so_item.picked_qty)
+ total_qty += flt(so_item.stock_qty)
+ per_picked = total_picked_qty / total_qty * 100
+
+ self.db_set("per_picked", flt(per_picked), update_modified=False)
+
def set_indicator(self):
"""Set indicator for portal"""
if self.per_billed < 100 and self.per_delivered < 100:
diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py
index 01448be..3191f15 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.py
+++ b/erpnext/stock/doctype/pick_list/pick_list.py
@@ -5,6 +5,7 @@
from collections import OrderedDict, defaultdict
from itertools import groupby
from operator import itemgetter
+from typing import Set
import frappe
from frappe import _
@@ -39,6 +40,8 @@
)
def before_submit(self):
+
+ update_sales_orders = set()
for item in self.locations:
# if the user has not entered any picked qty, set it to stock_qty, before submit
if item.picked_qty == 0:
@@ -47,6 +50,7 @@
if item.sales_order_item:
# update the picked_qty in SO Item
self.update_sales_order_item(item, item.picked_qty, item.item_code)
+ update_sales_orders.add(item.sales_order)
if not frappe.get_cached_value("Item", item.item_code, "has_serial_no"):
continue
@@ -66,11 +70,18 @@
title=_("Quantity Mismatch"),
)
+ self.update_sales_order_picking_status(update_sales_orders)
+
def before_cancel(self):
- # update picked_qty in SO Item on cancel of PL
+ """Deduct picked qty on cancelling pick list"""
+ updated_sales_orders = set()
+
for item in self.get("locations"):
if item.sales_order_item:
self.update_sales_order_item(item, -1 * item.picked_qty, item.item_code)
+ updated_sales_orders.add(item.sales_order)
+
+ self.update_sales_order_picking_status(updated_sales_orders)
def update_sales_order_item(self, item, picked_qty, item_code):
item_table = "Sales Order Item" if not item.product_bundle_item else "Packed Item"
@@ -91,17 +102,11 @@
frappe.db.set_value(item_table, item.sales_order_item, "picked_qty", already_picked + picked_qty)
- # TODO: only do this once after all items
- sales_order = frappe.get_doc("Sales Order", item.sales_order)
- total_picked_qty = 0
- total_so_qty = 0
- for so_item in sales_order.get("items"):
- total_picked_qty += flt(so_item.picked_qty)
- total_so_qty += flt(so_item.stock_qty)
- total_picked_qty = total_picked_qty + picked_qty
- per_picked = total_picked_qty / total_so_qty * 100
-
- sales_order.db_set("per_picked", flt(per_picked), update_modified=False)
+ @staticmethod
+ def update_sales_order_picking_status(sales_orders: Set[str]) -> None:
+ for sales_order in sales_orders:
+ if sales_order:
+ frappe.get_doc("Sales Order", sales_order).update_picking_status()
@frappe.whitelist()
def set_item_locations(self, save=False):