feat: link serial no to batch no (#20778)

* feat: link serial no to batch no

* fix: test cases
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index b10d8be..792199e 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -238,6 +238,10 @@
 		for d in self.items:
 			if not d.batch_no: continue
 
+			serial_nos = [d.name for d in frappe.get_all("Serial No", {'batch_no': d.batch_no})]
+			if serial_nos:
+				frappe.db.set_value("Serial No", { 'name': ['in', serial_nos] }, "batch_no", None)
+
 			d.batch_no = None
 			d.db_set("batch_no", None)
 
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 253d5f0..d80e8f2 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -7,6 +7,7 @@
 import frappe.defaults
 from frappe.utils import cint, flt, cstr, today, random_string
 from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice
+from erpnext.stock.doctype.item.test_item import create_item
 from erpnext import set_perpetual_inventory
 from erpnext.stock.doctype.serial_no.serial_no import SerialNoDuplicateError
 from erpnext.accounts.doctype.account.test_account import get_inventory_account
@@ -50,6 +51,28 @@
 		self.assertEqual(current_bin_stock_value, existing_bin_stock_value + 250)
 
 		self.assertFalse(get_gl_entries("Purchase Receipt", pr.name))
+	
+	def test_batched_serial_no_purchase(self):
+		item = frappe.get_doc("Item", { 'item_name': 'Batched Serialized Item' })
+		if not item:
+			item = create_item("Batched Serialized Item")
+			item.has_batch_no = 1
+			item.create_new_batch = 1
+			item.has_serial_no = 1
+			item.batch_number_series = "BS-BATCH-.##"
+			item.serial_no_series = "BS-.####"
+			item.save()
+
+		pr = make_purchase_receipt(item_code=item.name, qty=5, rate=500)
+
+		self.assertTrue(frappe.db.get_value('Batch', {'item': item.name, 'reference_name': pr.name}))
+		
+		pr.load_from_db()
+		batch_no = pr.items[0].batch_no
+		pr.cancel()
+
+		self.assertFalse(frappe.db.get_value('Batch', {'item': item.name, 'reference_name': pr.name}))
+		self.assertFalse(frappe.db.get_all('Serial No', {'batch_no': batch_no}))
 
 	def test_purchase_receipt_gl_entry(self):
 		pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", supplier_warehouse = "Work in Progress - TCP1", get_multiple_items = True, get_taxes_and_charges = True)
diff --git a/erpnext/stock/doctype/serial_no/serial_no.json b/erpnext/stock/doctype/serial_no/serial_no.json
index 712aadc..fb28b5c 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.json
+++ b/erpnext/stock/doctype/serial_no/serial_no.json
@@ -6,6 +6,7 @@
  "description": "Distinct unit of an Item",
  "doctype": "DocType",
  "document_type": "Setup",
+ "engine": "InnoDB",
  "field_order": [
   "details",
   "column_break0",
@@ -104,10 +105,11 @@
   },
   {
    "fieldname": "batch_no",
-   "fieldtype": "Data",
+   "fieldtype": "Link",
    "in_list_view": 1,
    "in_standard_filter": 1,
    "label": "Batch No",
+   "options": "Batch",
    "read_only": 1
   },
   {
@@ -425,7 +427,7 @@
  ],
  "icon": "fa fa-barcode",
  "idx": 1,
- "modified": "2019-08-07 17:28:32.243280",
+ "modified": "2020-02-28 19:31:09.357323",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Serial No",