fix: time out error while submitting the purchase receipt which has more than 100 serial nos
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index c80b9bd..7077504 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -262,6 +262,30 @@
self.assertEqual(pr2.per_billed, 80)
self.assertEqual(pr2.status, "To Bill")
+ def test_serial_no_against_purchase_receipt(self):
+ from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+
+ item_code = "Test Manual Created Serial No"
+ if not frappe.db.exists("Item", item_code):
+ item = make_item(item_code, dict(has_serial_no=1))
+
+ serial_no = random_string(5)
+ pr_doc = make_purchase_receipt(item_code=item_code,
+ qty=1, serial_no = serial_no)
+
+ self.assertEqual(serial_no, frappe.db.get_value("Serial No",
+ {"purchase_document_type": "Purchase Receipt", "purchase_document_no": pr_doc.name}, "name"))
+
+ item_code = "Test Auto Created Serial No"
+ if not frappe.db.exists("Item", item_code):
+ item = make_item(item_code, dict(has_serial_no=1, serial_no_series="KLJL.###"))
+
+ new_pr_doc = make_purchase_receipt(item_code=item_code, qty=1)
+
+ serial_no = get_serial_nos(new_pr_doc[0].serial_no)[0]
+ self.assertEqual(serial_no, frappe.db.get_value("Serial No",
+ {"purchase_document_type": "Purchase Receipt", "purchase_document_no": new_pr_doc.name}, "name"))
+
def test_not_accept_duplicate_serial_no(self):
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index 23d00da..6ec1645 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -29,13 +29,12 @@
self.via_stock_ledger = False
def validate(self):
- if self.get("__islocal") and self.warehouse:
+ if self.get("__islocal") and self.warehouse and not self.via_stock_ledger:
frappe.throw(_("New Serial No cannot have Warehouse. Warehouse must be set by Stock Entry or Purchase Receipt"), SerialNoCannotCreateDirectError)
self.set_maintenance_status()
self.validate_warehouse()
self.validate_item()
- self.on_stock_ledger_entry()
def set_maintenance_status(self):
if not self.warranty_expiry_date and not self.amc_expiry_date:
@@ -68,7 +67,7 @@
"""
Validate whether serial no is required for this item
"""
- item = frappe.get_doc("Item", self.item_code)
+ item = frappe.get_cached_doc("Item", self.item_code)
if item.has_serial_no!=1:
frappe.throw(_("Item {0} is not setup for Serial Nos. Check Item master").format(self.item_code))
@@ -117,9 +116,9 @@
"warranty_expiry_date"):
self.set(fieldname, None)
- def get_last_sle(self):
+ def get_last_sle(self, serial_no=None):
entries = {}
- sle_dict = self.get_stock_ledger_entries()
+ sle_dict = self.get_stock_ledger_entries(serial_no)
if sle_dict:
if sle_dict.get("incoming", []):
entries["purchase_sle"] = sle_dict["incoming"][0]
@@ -132,13 +131,28 @@
return entries
- def get_stock_ledger_entries(self):
+ def get_stock_ledger_entries(self, serial_no=None):
sle_dict = {}
- for sle in frappe.db.sql("""select * from `tabStock Ledger Entry`
- where serial_no like %s and item_code=%s and ifnull(is_cancelled, 'No')='No'
- order by posting_date desc, posting_time desc, creation desc""",
- ("%%%s%%" % self.name, self.item_code), as_dict=1):
- if self.name.upper() in get_serial_nos(sle.serial_no):
+ if not serial_no:
+ serial_no = self.name
+
+ for sle in frappe.db.sql("""
+ SELECT voucher_type, voucher_no,
+ posting_date, posting_time, incoming_rate, actual_qty, serial_no
+ FROM
+ `tabStock Ledger Entry`
+ WHERE
+ item_code=%s AND company = %s AND ifnull(is_cancelled, 'No')='No'
+ AND (serial_no = %s
+ OR serial_no like %s
+ OR serial_no like %s
+ OR serial_no like %s
+ )
+ ORDER BY
+ posting_date desc, posting_time desc, creation desc""",
+ (self.item_code, self.company,
+ serial_no, serial_no+'\n%', '%\n'+serial_no, '%\n'+serial_no+'\n%'), as_dict=1):
+ if serial_no.upper() in get_serial_nos(sle.serial_no):
if cint(sle.actual_qty) > 0:
sle_dict.setdefault("incoming", []).append(sle)
else:
@@ -178,12 +192,11 @@
where name=%s""" % (dt[0], '%s', '%s'),
('\n'.join(list(serial_nos)), item[0]))
- def on_stock_ledger_entry(self):
- if self.via_stock_ledger and not self.get("__islocal"):
- last_sle = self.get_last_sle()
- self.set_purchase_details(last_sle.get("purchase_sle"))
- self.set_sales_details(last_sle.get("delivery_sle"))
- self.set_maintenance_status()
+ def update_serial_no_reference(self, serial_no=None):
+ last_sle = self.get_last_sle(serial_no)
+ self.set_purchase_details(last_sle.get("purchase_sle"))
+ self.set_sales_details(last_sle.get("delivery_sle"))
+ self.set_maintenance_status()
def process_serial_no(sle):
item_det = get_item_details(sle.item_code)
@@ -407,27 +420,16 @@
def make_serial_no(serial_no, args):
sr = frappe.new_doc("Serial No")
- sr.warehouse = None
- sr.dont_update_if_missing.append("warehouse")
- sr.flags.ignore_permissions = True
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.asset = args.get('asset')
- sr.location = args.get('location')
+ sr.warehouse = args.get('warehouse')
-
- if args.get('purchase_document_type'):
- sr.purchase_document_type = args.get('purchase_document_type')
- sr.purchase_document_no = args.get('purchase_document_no')
- sr.supplier = args.get('supplier')
-
- sr.insert()
- if args.get('warehouse'):
- sr.warehouse = args.get('warehouse')
- sr.save()
+ sr.validate_item()
+ sr.update_serial_no_reference(serial_no)
+ sr.db_insert()
return sr.name