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