fix: avoid updating items table if no change due to putaway
diff --git a/erpnext/stock/doctype/putaway_rule/putaway_rule.py b/erpnext/stock/doctype/putaway_rule/putaway_rule.py
index 523ba12..4e472a9 100644
--- a/erpnext/stock/doctype/putaway_rule/putaway_rule.py
+++ b/erpnext/stock/doctype/putaway_rule/putaway_rule.py
@@ -9,7 +9,7 @@
import frappe
from frappe import _
from frappe.model.document import Document
-from frappe.utils import cint, floor, flt, nowdate
+from frappe.utils import cint, cstr, floor, flt, nowdate
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
from erpnext.stock.utils import get_stock_balance
@@ -142,11 +142,44 @@
if items_not_accomodated:
show_unassigned_items_message(items_not_accomodated)
- items[:] = updated_table if updated_table else items # modify items table
+ if updated_table and _items_changed(items, updated_table, doctype):
+ items[:] = updated_table
+ frappe.msgprint(_("Applied putaway rules."), alert=True)
if sync and json.loads(sync): # sync with client side
return items
+def _items_changed(old, new, doctype: str) -> bool:
+ """ Check if any items changed by application of putaway rules.
+
+ If not, changing item table can have side effects since `name` items also changes.
+ """
+ if len(old) != len(new):
+ return True
+
+ old = [frappe._dict(item) if isinstance(item, dict) else item for item in old]
+
+ if doctype == "Stock Entry":
+ compare_keys = ("item_code", "t_warehouse", "transfer_qty", "serial_no")
+ sort_key = lambda item: (item.item_code, cstr(item.t_warehouse), # noqa
+ flt(item.transfer_qty), cstr(item.serial_no))
+ else:
+ # purchase receipt / invoice
+ compare_keys = ("item_code", "warehouse", "stock_qty", "received_qty", "serial_no")
+ sort_key = lambda item: (item.item_code, cstr(item.warehouse), # noqa
+ flt(item.stock_qty), flt(item.received_qty), cstr(item.serial_no))
+
+ old_sorted = sorted(old, key=sort_key)
+ new_sorted = sorted(new, key=sort_key)
+
+ # Once sorted by all relevant keys both tables should align if they are same.
+ for old_item, new_item in zip(old_sorted, new_sorted):
+ for key in compare_keys:
+ if old_item.get(key) != new_item.get(key):
+ return True
+ return False
+
+
def get_ordered_putaway_rules(item_code, company, source_warehouse=None):
"""Returns an ordered list of putaway rules to apply on an item."""
filters = {