Test cases for serial no
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index b91fddf..4ab95c7 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -299,7 +299,7 @@
 		be delivered""").format(sales_order, sr.item_code, sr.name))
 
 def has_duplicate_serial_no(sn, sle):
-	if sn.warehouse:
+	if sn.warehouse and sle.voucher_type != 'Stock Reconciliation':
 		return True
 
 	if sn.company != sle.company:
@@ -413,14 +413,17 @@
 		update_rejected_serial_nos = True if (controller.doctype in ("Purchase Receipt", "Purchase Invoice")
 			and d.rejected_qty) else False
 		accepted_serial_nos_updated = False
+
 		if controller.doctype == "Stock Entry":
 			warehouse = d.t_warehouse
 			qty = d.transfer_qty
 		else:
 			warehouse = d.warehouse
-			qty = d.stock_qty
+			qty = (d.qty if controller.doctype == "Stock Reconciliation"
+				else d.stock_qty)
 
 		for sle in stock_ledger_entries:
+			print(accepted_serial_nos_updated, qty, sle.actual_qty)
 			if sle.voucher_detail_no==d.name:
 				if not accepted_serial_nos_updated and qty and abs(sle.actual_qty)==qty \
 					and sle.warehouse == warehouse and sle.serial_no != d.serial_no:
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 9aec76d..cdf6068 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -36,6 +36,9 @@
 		self.update_stock_ledger()
 		self.make_gl_entries()
 
+		from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit
+		update_serial_nos_after_submit(self, "items")
+
 	def on_cancel(self):
 		self.delete_and_repost_sle()
 		self.make_gl_entries_on_cancel()
@@ -48,7 +51,8 @@
 				self.posting_date, self.posting_time, batch_no=item.batch_no)
 
 			if ((item.qty==None or item.qty==item_dict.get("qty"))
-				and (item.valuation_rate==None or item.valuation_rate==item_dict.get("rate"))):
+				and (item.valuation_rate==None or item.valuation_rate==item_dict.get("rate"))
+				and item.serial_no == item_dict.get("serial_nos")):
 				return False
 			else:
 				# set default as current rates
@@ -64,7 +68,7 @@
 				item.current_qty = item_dict.get("qty")
 				item.current_valuation_rate = item_dict.get("rate")
 				self.difference_amount += (flt(item.qty, item.precision("qty")) * \
-					flt(item.valuation_rate or rate, item.precision("valuation_rate")) \
+					flt(item.valuation_rate or item_dict.get("rate"), item.precision("valuation_rate")) \
 					- flt(item_dict.get("qty"), item.precision("qty")) * flt(item_dict.get("rate"), item.precision("valuation_rate")))
 				return True
 
@@ -157,7 +161,7 @@
 			validate_is_stock_item(item_code, item.is_stock_item, verbose=0)
 
 			# item should not be serialized
-			if item.has_serial_no and not row.serial_no:
+			if item.has_serial_no and not row.serial_no and not item.serial_no_series:
 				raise frappe.ValidationError(_("Serial nos are required for serialized item {0}").format(item_code))
 
 			# item managed batch-wise not allowed
@@ -180,7 +184,8 @@
 
 		sl_entries = []
 		for row in self.items:
-			if row.serial_no or row.batch_no:
+			item = frappe.get_doc("Item", row.item_code)
+			if item.has_serial_no or item.has_batch_no:
 				self.get_sle_for_serialized_items(row, sl_entries)
 			else:
 				previous_sle = get_previous_sle({
@@ -213,6 +218,9 @@
 	def get_sle_for_serialized_items(self, row, sl_entries):
 		from erpnext.stock.stock_ledger import get_previous_sle
 
+		serial_nos = get_serial_nos(row.serial_no)
+
+
 		# To issue existing serial nos
 		if row.current_qty and (row.current_serial_no or row.batch_no):
 			args = self.get_sle_for_items(row)
@@ -230,7 +238,7 @@
 
 			sl_entries.append(args)
 
-		for serial_no in get_serial_nos(row.serial_no):
+		for serial_no in serial_nos:
 			args = self.get_sle_for_items(row, [serial_no])
 
 			previous_sle = get_previous_sle({
@@ -262,7 +270,7 @@
 
 				sl_entries.append(args)
 
-		if self.docstatus == 1:
+		if self.docstatus == 1 and not row.remove_serial_no_from_stock:
 			args = self.get_sle_for_items(row)
 
 			args.update({
@@ -273,6 +281,15 @@
 
 			sl_entries.append(args)
 
+		if serial_nos == get_serial_nos(row.current_serial_no):
+			# update valuation rate
+			self.update_valuation_rate_for_serial_nos(row, serial_nos)
+
+	def update_valuation_rate_for_serial_nos(self, row, serial_nos):
+		valuation_rate = row.valuation_rate if self.docstatus == 1 else row.current_valuation_rate
+		for d in serial_nos:
+			frappe.db.set_value("Serial No", d, 'purchase_rate', valuation_rate)
+
 	def get_sle_for_items(self, row, serial_nos=None):
 		"""Insert Stock Ledger Entries"""
 
@@ -287,6 +304,7 @@
 			"posting_time": self.posting_time,
 			"voucher_type": self.doctype,
 			"voucher_no": self.name,
+			"voucher_detail_no": row.name,
 			"company": self.company,
 			"stock_uom": frappe.db.get_value("Item", row.item_code, "stock_uom"),
 			"is_cancelled": "No" if self.docstatus != 2 else "Yes",
@@ -432,7 +450,6 @@
 	if item_dict.get("has_batch_no"):
 		qty = get_batch_qty(batch_no, warehouse) or 0
 
-	print(qty, rate, batch_no, warehouse)
 	return {
 		'qty': qty,
 		'rate': rate,
@@ -457,7 +474,7 @@
 		"serial_nos": serial_nos
 	})
 
-	rate = get_incoming_rate(args)
+	rate = get_incoming_rate(args, raise_error_if_no_rate=False) or 0
 
 	return qty, rate, serial_nos
 
diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
index 2dc585b..5ee8228 100644
--- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
@@ -13,9 +13,12 @@
 from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import EmptyStockReconciliationItemsError, get_items
 from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
 from erpnext.stock.doctype.item.test_item import create_item
+from erpnext.stock.utils import get_stock_balance, get_incoming_rate, get_available_serial_nos
+from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 
 class TestStockReconciliation(unittest.TestCase):
 	def setUp(self):
+		create_batch_or_serial_no_items()
 		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1)
 		self.insert_existing_sle()
 
@@ -106,6 +109,83 @@
 		make_stock_entry(posting_date="2013-01-05", posting_time="07:00", item_code="_Test Item",
 			target="_Test Warehouse - _TC", qty=15, basic_rate=1200)
 
+	def test_stock_reco_for_serialized_item(self):
+		set_perpetual_inventory()
+
+		to_delete_records = []
+		to_delete_serial_nos = []
+
+		# Add new serial nos
+		serial_item_code = "Stock-Reco-Serial-Item-1"
+		serial_warehouse = "_Test Warehouse for Stock Reco1 - _TC"
+
+		sr = create_stock_reconciliation(item_code=serial_item_code,
+			warehouse = serial_warehouse, qty=5, rate=200)
+
+		# print(sr.name)
+		serial_nos = get_serial_nos(sr.items[0].serial_no)
+		self.assertEqual(len(serial_nos), 5)
+
+		args = {
+			"item_code": serial_item_code,
+			"warehouse": serial_warehouse,
+			"posting_date": nowdate(),
+			"posting_time": nowtime(),
+			"serial_no": sr.items[0].serial_no
+		}
+
+		valuation_rate = get_incoming_rate(args)
+		self.assertEqual(valuation_rate, 200)
+
+		to_delete_records.append(sr.name)
+
+		sr = create_stock_reconciliation(item_code=serial_item_code,
+			warehouse = serial_warehouse, qty=5, rate=300, serial_no = '\n'.join(serial_nos))
+
+		# print(sr.name)
+		serial_nos1 = get_serial_nos(sr.items[0].serial_no)
+		self.assertEqual(len(serial_nos1), 5)
+
+		args = {
+			"item_code": serial_item_code,
+			"warehouse": serial_warehouse,
+			"posting_date": nowdate(),
+			"posting_time": nowtime(),
+			"serial_no": sr.items[0].serial_no
+		}
+
+		valuation_rate = get_incoming_rate(args)
+		self.assertEqual(valuation_rate, 300)
+
+		to_delete_records.append(sr.name)
+		to_delete_records.reverse()
+
+		for d in to_delete_records:
+			stock_doc = frappe.get_doc("Stock Reconciliation", d)
+			stock_doc.cancel()
+			frappe.delete_doc("Stock Reconciliation", stock_doc.name)
+
+		for d in serial_nos + serial_nos1:
+			if frappe.db.exists("Serial No", d):
+				frappe.delete_doc("Serial No", d)
+
+def create_batch_or_serial_no_items():
+	create_warehouse("_Test Warehouse for Stock Reco1",
+		{"is_group": 0, "parent_warehouse": "_Test Warehouse Group - _TC"})
+
+	serial_item_doc = create_item("Stock-Reco-Serial-Item-1", is_stock_item=1)
+	if not serial_item_doc.has_serial_no:
+		serial_item_doc.has_serial_no = 1
+		serial_item_doc.serial_no_series = "SRSI.####"
+		serial_item_doc.save(ignore_permissions=True)
+
+	batch_item_doc = create_item("Stock-Reco-batch-Item-1", is_stock_item=1)
+	if not batch_item_doc.has_batch_no:
+		batch_item_doc.has_batch_no = 1
+		batch_item_doc.create_new_batch = 1
+		serial_item_doc.batch_number_series = "BASR.#####"
+		batch_item_doc.save(ignore_permissions=True)
+
 def create_stock_reconciliation(**args):
 	args = frappe._dict(args)
 	sr = frappe.new_doc("Stock Reconciliation")
@@ -120,7 +200,10 @@
 		"item_code": args.item_code or "_Test Item",
 		"warehouse": args.warehouse or "_Test Warehouse - _TC",
 		"qty": args.qty,
-		"valuation_rate": args.rate
+		"valuation_rate": args.rate,
+		"serial_no": args.serial_no,
+		"batch_no": args.batch_no,
+		"remove_serial_no_from_stock": args.remove_serial_no_from_stock or 0
 	})
 
 	try:
@@ -140,3 +223,4 @@
 			}, allow_negative_stock=1)
 
 test_dependencies = ["Item", "Warehouse"]
+
diff --git a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
index dce87e5..fa42c9c 100644
--- a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
+++ b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
@@ -15,6 +15,7 @@
   "amount",
   "serial_no_and_batch_section",
   "serial_no",
+  "remove_serial_no_from_stock",
   "column_break_11",
   "batch_no",
   "section_break_3",
@@ -165,10 +166,16 @@
    "fieldtype": "Link",
    "label": "Batch No",
    "options": "Batch"
+  },
+  {
+   "default": "0",
+   "fieldname": "remove_serial_no_from_stock",
+   "fieldtype": "Check",
+   "label": "Remove Serial No from Stock"
   }
  ],
  "istable": 1,
- "modified": "2019-05-24 12:34:50.018491",
+ "modified": "2019-06-01 03:16:38.459307",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Reconciliation Item",
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index ea8e880..6ea3228 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -173,7 +173,7 @@
 		in_rate = get_valuation_rate(args.get('item_code'), args.get('warehouse'),
 			args.get('voucher_type'), voucher_no, args.get('allow_zero_valuation'),
 			currency=erpnext.get_company_currency(args.get('company')), company=args.get('company'),
-			raise_error_if_no_rate=True)
+			raise_error_if_no_rate=raise_error_if_no_rate)
 
 	return in_rate