fix: serial no material transfer performance issue (#20747)
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index d34f420..64d4c6c 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -205,6 +205,7 @@
def validate_serial_no(sle, item_det):
serial_nos = get_serial_nos(sle.serial_no) if sle.serial_no else []
+ validate_material_transfer_entry(sle)
if item_det.has_serial_no==0:
if serial_nos:
@@ -224,7 +225,9 @@
for serial_no in serial_nos:
if frappe.db.exists("Serial No", serial_no):
- sr = frappe.get_doc("Serial No", serial_no)
+ sr = frappe.db.get_value("Serial No", serial_no, ["name", "item_code", "batch_no", "sales_order",
+ "delivery_document_no", "delivery_document_type", "warehouse",
+ "purchase_document_no", "company"], as_dict=1)
if sr.item_code!=sle.item_code:
if not allow_serial_nos_with_different_item(serial_no, sle):
@@ -305,6 +308,19 @@
frappe.throw(_("Cannot cancel {0} {1} because Serial No {2} does not belong to the warehouse {3}")
.format(sle.voucher_type, sle.voucher_no, serial_no, sle.warehouse))
+def validate_material_transfer_entry(sle_doc):
+ sle_doc.update({
+ "skip_update_serial_no": False,
+ "skip_serial_no_validaiton": False
+ })
+
+ if (sle_doc.voucher_type == "Stock Entry" and sle_doc.is_cancelled == "No" and
+ frappe.get_cached_value("Stock Entry", sle_doc.voucher_no, "purpose") == "Material Transfer"):
+ if sle_doc.actual_qty < 0:
+ sle_doc.skip_update_serial_no = True
+ else:
+ sle_doc.skip_serial_no_validaiton = True
+
def validate_so_serial_no(sr, sales_order,):
if not sr.sales_order or sr.sales_order!= sales_order:
frappe.throw(_("""Sales Order {0} has reservation for item {1}, you can
@@ -312,7 +328,8 @@
be delivered""").format(sales_order, sr.item_code, sr.name))
def has_duplicate_serial_no(sn, sle):
- if sn.warehouse and sle.voucher_type != 'Stock Reconciliation':
+ if (sn.warehouse and not sle.skip_serial_no_validaiton
+ and sle.voucher_type != 'Stock Reconciliation'):
return True
if sn.company != sle.company:
@@ -337,7 +354,7 @@
"""
allow_serial_nos = False
if sle.voucher_type=="Stock Entry" and cint(sle.actual_qty) > 0:
- stock_entry = frappe.get_doc("Stock Entry", sle.voucher_no)
+ stock_entry = frappe.get_cached_doc("Stock Entry", sle.voucher_no)
if stock_entry.purpose in ("Repack", "Manufacture"):
for d in stock_entry.get("items"):
if d.serial_no and (d.s_warehouse if sle.is_cancelled=="No" else d.t_warehouse):
@@ -348,6 +365,7 @@
return allow_serial_nos
def update_serial_nos(sle, item_det):
+ if sle.skip_update_serial_no: return
if sle.is_cancelled == "No" and not sle.serial_no and cint(sle.actual_qty) > 0 \
and item_det.has_serial_no == 1 and item_det.serial_no_series:
serial_nos = get_auto_serial_nos(item_det.serial_no_series, sle.actual_qty)
@@ -369,22 +387,16 @@
voucher_type = args.get('voucher_type')
item_code = args.get('item_code')
for serial_no in serial_nos:
+ is_new = False
if frappe.db.exists("Serial No", serial_no):
- sr = frappe.get_doc("Serial No", serial_no)
- sr.via_stock_ledger = True
- sr.item_code = item_code
- sr.warehouse = args.get('warehouse') if args.get('actual_qty', 0) > 0 else None
- sr.batch_no = args.get('batch_no')
- sr.location = args.get('location')
- sr.company = args.get('company')
- sr.supplier = args.get('supplier')
- if sr.sales_order and voucher_type == "Stock Entry" \
- and not args.get('actual_qty', 0) > 0:
- sr.sales_order = None
- sr.update_serial_no_reference()
- sr.save(ignore_permissions=True)
+ sr = frappe.get_cached_doc("Serial No", serial_no)
elif args.get('actual_qty', 0) > 0:
- created_numbers.append(make_serial_no(serial_no, args))
+ sr = frappe.new_doc("Serial No")
+ is_new = True
+
+ sr = update_args_for_serial_no(sr, serial_no, args, is_new=is_new)
+ if is_new:
+ created_numbers.append(sr.name)
form_links = list(map(lambda d: frappe.utils.get_link_to_form('Serial No', d), created_numbers))
@@ -419,20 +431,34 @@
return [s.strip() for s in cstr(serial_no).strip().upper().replace(',', '\n').split('\n')
if s.strip()]
-def make_serial_no(serial_no, args):
- sr = frappe.new_doc("Serial No")
- sr.serial_no = serial_no
- sr.item_code = args.get('item_code')
- sr.company = args.get('company')
- sr.batch_no = args.get('batch_no')
- sr.via_stock_ledger = args.get('via_stock_ledger') or True
- sr.warehouse = args.get('warehouse')
+def update_args_for_serial_no(serial_no_doc, serial_no, args, is_new=False):
+ serial_no_doc.update({
+ "item_code": args.get("item_code"),
+ "company": args.get("company"),
+ "batch_no": args.get("batch_no"),
+ "via_stock_ledger": args.get("via_stock_ledger") or True,
+ "supplier": args.get("supplier"),
+ "location": args.get("location"),
+ "warehouse": (args.get("warehouse")
+ if args.get("actual_qty", 0) > 0 else None)
+ })
- sr.validate_item()
- sr.update_serial_no_reference(serial_no)
- sr.db_insert()
+ if is_new:
+ serial_no_doc.serial_no = serial_no
- return sr.name
+ if (serial_no_doc.sales_order and args.get("voucher_type") == "Stock Entry"
+ and not args.get("actual_qty", 0) > 0):
+ serial_no_doc.sales_order = None
+
+ serial_no_doc.validate_item()
+ serial_no_doc.update_serial_no_reference(serial_no)
+
+ if is_new:
+ serial_no_doc.db_insert()
+ else:
+ serial_no_doc.db_update()
+
+ return serial_no_doc
def update_serial_nos_after_submit(controller, parentfield):
stock_ledger_entries = frappe.db.sql("""select voucher_detail_no, serial_no, actual_qty, warehouse
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
index c9eba71..c03eb79 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
@@ -240,6 +240,7 @@
"options": "Company",
"print_width": "150px",
"read_only": 1,
+ "search_index": 1,
"width": "150px"
},
{
@@ -274,7 +275,7 @@
"icon": "fa fa-list",
"idx": 1,
"in_create": 1,
- "modified": "2019-11-27 12:17:31.522675",
+ "modified": "2020-02-25 22:53:33.504681",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Ledger Entry",
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index 2c6c953..f3381c7 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -136,7 +136,7 @@
bin_obj.flags.ignore_permissions = 1
bin_obj.insert()
else:
- bin_obj = frappe.get_doc('Bin', bin)
+ bin_obj = frappe.get_cached_doc('Bin', bin)
bin_obj.flags.ignore_permissions = True
return bin_obj