Allow same serial nos for raw materials and fg item in repack / manufacture entry
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index 2c6a523..07ac326 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -53,7 +53,7 @@
 		if not self.get("__islocal"):
 			item_code, warehouse = frappe.db.get_value("Serial No",
 				self.name, ["item_code", "warehouse"])
-			if item_code != self.item_code:
+			if not self.via_stock_ledger and item_code != self.item_code:
 				frappe.throw(_("Item Code cannot be changed for Serial No."),
 					SerialNoCannotCannotChangeError)
 			if not self.via_stock_ledger and warehouse != self.warehouse:
@@ -205,9 +205,10 @@
 					sr = frappe.get_doc("Serial No", serial_no)
 
 					if sr.item_code!=sle.item_code:
-						frappe.throw(_("Serial No {0} does not belong to Item {1}").format(serial_no,
-							sle.item_code), SerialNoItemError)
-
+						if not allow_serial_nos_with_different_item(serial_no, sle):
+							frappe.throw(_("Serial No {0} does not belong to Item {1}").format(serial_no,
+								sle.item_code), SerialNoItemError)
+								
 					if sr.warehouse and sle.actual_qty > 0:
 						frappe.throw(_("Serial No {0} has already been received").format(sr.name),
 							SerialNoDuplicateError)
@@ -228,7 +229,24 @@
 		elif sle.actual_qty < 0 or not item_det.serial_no_series:
 			frappe.throw(_("Serial Nos Required for Serialized Item {0}").format(sle.item_code),
 				SerialNoRequiredError)
-
+				
+def allow_serial_nos_with_different_item(sle_serial_no, sle):
+	"""
+		Allows same serial nos for raw materials and finished goods 
+		in Manufacture / Repack type Stock Entry
+	"""
+	allow_serial_nos = False
+	if sle.voucher_type=="Stock Entry" and sle.actual_qty > 0:
+		stock_entry = frappe.get_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):
+					serial_nos = get_serial_nos(d.serial_no)
+					if sle_serial_no in serial_nos:
+						allow_serial_nos = True
+	
+	return allow_serial_nos
+			
 def update_serial_nos(sle, item_det):
 	if sle.is_cancelled == "No" and not sle.serial_no and sle.actual_qty > 0 \
 			and item_det.has_serial_no == 1 and item_det.serial_no_series:
@@ -245,6 +263,7 @@
 			if frappe.db.exists("Serial No", serial_no):
 				sr = frappe.get_doc("Serial No", serial_no)
 				sr.via_stock_ledger = True
+				sr.item_code = sle.item_code
 				sr.warehouse = sle.warehouse if sle.actual_qty > 0 else None
 				sr.save(ignore_permissions=True)
 			elif sle.actual_qty > 0:
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 18d600d..868053e 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -359,14 +359,17 @@
 
 	def update_stock_ledger(self):
 		sl_entries = []
+		
+		# make sl entries for source warehouse first, then do for target warehouse
 		for d in self.get('items'):
-			if cstr(d.s_warehouse) and self.docstatus == 1:
+			if cstr(d.s_warehouse):
 				sl_entries.append(self.get_sl_entries(d, {
 					"warehouse": cstr(d.s_warehouse),
 					"actual_qty": -flt(d.transfer_qty),
 					"incoming_rate": 0
 				}))
-
+				
+		for d in self.get('items'):
 			if cstr(d.t_warehouse):
 				sl_entries.append(self.get_sl_entries(d, {
 					"warehouse": cstr(d.t_warehouse),
@@ -374,15 +377,18 @@
 					"incoming_rate": flt(d.valuation_rate)
 				}))
 
-			# On cancellation, make stock ledger entry for
-			# target warehouse first, to update serial no values properly
+		# On cancellation, make stock ledger entry for
+		# target warehouse first, to update serial no values properly
 
-			if cstr(d.s_warehouse) and self.docstatus == 2:
-				sl_entries.append(self.get_sl_entries(d, {
-					"warehouse": cstr(d.s_warehouse),
-					"actual_qty": -flt(d.transfer_qty),
-					"incoming_rate": 0
-				}))
+			# if cstr(d.s_warehouse) and self.docstatus == 2:
+			# 	sl_entries.append(self.get_sl_entries(d, {
+			# 		"warehouse": cstr(d.s_warehouse),
+			# 		"actual_qty": -flt(d.transfer_qty),
+			# 		"incoming_rate": 0
+			# 	}))
+
+		if self.docstatus == 2:
+			sl_entries.reverse()
 
 		self.make_sl_entries(sl_entries, self.amended_from and 'Yes' or 'No')
 
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index 62c55fd..252deaa 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -566,6 +566,27 @@
 		stock_entry = frappe.get_doc(make_stock_entry(production_order.name, "Manufacture", 1))
 		stock_entry.insert()
 		self.assertTrue("_Test Variant Item-S" in [d.item_code for d in stock_entry.items])
+		
+	def test_same_serial_nos_in_repack_or_manufacture_entries(self):
+		s1 = make_serialized_item(target_warehouse="_Test Warehouse - _TC")
+		serial_nos = s1.get("items")[0].serial_no
+		
+		s2 = make_stock_entry(item_code="_Test Serialized Item With Series", source="_Test Warehouse - _TC", 
+			qty=2, basic_rate=100, purpose="Repack", serial_no=serial_nos, do_not_save=True)
+			
+		s2.append("items", {
+			"item_code": "_Test Serialized Item",
+			"t_warehouse": "_Test Warehouse - _TC",
+			"qty": 2,
+			"basic_rate": 120,
+			"expense_account": "Stock Adjustment - _TC",
+			"conversion_factor": 1.0,
+			"cost_center": "_Test Cost Center - _TC",
+			"serial_no": serial_nos
+		})
+		
+		s2.submit()
+		s2.cancel()
 
 def make_serialized_item(item_code=None, serial_no=None, target_warehouse=None):
 	se = frappe.copy_doc(test_records[0])
@@ -616,7 +637,8 @@
 		"basic_rate": args.basic_rate,
 		"expense_account": args.expense_account or "Stock Adjustment - _TC",
 		"conversion_factor": 1.0,
-		"cost_center": "_Test Cost Center - _TC"
+		"cost_center": "_Test Cost Center - _TC",
+		"serial_no": args.serial_no
 	})
 
 	if not args.do_not_save: