fix: removed sales and purchase fields from serial nos
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 868a150..8ed11a4 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -1448,6 +1448,7 @@
 			"Repost Payment Ledger Items",
 			"Payment Ledger Entry",
 			"Tax Withheld Vouchers",
+			"Serial and Batch Bundle",
 		)
 		self.update_advance_tax_references(cancel=1)
 
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 7454332..714f24a 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -400,6 +400,7 @@
 			"Repost Payment Ledger",
 			"Repost Payment Ledger Items",
 			"Payment Ledger Entry",
+			"Serial and Batch Bundle",
 		)
 
 	def update_status_updater_args(self):
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index 7687aad..bd4bc18 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -5,7 +5,7 @@
 import frappe
 from frappe import _, bold, throw
 from frappe.contacts.doctype.address.address import get_address_display
-from frappe.utils import cint, cstr, flt, get_link_to_form, nowtime
+from frappe.utils import cint, flt, get_link_to_form, nowtime
 
 from erpnext.controllers.accounts_controller import get_taxes_and_charges
 from erpnext.controllers.sales_and_purchase_return import get_rate_for_return
@@ -299,8 +299,7 @@
 									"item_code": p.item_code,
 									"qty": flt(p.qty),
 									"uom": p.uom,
-									"batch_no": cstr(p.batch_no).strip(),
-									"serial_no": cstr(p.serial_no).strip(),
+									"serial_and_batch_bundle": p.serial_and_batch_bundle,
 									"name": d.name,
 									"target_warehouse": p.target_warehouse,
 									"company": self.company,
@@ -323,8 +322,7 @@
 							"uom": d.uom,
 							"stock_uom": d.stock_uom,
 							"conversion_factor": d.conversion_factor,
-							"batch_no": cstr(d.get("batch_no")).strip(),
-							"serial_no": cstr(d.get("serial_no")).strip(),
+							"serial_and_batch_bundle": d.serial_and_batch_bundle,
 							"name": d.name,
 							"target_warehouse": d.target_warehouse,
 							"company": self.company,
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 6156aba..6e71004 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -354,6 +354,7 @@
 										"batch_no": batch_no,
 										"qty": d.qty,
 										"warehouse": d.get(warehouse_field),
+										"incoming_rate": d.rate,
 									}
 								],
 							}
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index 7584522..e30a302 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -33,7 +33,6 @@
 from erpnext.stock.doctype.batch.batch import make_batch
 from erpnext.stock.doctype.item.item import get_item_defaults, validate_end_of_life
 from erpnext.stock.doctype.serial_no.serial_no import (
-	auto_make_serial_nos,
 	clean_serial_no_string,
 	get_auto_serial_nos,
 	get_serial_nos,
@@ -455,7 +454,7 @@
 
 		if self.serial_no:
 			args.update({"serial_no": self.serial_no, "actual_qty": self.qty})
-			auto_make_serial_nos(args)
+			# auto_make_serial_nos(args)
 
 		serial_nos_length = len(get_serial_nos(self.serial_no))
 		if serial_nos_length != self.qty:
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index b4676c1..52abbc0 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -6,6 +6,9 @@
 	setup() {
 		super.setup();
 		let me = this;
+
+		this.frm.ignore_doctypes_on_cancel_all = ['Serial and Batch Bundle'];
+
 		frappe.flags.hide_serial_batch_dialog = true;
 		frappe.ui.form.on(this.frm.doctype + " Item", "rate", function(frm, cdt, cdn) {
 			var item = frappe.get_doc(cdt, cdn);
@@ -124,7 +127,9 @@
 				let item_row = locals[cdt][cdn];
 				return {
 					filters: {
-						'item_code': item_row.item_code
+						'item_code': item_row.item_code,
+						'voucher_type': doc.doctype,
+						'voucher_no': ["in", [doc.name, ""]],
 					}
 				}
 			});
@@ -2277,12 +2282,12 @@
 	}
 };
 
-erpnext.show_serial_batch_selector = function (frm, d, callback, on_close, show_dialog) {
+erpnext.show_serial_batch_selector = function (frm, item_row, callback, on_close, show_dialog) {
 	let warehouse, receiving_stock, existing_stock;
 	if (frm.doc.is_return) {
 		if (["Purchase Receipt", "Purchase Invoice"].includes(frm.doc.doctype)) {
 			existing_stock = true;
-			warehouse = d.warehouse;
+			warehouse = item_row.warehouse;
 		} else if (["Delivery Note", "Sales Invoice"].includes(frm.doc.doctype)) {
 			receiving_stock = true;
 		}
@@ -2292,11 +2297,11 @@
 				receiving_stock = true;
 			} else {
 				existing_stock = true;
-				warehouse = d.s_warehouse;
+				warehouse = item_row.s_warehouse;
 			}
 		} else {
 			existing_stock = true;
-			warehouse = d.warehouse;
+			warehouse = item_row.warehouse;
 		}
 	}
 
@@ -2309,16 +2314,13 @@
 	}
 
 	frappe.require("assets/erpnext/js/utils/serial_no_batch_selector.js", function() {
-		new erpnext.SerialNoBatchSelector({
-			frm: frm,
-			item: d,
-			warehouse_details: {
-				type: "Warehouse",
-				name: warehouse
-			},
-			callback: callback,
-			on_close: on_close
-		}, show_dialog);
+		new erpnext.SerialNoBatchBundleUpdate(frm, item_row, (r) => {
+			if (r) {
+				frm.refresh_fields();
+				frappe.model.set_value(item_row.doctype, item_row.name,
+					"serial_and_batch_bundle", r.name);
+			}
+		});
 	});
 }
 
diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js
index 90967d9..fcaaaf0 100644
--- a/erpnext/public/js/utils/serial_no_batch_selector.js
+++ b/erpnext/public/js/utils/serial_no_batch_selector.js
@@ -629,20 +629,37 @@
 	}
 
 	make() {
+		let label = this.item?.has_serial_no ? __('Serial No') : __('Batch No');
+		let primary_label = this.item?.serial_and_batch_bundle
+			? __('Update') : __('Add');
+
+		if (this.item?.has_serial_no && this.item?.batch_no) {
+			label = __('Serial No / Batch No');
+		}
+
+		primary_label += ' ' + label;
+
 		this.dialog = new frappe.ui.Dialog({
-			title: __('Update Serial No / Batch No'),
+			title: this.item?.title || primary_label,
 			fields: this.get_dialog_fields(),
-			primary_action_label: __('Update'),
+			primary_action_label: primary_label,
 			primary_action: () => this.update_ledgers()
 		});
+
+		if (this.item?.outward) {
+			this.prepare_for_auto_fetch();
+		}
+
 		this.dialog.show();
 	}
 
 	get_serial_no_filters() {
+		let warehouse = this.item?.outward ?
+			this.item.warehouse : "";
+
 		return {
 			'item_code': this.item.item_code,
-			'warehouse': ["=", ""],
-			'delivery_document_no': ["=", ""],
+			'warehouse': ["=", warehouse]
 		};
 	}
 
@@ -681,13 +698,15 @@
 			});
 		}
 
-		if (this.item.has_batch_no && this.item.has_serial_no) {
-			fields.push({
-				fieldtype: 'Section Break',
-			});
+		if (this.item?.outward) {
+			fields = [...fields, ...this.get_filter_fields()];
 		}
 
 		fields.push({
+			fieldtype: 'Section Break',
+		});
+
+		fields.push({
 			fieldname: 'ledgers',
 			fieldtype: 'Table',
 			allow_bulk_edit: true,
@@ -698,6 +717,41 @@
 		return fields;
 	}
 
+	get_filter_fields() {
+		return [
+			{
+				fieldtype: 'Section Break',
+				label: __('Auto Fetch')
+			},
+			{
+				fieldtype: 'Float',
+				fieldname: 'qty',
+				default: this.item.qty || 0,
+				label: __('Qty to Fetch'),
+			},
+			{
+				fieldtype: 'Column Break',
+			},
+			{
+				fieldtype: 'Select',
+				options: ['FIFO', 'LIFO', 'Expiry'],
+				default: 'FIFO',
+				fieldname: 'based_on',
+				label: __('Fetch Based On')
+			},
+			{
+				fieldtype: 'Column Break',
+			},
+			{
+				fieldtype: 'Button',
+				fieldname: 'get_auto_data',
+				label: __('Fetch {0}',
+					[this.item?.has_serial_no ? 'Serial Nos' : 'Batch Nos']),
+			},
+		]
+
+	}
+
 	get_dialog_table_fields() {
 		let fields = []
 
@@ -714,7 +768,9 @@
 					}
 				}
 			})
-		} else if (this.item.has_batch_no) {
+		}
+
+		if (this.item.has_batch_no) {
 			fields = [
 				{
 					fieldtype: 'Link',
@@ -742,6 +798,38 @@
 		return fields;
 	}
 
+	prepare_for_auto_fetch() {
+		this.dialog.fields_dict.get_auto_data.$input.on('click', () => {
+			this.get_auto_data();
+		});
+	}
+
+	get_auto_data() {
+		const { qty, based_on } = this.dialog.get_values();
+
+		if (!qty) {
+			frappe.throw(__('Please enter Qty to Fetch'));
+		}
+
+		frappe.call({
+			method: 'erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle.get_auto_data',
+			args: {
+				item_code: this.item.item_code,
+				warehouse: this.item.warehouse,
+				has_serial_no: this.item.has_serial_no,
+				has_batch_no: this.item.has_batch_no,
+				qty: qty,
+				based_on: based_on
+			},
+			callback: (r) => {
+				if (r.message) {
+					this.dialog.fields_dict.ledgers.df.data = r.message;
+					this.dialog.fields_dict.ledgers.grid.refresh();
+				}
+			}
+		});
+	}
+
 	update_serial_batch_no() {
 		const { scan_serial_no, scan_batch_no } = this.dialog.get_values();
 
diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js
index e3de49c..f5268d6 100644
--- a/erpnext/selling/sales_common.js
+++ b/erpnext/selling/sales_common.js
@@ -420,6 +420,40 @@
 		});
 	}
 
+	pick_serial_and_batch(doc, cdt, cdn) {
+		let item = locals[cdt][cdn];
+		let me = this;
+		let path = "assets/erpnext/js/utils/serial_no_batch_selector.js";
+
+		frappe.db.get_value("Item", item.item_code, ["has_batch_no", "has_serial_no"])
+			.then((r) => {
+				if (r.message && (r.message.has_batch_no || r.message.has_serial_no)) {
+					item.has_serial_no = r.message.has_serial_no;
+					item.has_batch_no = r.message.has_batch_no;
+					item.outward = true;
+
+					item.title = item.has_serial_no ?
+						__("Select Serial No") : __("Select Batch No");
+
+					if (item.has_serial_no && item.has_batch_no) {
+						item.title = __("Select Serial and Batch");
+					}
+
+					frappe.require(path, function() {
+						new erpnext.SerialNoBatchBundleUpdate(
+							me.frm, item, (r) => {
+								if (r) {
+									me.frm.refresh_fields();
+									frappe.model.set_value(cdt, cdn,
+										"serial_and_batch_bundle", r.name);
+								}
+							}
+						);
+					});
+				}
+			});
+	}
+
 	update_auto_repeat_reference(doc) {
 		if (doc.auto_repeat) {
 			frappe.call({
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index 2ee372e..a647a17 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -12,7 +12,6 @@
 
 from erpnext.controllers.accounts_controller import get_taxes_and_charges
 from erpnext.controllers.selling_controller import SellingController
-from erpnext.stock.doctype.batch.batch import set_batch_nos
 from erpnext.stock.doctype.serial_no.serial_no import get_delivery_note_serial_no
 
 form_grid_templates = {"items": "templates/form_grid/item_grid.html"}
@@ -142,11 +141,6 @@
 		from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
 
 		make_packing_list(self)
-
-		if self._action != "submit" and not self.is_return:
-			set_batch_nos(self, "warehouse", throw=True)
-			set_batch_nos(self, "warehouse", throw=True, child_table="packed_items")
-
 		self.update_current_stock()
 
 		if not self.installation_status:
@@ -274,7 +268,12 @@
 
 		self.make_gl_entries_on_cancel()
 		self.repost_future_sle_and_gle()
-		self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Repost Item Valuation")
+		self.ignore_linked_doctypes = (
+			"GL Entry",
+			"Stock Ledger Entry",
+			"Repost Item Valuation",
+			"Serial and Batch Bundle",
+		)
 
 	def update_stock_reservation_entries(self) -> None:
 		"""Updates Delivered Qty in Stock Reservation Entries."""
diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
index 3853bd1..3f77869 100644
--- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
+++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
@@ -77,8 +77,8 @@
   "dn_detail",
   "pick_list_item",
   "section_break_40",
-  "batch_no",
-  "serial_no",
+  "pick_serial_and_batch",
+  "serial_and_batch_bundle",
   "actual_batch_qty",
   "actual_qty",
   "installed_qty",
@@ -508,16 +508,6 @@
    "fieldtype": "Section Break"
   },
   {
-   "fieldname": "batch_no",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Batch No",
-   "oldfieldname": "batch_no",
-   "oldfieldtype": "Link",
-   "options": "Batch",
-   "print_hide": 1
-  },
-  {
    "allow_on_submit": 1,
    "fieldname": "actual_qty",
    "fieldtype": "Float",
@@ -543,15 +533,6 @@
    "width": "150px"
   },
   {
-   "fieldname": "serial_no",
-   "fieldtype": "Text",
-   "in_list_view": 1,
-   "label": "Serial No",
-   "no_copy": 1,
-   "oldfieldname": "serial_no",
-   "oldfieldtype": "Text"
-  },
-  {
    "fieldname": "item_group",
    "fieldtype": "Link",
    "hidden": 1,
@@ -861,6 +842,17 @@
    "no_copy": 1,
    "non_negative": 1,
    "read_only": 1
+  },
+  {
+   "fieldname": "serial_and_batch_bundle",
+   "fieldtype": "Link",
+   "label": "Serial and Batch Bundle",
+   "options": "Serial and Batch Bundle"
+  },
+  {
+   "fieldname": "pick_serial_and_batch",
+   "fieldtype": "Button",
+   "label": "Pick Serial / Batch No"
   }
  ],
  "idx": 1,
diff --git a/erpnext/stock/doctype/packed_item/packed_item.json b/erpnext/stock/doctype/packed_item/packed_item.json
index c5fb241..244c905 100644
--- a/erpnext/stock/doctype/packed_item/packed_item.json
+++ b/erpnext/stock/doctype/packed_item/packed_item.json
@@ -19,6 +19,7 @@
   "rate",
   "uom",
   "section_break_9",
+  "serial_and_batch_bundle",
   "serial_no",
   "column_break_11",
   "batch_no",
@@ -253,6 +254,12 @@
    "no_copy": 1,
    "non_negative": 1,
    "read_only": 1
+  },
+  {
+   "fieldname": "serial_and_batch_bundle",
+   "fieldtype": "Link",
+   "label": "Serial and Batch Bundle",
+   "options": "Serial and Batch Bundle"
   }
  ],
  "idx": 1,
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
index e0cb8ca..312c166 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
@@ -7,8 +7,6 @@
 
 frappe.ui.form.on("Purchase Receipt", {
 	setup: (frm) => {
-		frm.ignore_doctypes_on_cancel_all = ['Serial and Batch Bundle'];
-
 		frm.make_methods = {
 			'Landed Cost Voucher': () => {
 				let lcv = frappe.model.get_new_doc('Landed Cost Voucher');
diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.js b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.js
index 085e33d..f16a72b 100644
--- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.js
+++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.js
@@ -10,6 +10,36 @@
 		frm.trigger('toggle_fields');
 	},
 
+	warehouse(frm) {
+		if (frm.doc.warehouse) {
+			frm.call({
+				method: "set_warehouse",
+				doc: frm.doc,
+				callback(r) {
+					refresh_field("ledgers");
+				}
+			})
+		}
+	},
+
+	has_serial_no(frm) {
+		frm.trigger('toggle_fields');
+	},
+
+	has_batch_no(frm) {
+		frm.trigger('toggle_fields');
+	},
+
+	toggle_fields(frm) {
+		frm.fields_dict.ledgers.grid.update_docfield_property(
+			'serial_no', 'read_only', !frm.doc.has_serial_no
+		);
+
+		frm.fields_dict.ledgers.grid.update_docfield_property(
+			'batch_no', 'read_only', !frm.doc.has_batch_no
+		);
+	},
+
 	set_queries(frm) {
 		frm.set_query('item_code', () => {
 			return {
@@ -35,6 +65,15 @@
 			};
 		});
 
+		frm.set_query('warehouse', () => {
+			return {
+				filters: {
+					'is_group': 0,
+					'company': frm.doc.company,
+				}
+			};
+		});
+
 		frm.set_query('serial_no', 'ledgers', () => {
 			return {
 				filters: {
@@ -58,23 +97,14 @@
 				}
 			};
 		});
-	},
-
-	has_serial_no(frm) {
-		frm.trigger('toggle_fields');
-	},
-
-	has_batch_no(frm) {
-		frm.trigger('toggle_fields');
-	},
-
-	toggle_fields(frm) {
-		frm.fields_dict.ledgers.grid.update_docfield_property(
-			'serial_no', 'read_only', !frm.doc.has_serial_no
-		);
-
-		frm.fields_dict.ledgers.grid.update_docfield_property(
-			'batch_no', 'read_only', !frm.doc.has_batch_no
-		);
 	}
 });
+
+
+frappe.ui.form.on("Serial and Batch Ledger", {
+	ledgers_add(frm, cdt, cdn) {
+		if (frm.doc.warehouse) {
+			locals[cdt][cdn].warehouse = frm.doc.warehouse;
+		}
+	},
+})
\ No newline at end of file
diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.json b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.json
index a08ed83..cfe35d7 100644
--- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.json
+++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.json
@@ -8,17 +8,23 @@
   "item_details_tab",
   "company",
   "item_group",
-  "has_serial_no",
+  "warehouse",
   "column_break_4",
   "item_code",
   "item_name",
+  "has_serial_no",
   "has_batch_no",
   "serial_no_and_batch_no_tab",
   "ledgers",
-  "qty",
+  "quantity_and_rate_section",
+  "total_qty",
+  "column_break_13",
+  "avg_rate",
+  "total_amount",
   "tab_break_12",
   "voucher_type",
   "voucher_no",
+  "column_break_aouy",
   "is_cancelled",
   "amended_from"
  ],
@@ -91,12 +97,6 @@
    "reqd": 1
   },
   {
-   "fieldname": "qty",
-   "fieldtype": "Float",
-   "label": "Total Qty",
-   "read_only": 1
-  },
-  {
    "fieldname": "voucher_type",
    "fieldtype": "Link",
    "label": "Voucher Type",
@@ -129,12 +129,54 @@
    "fieldname": "tab_break_12",
    "fieldtype": "Tab Break",
    "label": "Reference"
+  },
+  {
+   "fieldname": "quantity_and_rate_section",
+   "fieldtype": "Section Break",
+   "label": "Quantity and Rate"
+  },
+  {
+   "fieldname": "column_break_13",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "avg_rate",
+   "fieldtype": "Float",
+   "label": "Avg Rate",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "total_amount",
+   "fieldtype": "Float",
+   "label": "Total Amount",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "total_qty",
+   "fieldtype": "Float",
+   "label": "Total Qty",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_aouy",
+   "fieldtype": "Column Break"
+  },
+  {
+   "depends_on": "company",
+   "fieldname": "warehouse",
+   "fieldtype": "Link",
+   "label": "Warehouse",
+   "options": "Warehouse",
+   "reqd": 1
   }
  ],
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2022-11-24 13:05:11.623968",
+ "modified": "2023-01-10 11:32:09.018760",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Serial and Batch Bundle",
diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
index 554c032..1c9dc15 100644
--- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
+++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
@@ -1,20 +1,114 @@
 # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
 # For license information, please see license.txt
 
+import collections
+
 import frappe
 from frappe import _
 from frappe.model.document import Document
+from frappe.query_builder.functions import Sum
+from frappe.utils import cint, flt, today
+from pypika import Case
 
 
 class SerialandBatchBundle(Document):
 	def validate(self):
 		self.validate_serial_and_batch_no()
+		self.validate_duplicate_serial_and_batch_no()
+
+	def before_save(self):
+		self.set_outgoing_rate()
+
+		if self.ledgers:
+			self.set_total_qty()
+			self.set_avg_rate()
+
+	@frappe.whitelist()
+	def set_warehouse(self):
+		for row in self.ledgers:
+			row.warehouse = self.warehouse
+
+	def set_total_qty(self):
+		self.total_qty = sum([row.qty for row in self.ledgers])
+
+	def set_avg_rate(self):
+		self.total_amount = 0.0
+
+		for row in self.ledgers:
+			rate = flt(row.incoming_rate) or flt(row.outgoing_rate)
+			self.total_amount += flt(row.qty) * rate
+
+		if self.total_qty:
+			self.avg_rate = flt(self.total_amount) / flt(self.total_qty)
+
+	def set_outgoing_rate(self, update_rate=False):
+		if not self.calculate_outgoing_rate():
+			return
+
+		serial_nos = [row.serial_no for row in self.ledgers]
+		data = get_serial_and_batch_ledger(
+			item_code=self.item_code,
+			warehouse=self.ledgers[0].warehouse,
+			serial_nos=serial_nos,
+			fetch_incoming_rate=True,
+		)
+
+		if not data:
+			return
+
+		serial_no_details = {row.serial_no: row for row in data}
+
+		for ledger in self.ledgers:
+			if sn_details := serial_no_details.get(ledger.serial_no):
+				if ledger.outgoing_rate and ledger.outgoing_rate == sn_details.incoming_rate:
+					continue
+
+				ledger.outgoing_rate = sn_details.incoming_rate or 0.0
+				if update_rate:
+					ledger.db_set("outgoing_rate", ledger.outgoing_rate)
+
+	def calculate_outgoing_rate(self):
+		if not (self.has_serial_no and self.ledgers):
+			return
+
+		if not (self.voucher_type and self.voucher_no):
+			return False
+
+		if self.voucher_type in ["Purchase Receipt", "Purchase Invoice"]:
+			return frappe.get_cached_value(self.voucher_type, self.voucher_no, "is_return")
+		elif self.voucher_type in ["Sales Invoice", "Delivery Note"]:
+			return not frappe.get_cached_value(self.voucher_type, self.voucher_no, "is_return")
+		elif self.voucher_type == "Stock Entry":
+			return frappe.get_cached_value(self.voucher_type, self.voucher_no, "purpose") in [
+				"Material Receipt"
+			]
 
 	def validate_serial_and_batch_no(self):
 		if self.item_code and not self.has_serial_no and not self.has_batch_no:
 			msg = f"The Item {self.item_code} does not have Serial No or Batch No"
 			frappe.throw(_(msg))
 
+	def validate_duplicate_serial_and_batch_no(self):
+		serial_nos = []
+		batch_nos = []
+
+		for row in self.ledgers:
+			if row.serial_no:
+				serial_nos.append(row.serial_no)
+
+			if row.batch_no:
+				batch_nos.append(row.batch_no)
+
+		if serial_nos:
+			for key, value in collections.Counter(serial_nos).items():
+				if value > 1:
+					frappe.throw(_(f"Duplicate Serial No {key} found"))
+
+		if batch_nos:
+			for key, value in collections.Counter(batch_nos).items():
+				if value > 1:
+					frappe.throw(_(f"Duplicate Batch No {key} found"))
+
 	def before_cancel(self):
 		self.delink_serial_and_batch_bundle()
 		self.clear_table()
@@ -30,6 +124,35 @@
 	def clear_table(self):
 		self.set("ledgers", [])
 
+	def delink_refernce_from_voucher(self):
+		child_table = f"{self.voucher_type} Item"
+		if self.voucher_type == "Stock Entry":
+			child_table = f"{self.voucher_type} Detail"
+
+		vouchers = frappe.get_all(
+			child_table,
+			fields=["name"],
+			filters={"serial_and_batch_bundle": self.name, "docstatus": 0},
+		)
+
+		for voucher in vouchers:
+			frappe.db.set_value(child_table, voucher.name, "serial_and_batch_bundle", None)
+
+	def delink_reference_from_batch(self):
+		batches = frappe.get_all(
+			"Batch",
+			fields=["name"],
+			filters={"reference_name": self.name, "reference_doctype": "Serial and Batch Bundle"},
+		)
+
+		for batch in batches:
+			frappe.db.set_value("Batch", batch.name, {"reference_name": None, "reference_doctype": None})
+
+	def on_trash(self):
+		self.delink_refernce_from_voucher()
+		self.delink_reference_from_batch()
+		self.clear_table()
+
 
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
@@ -125,3 +248,144 @@
 	frappe.msgprint(_("Serial and Batch Bundle updated"), alert=True)
 
 	return doc
+
+
+def get_serial_and_batch_ledger(**kwargs):
+	kwargs = frappe._dict(kwargs)
+
+	sle_table = frappe.qb.DocType("Stock Ledger Entry")
+	serial_batch_table = frappe.qb.DocType("Serial and Batch Ledger")
+
+	query = (
+		frappe.qb.from_(sle_table)
+		.inner_join(serial_batch_table)
+		.on(sle_table.serial_and_batch_bundle == serial_batch_table.parent)
+		.select(
+			serial_batch_table.serial_no,
+			serial_batch_table.warehouse,
+			serial_batch_table.batch_no,
+			serial_batch_table.qty,
+			serial_batch_table.incoming_rate,
+		)
+		.where((sle_table.item_code == kwargs.item_code) & (sle_table.warehouse == kwargs.warehouse))
+	)
+
+	if kwargs.serial_nos:
+		query = query.where(serial_batch_table.serial_no.isin(kwargs.serial_nos))
+
+	if kwargs.batch_nos:
+		query = query.where(serial_batch_table.batch_no.isin(kwargs.batch_nos))
+
+	if kwargs.fetch_incoming_rate:
+		query = query.where(sle_table.actual_qty > 0)
+
+	return query.run(as_dict=True)
+
+
+def get_copy_of_serial_and_batch_bundle(serial_and_batch_bundle, warehouse):
+	bundle_doc = frappe.copy_doc(serial_and_batch_bundle)
+	for row in bundle_doc.ledgers:
+		row.warehouse = warehouse
+		row.incoming_rate = row.outgoing_rate
+		row.outgoing_rate = 0.0
+
+	return bundle_doc.submit(ignore_permissions=True)
+
+
+@frappe.whitelist()
+def get_auto_data(**kwargs):
+	kwargs = frappe._dict(kwargs)
+
+	if cint(kwargs.has_serial_no):
+		return get_auto_serial_nos(kwargs)
+
+	elif cint(kwargs.has_batch_no):
+		return get_auto_batch_nos(kwargs)
+
+
+def get_auto_serial_nos(kwargs):
+	fields = ["name as serial_no"]
+	if kwargs.has_batch_no:
+		fields.append("batch_no")
+
+	order_by = "creation"
+	if kwargs.based_on == "LIFO":
+		order_by = "creation desc"
+	elif kwargs.based_on == "Expiry":
+		order_by = "amc_expiry_date asc"
+
+	return frappe.get_all(
+		"Serial No",
+		fields=fields,
+		filters={"item_code": kwargs.item_code, "warehouse": kwargs.warehouse},
+		limit=cint(kwargs.qty),
+		order_by=order_by,
+	)
+
+
+def get_auto_batch_nos(kwargs):
+	available_batches = get_available_batches(kwargs)
+
+	qty = flt(kwargs.qty)
+
+	batches = []
+
+	for batch in available_batches:
+		if qty > 0:
+			batch_qty = flt(batch.qty)
+			if qty > batch_qty:
+				batches.append(
+					{
+						"batch_no": batch.batch_no,
+						"qty": batch_qty,
+					}
+				)
+				qty -= batch_qty
+			else:
+				batches.append(
+					{
+						"batch_no": batch.batch_no,
+						"qty": qty,
+					}
+				)
+				qty = 0
+
+	return batches
+
+
+def get_available_batches(kwargs):
+	stock_ledger_entry = frappe.qb.DocType("Stock Ledger Entry")
+	batch_ledger = frappe.qb.DocType("Serial and Batch Ledger")
+	batch_table = frappe.qb.DocType("Batch")
+
+	query = (
+		frappe.qb.from_(stock_ledger_entry)
+		.inner_join(batch_ledger)
+		.on(stock_ledger_entry.serial_and_batch_bundle == batch_ledger.parent)
+		.inner_join(batch_table)
+		.on(batch_ledger.batch_no == batch_table.name)
+		.select(
+			batch_ledger.batch_no,
+			Sum(
+				Case().when(stock_ledger_entry.actual_qty > 0, batch_ledger.qty).else_(batch_ledger.qty * -1)
+			).as_("qty"),
+		)
+		.where(
+			(stock_ledger_entry.item_code == kwargs.item_code)
+			& (stock_ledger_entry.warehouse == kwargs.warehouse)
+			& ((batch_table.expiry_date >= today()) | (batch_table.expiry_date.isnull()))
+		)
+		.groupby(batch_ledger.batch_no)
+	)
+
+	if kwargs.based_on == "LIFO":
+		query = query.orderby(batch_table.creation, order=frappe.qb.desc)
+	elif kwargs.based_on == "Expiry":
+		query = query.orderby(batch_table.expiry_date)
+	else:
+		query = query.orderby(batch_table.creation)
+
+	data = query.run(as_dict=True)
+	data = list(filter(lambda x: x.qty > 0, data))
+
+	return data
diff --git a/erpnext/stock/doctype/serial_and_batch_ledger/serial_and_batch_ledger.json b/erpnext/stock/doctype/serial_and_batch_ledger/serial_and_batch_ledger.json
index 7fa9574..65eaa03 100644
--- a/erpnext/stock/doctype/serial_and_batch_ledger/serial_and_batch_ledger.json
+++ b/erpnext/stock/doctype/serial_and_batch_ledger/serial_and_batch_ledger.json
@@ -5,12 +5,17 @@
  "editable_grid": 1,
  "engine": "InnoDB",
  "field_order": [
+  "item_code",
   "serial_no",
   "batch_no",
   "column_break_2",
   "qty",
   "warehouse",
-  "is_rejected"
+  "section_break_6",
+  "incoming_rate",
+  "column_break_8",
+  "outgoing_rate",
+  "stock_value_difference"
  ],
  "fields": [
   {
@@ -34,6 +39,7 @@
    "options": "Batch"
   },
   {
+   "default": "1",
    "fieldname": "qty",
    "fieldtype": "Float",
    "in_list_view": 1,
@@ -47,21 +53,52 @@
    "options": "Warehouse"
   },
   {
-   "default": "0",
-   "depends_on": "eval:parent.voucher_type == 'Purchase Receipt'",
-   "fieldname": "is_rejected",
-   "fieldtype": "Check",
-   "label": "Is Rejected"
-  },
-  {
    "fieldname": "column_break_2",
    "fieldtype": "Column Break"
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "section_break_6",
+   "fieldtype": "Section Break",
+   "label": "Rate Section"
+  },
+  {
+   "fieldname": "incoming_rate",
+   "fieldtype": "Float",
+   "label": "Incoming Rate",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "outgoing_rate",
+   "fieldtype": "Float",
+   "label": "Outgoing Rate",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_8",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "item_code",
+   "fieldtype": "Link",
+   "label": "Item Code",
+   "options": "Item",
+   "read_only": 1
+  },
+  {
+   "fieldname": "stock_value_difference",
+   "fieldtype": "Float",
+   "label": "Change in Stock Value",
+   "no_copy": 1,
+   "read_only": 1
   }
  ],
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2022-11-24 13:00:23.598351",
+ "modified": "2023-01-10 12:55:57.368650",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Serial and Batch Ledger",
diff --git a/erpnext/stock/doctype/serial_no/serial_no.json b/erpnext/stock/doctype/serial_no/serial_no.json
index 7989b1a..7f22af1 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.json
+++ b/erpnext/stock/doctype/serial_no/serial_no.json
@@ -12,24 +12,13 @@
   "column_break0",
   "serial_no",
   "item_code",
-  "warehouse",
   "batch_no",
+  "warehouse",
   "column_break1",
   "item_name",
   "description",
   "item_group",
   "brand",
-  "sales_order",
-  "purchase_details",
-  "column_break2",
-  "purchase_document_type",
-  "purchase_document_no",
-  "purchase_date",
-  "purchase_time",
-  "purchase_rate",
-  "column_break3",
-  "supplier",
-  "supplier_name",
   "asset_details",
   "asset",
   "asset_status",
@@ -38,14 +27,6 @@
   "employee",
   "delivery_details",
   "delivery_document_type",
-  "delivery_document_no",
-  "delivery_date",
-  "delivery_time",
-  "column_break5",
-  "customer",
-  "customer_name",
-  "invoice_details",
-  "sales_invoice",
   "warranty_amc_details",
   "column_break6",
   "warranty_expiry_date",
@@ -56,7 +37,6 @@
   "more_info",
   "serial_no_details",
   "company",
-  "status",
   "work_order"
  ],
  "fields": [
@@ -91,29 +71,6 @@
    "reqd": 1
   },
   {
-   "description": "Warehouse can only be changed via Stock Entry / Delivery Note / Purchase Receipt",
-   "fieldname": "warehouse",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "in_standard_filter": 1,
-   "label": "Warehouse",
-   "no_copy": 1,
-   "oldfieldname": "warehouse",
-   "oldfieldtype": "Link",
-   "options": "Warehouse",
-   "read_only": 1,
-   "search_index": 1
-  },
-  {
-   "fieldname": "batch_no",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "in_standard_filter": 1,
-   "label": "Batch No",
-   "options": "Batch",
-   "read_only": 1
-  },
-  {
    "fieldname": "column_break1",
    "fieldtype": "Column Break"
   },
@@ -151,84 +108,6 @@
    "read_only": 1
   },
   {
-   "fieldname": "sales_order",
-   "fieldtype": "Link",
-   "label": "Sales Order",
-   "options": "Sales Order"
-  },
-  {
-   "fieldname": "purchase_details",
-   "fieldtype": "Section Break",
-   "label": "Purchase / Manufacture Details"
-  },
-  {
-   "fieldname": "column_break2",
-   "fieldtype": "Column Break",
-   "width": "50%"
-  },
-  {
-   "fieldname": "purchase_document_type",
-   "fieldtype": "Link",
-   "label": "Creation Document Type",
-   "no_copy": 1,
-   "options": "DocType",
-   "read_only": 1
-  },
-  {
-   "fieldname": "purchase_document_no",
-   "fieldtype": "Dynamic Link",
-   "label": "Creation Document No",
-   "no_copy": 1,
-   "options": "purchase_document_type",
-   "read_only": 1
-  },
-  {
-   "fieldname": "purchase_date",
-   "fieldtype": "Date",
-   "label": "Creation Date",
-   "no_copy": 1,
-   "oldfieldname": "purchase_date",
-   "oldfieldtype": "Date",
-   "read_only": 1
-  },
-  {
-   "fieldname": "purchase_time",
-   "fieldtype": "Time",
-   "label": "Creation Time",
-   "no_copy": 1,
-   "read_only": 1
-  },
-  {
-   "fieldname": "purchase_rate",
-   "fieldtype": "Currency",
-   "label": "Incoming Rate",
-   "no_copy": 1,
-   "oldfieldname": "purchase_rate",
-   "oldfieldtype": "Currency",
-   "options": "Company:company:default_currency",
-   "read_only": 1
-  },
-  {
-   "fieldname": "column_break3",
-   "fieldtype": "Column Break",
-   "width": "50%"
-  },
-  {
-   "fieldname": "supplier",
-   "fieldtype": "Link",
-   "label": "Supplier",
-   "no_copy": 1,
-   "options": "Supplier"
-  },
-  {
-   "bold": 1,
-   "fieldname": "supplier_name",
-   "fieldtype": "Data",
-   "label": "Supplier Name",
-   "no_copy": 1,
-   "read_only": 1
-  },
-  {
    "fieldname": "asset_details",
    "fieldtype": "Section Break",
    "label": "Asset Details"
@@ -284,67 +163,6 @@
    "read_only": 1
   },
   {
-   "fieldname": "delivery_document_no",
-   "fieldtype": "Dynamic Link",
-   "label": "Delivery Document No",
-   "no_copy": 1,
-   "options": "delivery_document_type",
-   "read_only": 1
-  },
-  {
-   "fieldname": "delivery_date",
-   "fieldtype": "Date",
-   "label": "Delivery Date",
-   "no_copy": 1,
-   "oldfieldname": "delivery_date",
-   "oldfieldtype": "Date",
-   "read_only": 1
-  },
-  {
-   "fieldname": "delivery_time",
-   "fieldtype": "Time",
-   "label": "Delivery Time",
-   "no_copy": 1,
-   "read_only": 1
-  },
-  {
-   "fieldname": "column_break5",
-   "fieldtype": "Column Break",
-   "width": "50%"
-  },
-  {
-   "fieldname": "customer",
-   "fieldtype": "Link",
-   "label": "Customer",
-   "no_copy": 1,
-   "oldfieldname": "customer",
-   "oldfieldtype": "Link",
-   "options": "Customer",
-   "print_hide": 1
-  },
-  {
-   "bold": 1,
-   "fieldname": "customer_name",
-   "fieldtype": "Data",
-   "label": "Customer Name",
-   "no_copy": 1,
-   "oldfieldname": "customer_name",
-   "oldfieldtype": "Data",
-   "read_only": 1
-  },
-  {
-   "fieldname": "invoice_details",
-   "fieldtype": "Section Break",
-   "label": "Invoice Details"
-  },
-  {
-   "fieldname": "sales_invoice",
-   "fieldtype": "Link",
-   "label": "Sales Invoice",
-   "options": "Sales Invoice",
-   "read_only": 1
-  },
-  {
    "fieldname": "warranty_amc_details",
    "fieldtype": "Section Break",
    "label": "Warranty / AMC Details"
@@ -408,6 +226,7 @@
   {
    "fieldname": "company",
    "fieldtype": "Link",
+   "in_list_view": 1,
    "label": "Company",
    "options": "Company",
    "remember_last_selected_value": 1,
@@ -416,24 +235,29 @@
    "set_only_once": 1
   },
   {
-   "fieldname": "status",
-   "fieldtype": "Select",
-   "in_standard_filter": 1,
-   "label": "Status",
-   "options": "\nActive\nInactive\nDelivered\nExpired",
-   "read_only": 1
-  },
-  {
    "fieldname": "work_order",
    "fieldtype": "Link",
    "label": "Work Order",
    "options": "Work Order"
+  },
+  {
+   "fieldname": "warehouse",
+   "fieldtype": "Link",
+   "label": "Warehouse",
+   "options": "Warehouse",
+   "read_only": 1
+  },
+  {
+   "fieldname": "batch_no",
+   "fieldtype": "Link",
+   "label": "Batch No",
+   "options": "Batch"
   }
  ],
  "icon": "fa fa-barcode",
  "idx": 1,
  "links": [],
- "modified": "2023-04-14 15:58:46.139887",
+ "modified": "2023-04-15 15:58:46.139887",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Serial No",
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index 98beda0..6d92cc3 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -9,17 +9,7 @@
 from frappe import ValidationError, _
 from frappe.model.naming import make_autoname
 from frappe.query_builder.functions import Coalesce
-from frappe.utils import (
-	add_days,
-	cint,
-	cstr,
-	flt,
-	get_link_to_form,
-	getdate,
-	now,
-	nowdate,
-	safe_json_loads,
-)
+from frappe.utils import cint, flt, get_link_to_form, getdate, now, nowdate, safe_json_loads
 
 from erpnext.controllers.stock_controller import StockController
 from erpnext.stock.get_item_details import get_reserved_qty_for_so
@@ -80,19 +70,6 @@
 			)
 
 		self.set_maintenance_status()
-		self.validate_warehouse()
-		self.validate_item()
-		self.set_status()
-
-	def set_status(self):
-		if self.delivery_document_type:
-			self.status = "Delivered"
-		elif self.warranty_expiry_date and getdate(self.warranty_expiry_date) <= getdate(nowdate()):
-			self.status = "Expired"
-		elif not self.warehouse:
-			self.status = "Inactive"
-		else:
-			self.status = "Active"
 
 	def set_maintenance_status(self):
 		if not self.warranty_expiry_date and not self.amc_expiry_date:
@@ -110,127 +87,6 @@
 		if self.warranty_expiry_date and getdate(self.warranty_expiry_date) >= getdate(nowdate()):
 			self.maintenance_status = "Under Warranty"
 
-	def validate_warehouse(self):
-		if not self.get("__islocal"):
-			item_code, warehouse = frappe.db.get_value("Serial No", self.name, ["item_code", "warehouse"])
-			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:
-				frappe.throw(_("Warehouse cannot be changed for Serial No."), SerialNoCannotCannotChangeError)
-
-	def validate_item(self):
-		"""
-		Validate whether serial no is required for this item
-		"""
-		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)
-			)
-
-		self.item_group = item.item_group
-		self.description = item.description
-		self.item_name = item.item_name
-		self.brand = item.brand
-		self.warranty_period = item.warranty_period
-
-	def set_purchase_details(self, purchase_sle):
-		if purchase_sle:
-			self.purchase_document_type = purchase_sle.voucher_type
-			self.purchase_document_no = purchase_sle.voucher_no
-			self.purchase_date = purchase_sle.posting_date
-			self.purchase_time = purchase_sle.posting_time
-			self.purchase_rate = purchase_sle.incoming_rate
-			if purchase_sle.voucher_type in ("Purchase Receipt", "Purchase Invoice"):
-				self.supplier, self.supplier_name = frappe.db.get_value(
-					purchase_sle.voucher_type, purchase_sle.voucher_no, ["supplier", "supplier_name"]
-				)
-
-			# If sales return entry
-			if self.purchase_document_type == "Delivery Note":
-				self.sales_invoice = None
-		else:
-			for fieldname in (
-				"purchase_document_type",
-				"purchase_document_no",
-				"purchase_date",
-				"purchase_time",
-				"purchase_rate",
-				"supplier",
-				"supplier_name",
-			):
-				self.set(fieldname, None)
-
-	def set_sales_details(self, delivery_sle):
-		if delivery_sle:
-			self.delivery_document_type = delivery_sle.voucher_type
-			self.delivery_document_no = delivery_sle.voucher_no
-			self.delivery_date = delivery_sle.posting_date
-			self.delivery_time = delivery_sle.posting_time
-			if delivery_sle.voucher_type in ("Delivery Note", "Sales Invoice"):
-				self.customer, self.customer_name = frappe.db.get_value(
-					delivery_sle.voucher_type, delivery_sle.voucher_no, ["customer", "customer_name"]
-				)
-			if self.warranty_period:
-				self.warranty_expiry_date = add_days(
-					cstr(delivery_sle.posting_date), cint(self.warranty_period)
-				)
-		else:
-			for fieldname in (
-				"delivery_document_type",
-				"delivery_document_no",
-				"delivery_date",
-				"delivery_time",
-				"customer",
-				"customer_name",
-				"warranty_expiry_date",
-			):
-				self.set(fieldname, None)
-
-	def get_last_sle(self, serial_no=None):
-		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]
-
-			if len(sle_dict.get("incoming", [])) - len(sle_dict.get("outgoing", [])) > 0:
-				entries["last_sle"] = sle_dict["incoming"][0]
-			else:
-				entries["last_sle"] = sle_dict["outgoing"][0]
-				entries["delivery_sle"] = sle_dict["outgoing"][0]
-
-		return entries
-
-	def get_stock_ledger_entries(self, serial_no=None):
-		sle_dict = {}
-		if not serial_no:
-			serial_no = self.name
-
-		print("serial_no", serial_no)
-		for sle in frappe.db.sql(
-			"""
-			SELECT sle.voucher_type, sle.voucher_no, serial_and_batch_bundle,
-				sle.posting_date, sle.posting_time, sle.incoming_rate, sle.actual_qty, snb.serial_no
-			FROM
-				`tabStock Ledger Entry` sle, `tabSerial and Batch Ledger` snb
-			WHERE
-				sle.item_code=%s AND sle.company = %s
-				AND sle.is_cancelled = 0
-				AND snb.serial_no = %s and snb.parent = sle.serial_and_batch_bundle
-			ORDER BY
-				sle.posting_date desc, sle.posting_time desc, sle.creation desc""",
-			(self.item_code, self.company, serial_no),
-			as_dict=1,
-		):
-			if serial_no.upper() in get_serial_nos(sle.serial_and_batch_bundle):
-				if cint(sle.actual_qty) > 0:
-					sle_dict.setdefault("incoming", []).append(sle)
-				else:
-					sle_dict.setdefault("outgoing", []).append(sle)
-
-		return sle_dict
-
 	def on_trash(self):
 		sl_entries = frappe.db.sql(
 			"""select serial_no from `tabStock Ledger Entry`
@@ -251,19 +107,11 @@
 				_("Cannot delete Serial No {0}, as it is used in stock transactions").format(self.name)
 			)
 
-	def update_serial_no_reference(self, serial_no=None):
-		last_sle = self.get_last_sle(serial_no)
-		print(last_sle)
-		self.set_purchase_details(last_sle.get("purchase_sle"))
-		self.set_sales_details(last_sle.get("delivery_sle"))
-		self.set_maintenance_status()
-		self.set_status()
-
 
 def process_serial_no(sle):
 	item_det = get_item_details(sle.item_code)
 	validate_serial_no(sle, item_det)
-	update_serial_nos(sle, item_det)
+	create_serial_nos(sle, item_det)
 
 
 def validate_serial_no(sle, item_det):
@@ -277,6 +125,7 @@
 				SerialNoNotRequiredError,
 			)
 	elif not sle.is_cancelled:
+		return
 		if serial_nos:
 			if cint(sle.actual_qty) != flt(sle.actual_qty):
 				frappe.throw(
@@ -440,6 +289,7 @@
 				_("Serial Nos Required for Serialized Item {0}").format(sle.item_code), SerialNoRequiredError
 			)
 	elif serial_nos:
+		return
 		# SLE is being cancelled and has serial nos
 		for serial_no in serial_nos:
 			check_serial_no_validity_on_cancel(serial_no, sle)
@@ -528,7 +378,7 @@
 	return allow_serial_nos
 
 
-def update_serial_nos(sle, item_det):
+def create_serial_nos(sle, item_det):
 	if sle.skip_update_serial_no:
 		return
 	if (
@@ -538,7 +388,7 @@
 		and item_det.has_serial_no == 1
 		and item_det.serial_no_series
 	):
-		bundle = make_serial_bundle(sle, item_det)
+		bundle = make_serial_no_bundle(sle, item_det)
 		if bundle:
 			sle.db_set("serial_and_batch_bundle", bundle.name)
 			child_doctype = sle.voucher_type + " Item"
@@ -552,64 +402,127 @@
 			)
 
 	elif sle.serial_and_batch_bundle:
-		auto_make_serial_nos(sle)
-
-
-def make_serial_bundle(sle, item_details):
-	sr_nos = auto_create_serial_nos(sle, item_details)
-
-	if sr_nos:
-		sn_doc = frappe.new_doc("Serial and Batch Bundle")
-		sn_doc.item_code = item_details.name
-		sn_doc.item_name = item_details.item_name
-		sn_doc.item_group = item_details.item_group
-		sn_doc.has_serial_no = item_details.has_serial_no
-		sn_doc.has_batch_no = item_details.has_batch_no
-		sn_doc.voucher_type = sle.voucher_type
-		sn_doc.voucher_no = sle.voucher_no
-		sn_doc.flags.ignore_mandatory = True
-		sn_doc.qty = sle.actual_qty
-		sn_doc.insert()
-
-		batch_no = ""
-		if item_details.has_batch_no:
-			batch_no = create_batch_for_serial_no(sle)
-
-		ledgers = []
-		fields = [
-			"name",
-			"serial_no",
-			"batch_no",
-			"warehouse",
-			"qty",
-			"parent",
-			"parenttype",
-			"parentfield",
-		]
-
-		for serial_no in sr_nos:
-			ledgers.append(
-				(
-					frappe.generate_hash("", 10),
-					serial_no,
-					batch_no,
-					sle.warehouse,
-					1,
-					sn_doc.name,
-					sn_doc.doctype,
-					"ledgers",
-				)
+		if sle.is_cancelled:
+			frappe.db.set_value(
+				"Serial and Batch Bundle",
+				sle.serial_and_batch_bundle,
+				"is_cancelled",
+				1,
 			)
 
-		frappe.db.bulk_insert(
-			"Serial and Batch Ledger",
-			fields=fields,
-			values=set(ledgers),
-			ignore_duplicates=True,
+		if item_det.has_serial_no:
+			update_warehouse_in_serial_no(sle, item_det)
+
+
+def update_warehouse_in_serial_no(sle, item_det):
+	serial_nos = get_serial_nos(sle.serial_and_batch_bundle)
+	serial_no_data = get_serial_nos_warehouse(sle.item_code, serial_nos)
+
+	if not serial_no_data:
+		for serial_no in serial_nos:
+			frappe.db.set_value("Serial No", serial_no, "warehouse", None)
+
+	else:
+		for row in serial_no_data:
+			if not row.serial_no:
+				continue
+
+			warehouse = row.warehouse if row.actual_qty > 0 else None
+			frappe.db.set_value("Serial No", row.serial_no, "warehouse", warehouse)
+
+
+def get_serial_nos_warehouse(item_code, serial_nos):
+	ledger_table = frappe.qb.DocType("Serial and Batch Ledger")
+	sle_table = frappe.qb.DocType("Stock Ledger Entry")
+
+	return (
+		frappe.qb.from_(ledger_table)
+		.inner_join(sle_table)
+		.on(ledger_table.parent == sle_table.serial_and_batch_bundle)
+		.select(
+			ledger_table.serial_no,
+			sle_table.actual_qty,
+			ledger_table.warehouse,
+		)
+		.where(
+			(ledger_table.serial_no.isin(serial_nos))
+			& (sle_table.is_cancelled == 0)
+			& (sle_table.item_code == item_code)
+			& (sle_table.serial_and_batch_bundle.isnotnull())
+		)
+		.orderby(sle_table.posting_date, order=frappe.qb.desc)
+		.orderby(sle_table.posting_time, order=frappe.qb.desc)
+		.orderby(sle_table.creation, order=frappe.qb.desc)
+		.groupby(ledger_table.serial_no)
+	).run(as_dict=True)
+
+
+def make_serial_no_bundle(sle, item_details):
+	sr_nos = auto_create_serial_nos(sle, item_details)
+	if sr_nos:
+		return make_serial_batch_bundle(sle, item_details, sr_nos)
+
+
+def make_serial_batch_bundle(sle, item_details, sr_nos):
+	sn_doc = frappe.new_doc("Serial and Batch Bundle")
+	sn_doc.item_code = item_details.name
+	sn_doc.item_name = item_details.item_name
+	sn_doc.item_group = item_details.item_group
+	sn_doc.has_serial_no = item_details.has_serial_no
+	sn_doc.has_batch_no = item_details.has_batch_no
+	sn_doc.voucher_type = sle.voucher_type
+	sn_doc.voucher_no = sle.voucher_no
+	sn_doc.flags.ignore_mandatory = True
+	sn_doc.flags.ignore_validate = True
+	sn_doc.total_qty = sle.actual_qty
+	sn_doc.avg_rate = sle.incoming_rate
+	sn_doc.total_amount = flt(sle.actual_qty) * flt(sle.incoming_rate)
+	sn_doc.insert()
+
+	batch_no = ""
+	if item_details.has_batch_no:
+		batch_no = create_batch_for_serial_no(sle)
+
+	add_serial_no_to_bundle(sn_doc, sle, sr_nos, batch_no, item_details)
+
+	sn_doc.load_from_db()
+	sn_doc.flags.ignore_validate = True
+	return sn_doc.submit()
+
+
+def add_serial_no_to_bundle(sn_doc, sle, sr_nos, batch_no, item_details):
+	ledgers = []
+
+	fields = [
+		"name",
+		"serial_no",
+		"batch_no",
+		"warehouse",
+		"item_code",
+		"qty",
+		"incoming_rate",
+		"parent",
+		"parenttype",
+		"parentfield",
+	]
+
+	for serial_no in sr_nos:
+		ledgers.append(
+			(
+				frappe.generate_hash("Serial and Batch Ledger", 10),
+				serial_no,
+				batch_no,
+				sle.warehouse,
+				item_details.item_code,
+				1,
+				sle.incoming_rate,
+				sn_doc.name,
+				sn_doc.doctype,
+				"ledgers",
+			)
 		)
 
-		sn_doc.load_from_db()
-		return sn_doc.submit()
+	frappe.db.bulk_insert("Serial and Batch Ledger", fields=fields, values=set(ledgers))
 
 
 def create_batch_for_serial_no(sle):
@@ -629,6 +542,10 @@
 def auto_create_serial_nos(sle, item_details) -> List[str]:
 	sr_nos = []
 	serial_nos_details = []
+	current_series = frappe.db.sql(
+		"select current from `tabSeries` where name = %s", item_details.serial_no_series
+	)
+
 	for i in range(cint(sle.actual_qty)):
 		serial_no = make_autoname(item_details.serial_no_series, "Serial No")
 		sr_nos.append(serial_no)
@@ -640,13 +557,8 @@
 				now(),
 				frappe.session.user,
 				frappe.session.user,
-				sle.voucher_type,
-				sle.voucher_no,
 				sle.warehouse,
 				sle.company,
-				sle.posting_date,
-				sle.posting_time,
-				sle.incoming_rate,
 				sle.item_code,
 				item_details.item_name,
 				item_details.description,
@@ -661,24 +573,14 @@
 			"modified",
 			"owner",
 			"modified_by",
-			"purchase_document_type",
-			"purchase_document_no",
 			"warehouse",
 			"company",
-			"purchase_date",
-			"purchase_time",
-			"purchase_rate",
 			"item_code",
 			"item_name",
 			"description",
 		]
 
-		frappe.db.bulk_insert(
-			"Serial No",
-			fields=fields,
-			values=set(serial_nos_details),
-			ignore_duplicates=True,
-		)
+		frappe.db.bulk_insert("Serial No", fields=fields, values=set(serial_nos_details))
 
 	return sr_nos
 
@@ -698,41 +600,6 @@
 	return sr_no
 
 
-def auto_make_serial_nos(args):
-	serial_nos = get_serial_nos(args.get("serial_and_batch_bundle"))
-	created_numbers = []
-	voucher_type = args.get("voucher_type")
-	item_code = args.get("item_code")
-	for serial_no in serial_nos:
-		is_new = False
-		if frappe.db.exists("Serial No", serial_no):
-			sr = frappe.get_cached_doc("Serial No", serial_no)
-		elif args.get("actual_qty", 0) > 0:
-			sr = frappe.new_doc("Serial No")
-			is_new = True
-
-		sr = update_args_for_serial_no(sr, serial_no, args, is_new=is_new)
-		if is_new:
-			created_numbers.append(sr.name)
-
-	form_links = list(map(lambda d: get_link_to_form("Serial No", d), created_numbers))
-
-	# Setting up tranlated title field for all cases
-	singular_title = _("Serial Number Created")
-	multiple_title = _("Serial Numbers Created")
-
-	if voucher_type:
-		multiple_title = singular_title = _("{0} Created").format(voucher_type)
-
-	if len(form_links) == 1:
-		frappe.msgprint(_("Serial No {0} Created").format(form_links[0]), singular_title)
-	elif len(form_links) > 0:
-		message = _("The following serial numbers were created: <br><br> {0}").format(
-			get_items_html(form_links, item_code)
-		)
-		frappe.msgprint(message, multiple_title)
-
-
 def get_items_html(serial_nos, item_code):
 	body = ", ".join(serial_nos)
 	return """<details><summary>
@@ -773,36 +640,8 @@
 	return "\n".join(serial_no_list)
 
 
-def update_args_for_serial_no(serial_no_doc, serial_no, args, is_new=False):
-	for field in ["item_code", "work_order", "company", "batch_no", "supplier", "location"]:
-		if args.get(field):
-			serial_no_doc.set(field, args.get(field))
-
-	serial_no_doc.via_stock_ledger = args.get("via_stock_ledger") or True
-	serial_no_doc.warehouse = args.get("warehouse") if args.get("actual_qty", 0) > 0 else None
-
-	if is_new:
-		serial_no_doc.serial_no = serial_no
-
-	if (
-		serial_no_doc.sales_order
-		and args.get("voucher_type") == "Stock Entry"
-		and not args.get("actual_qty", 0) > 0
-	):
-		serial_no_doc.sales_order = None
-
-	serial_no_doc.validate_item()
-	serial_no_doc.update_serial_no_reference(serial_no)
-
-	if is_new:
-		serial_no_doc.db_insert()
-	else:
-		serial_no_doc.db_update()
-
-	return serial_no_doc
-
-
 def update_serial_nos_after_submit(controller, parentfield):
+	return
 	stock_ledger_entries = frappe.db.sql(
 		"""select voucher_detail_no, serial_no, actual_qty, warehouse
 		from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s""",
diff --git a/erpnext/stock/doctype/serial_no/serial_no_list.js b/erpnext/stock/doctype/serial_no/serial_no_list.js
deleted file mode 100644
index 7526d1d..0000000
--- a/erpnext/stock/doctype/serial_no/serial_no_list.js
+++ /dev/null
@@ -1,14 +0,0 @@
-frappe.listview_settings['Serial No'] = {
-	add_fields: ["item_code", "warehouse", "warranty_expiry_date", "delivery_document_type"],
-	get_indicator: (doc) => {
-		if (doc.delivery_document_type) {
-			return [__("Delivered"), "green", "delivery_document_type,is,set"];
-		} else if (doc.warranty_expiry_date && frappe.datetime.get_diff(doc.warranty_expiry_date, frappe.datetime.nowdate()) <= 0) {
-			return [__("Expired"), "red", "warranty_expiry_date,not in,|warranty_expiry_date,<=,Today|delivery_document_type,is,not set"];
-		} else if (!doc.warehouse) {
-			return [__("Inactive"), "grey", "warehouse,is,not set"];
-		} else {
-			return [__("Active"), "green", "delivery_document_type,is,not set"];
-		}
-	}
-};
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 55b950b..3263ed4 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -29,6 +29,9 @@
 from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
 from erpnext.stock.doctype.batch.batch import get_batch_no, get_batch_qty, set_batch_nos
 from erpnext.stock.doctype.item.item import get_item_defaults
+from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import (
+	get_copy_of_serial_and_batch_bundle,
+)
 from erpnext.stock.doctype.serial_no.serial_no import (
 	get_serial_nos,
 	update_serial_nos_after_submit,
@@ -232,7 +235,12 @@
 		self.update_work_order()
 		self.update_stock_ledger()
 
-		self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Repost Item Valuation")
+		self.ignore_linked_doctypes = (
+			"GL Entry",
+			"Stock Ledger Entry",
+			"Repost Item Valuation",
+			"Serial and Batch Bundle",
+		)
 
 		self.make_gl_entries_on_cancel()
 		self.repost_future_sle_and_gle()
@@ -1208,6 +1216,11 @@
 	def get_sle_for_target_warehouse(self, sl_entries, finished_item_row):
 		for d in self.get("items"):
 			if cstr(d.t_warehouse):
+				if d.s_warehouse and d.serial_and_batch_bundle:
+					d.serial_and_batch_bundle = get_copy_of_serial_and_batch_bundle(
+						d.serial_and_batch_bundle, d.t_warehouse
+					)
+
 				sle = self.get_sl_entries(
 					d,
 					{
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
index 0df0a04..4ad6b26 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
@@ -32,9 +32,11 @@
   "stock_uom",
   "project",
   "serial_and_batch_bundle",
+  "has_batch_no",
   "batch_no",
   "column_break_26",
   "fiscal_year",
+  "has_serial_no",
   "serial_no",
   "is_cancelled",
   "to_rename"
@@ -317,6 +319,20 @@
    "label": "Serial and Batch Bundle",
    "options": "Serial and Batch Bundle",
    "search_index": 1
+  },
+  {
+   "default": "0",
+   "fetch_from": "item_code.has_batch_no",
+   "fieldname": "has_batch_no",
+   "fieldtype": "Check",
+   "label": "Has Batch No"
+  },
+  {
+   "default": "0",
+   "fetch_from": "item_code.has_serial_no",
+   "fieldname": "has_serial_no",
+   "fieldtype": "Check",
+   "label": "Has Serial No"
   }
  ],
  "hide_toolbar": 1,
@@ -325,7 +341,7 @@
  "in_create": 1,
  "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2022-11-24 13:14:31.974743",
+ "modified": "2022-12-28 14:50:56.359348",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Ledger Entry",
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
index 1bcea69..c95d821 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -8,7 +8,7 @@
 from frappe import _
 from frappe.core.doctype.role.role import get_users
 from frappe.model.document import Document
-from frappe.utils import add_days, cint, formatdate, get_datetime, getdate
+from frappe.utils import add_days, cint, formatdate, get_datetime, get_link_to_form, getdate
 
 from erpnext.accounts.utils import get_fiscal_year
 from erpnext.controllers.item_variant import ItemTemplateCannotHaveStock
@@ -47,6 +47,7 @@
 		self.validate_and_set_fiscal_year()
 		self.block_transactions_against_group_warehouse()
 		self.validate_with_last_transaction_posting_time()
+		self.process_serial_and_batch_bundle()
 
 	def on_submit(self):
 		self.check_stock_frozen_date()
@@ -103,15 +104,20 @@
 		if item_detail.has_serial_no or item_detail.has_batch_no:
 			if not self.serial_and_batch_bundle:
 				frappe.throw(_(f"Serial No and Batch No are mandatory for Item {self.item_code}"))
-			elif self.item_code != frappe.get_cached_value(
-				"Serial and Batch Bundle", self.serial_and_batch_bundle, "item_code"
-			):
-				frappe.throw(
-					_(
-						f"Serial No and Batch No Bundle {self.serial_and_batch_bundle} is not for Item {self.item_code}"
-					)
+			else:
+				bundle_data = frappe.get_cached_value(
+					"Serial and Batch Bundle", self.serial_and_batch_bundle, ["item_code", "docstatus"], as_dict=1
 				)
 
+				if self.item_code != bundle_data.item_code:
+					frappe.throw(
+						_(f"Serial and Batch Bundle {self.serial_and_batch_bundle} is not for Item {self.item_code}")
+					)
+
+				if bundle_data.docstatus != 1:
+					link = get_link_to_form("Serial and Batch Bundle", self.serial_and_batch_bundle)
+					frappe.throw(_(f"Serial and Batch Bundle {link} should be submitted first"))
+
 		if self.serial_and_batch_bundle and not (item_detail.has_serial_no or item_detail.has_batch_no):
 			frappe.throw(_(f"Serial No and Batch No are not allowed for Item {self.item_code}"))
 
@@ -211,6 +217,36 @@
 					msg += "<br>" + "<br>".join(authorized_users)
 					frappe.throw(msg, BackDatedStockTransaction, title=_("Backdated Stock Entry"))
 
+	def process_serial_and_batch_bundle(self):
+		if self.serial_and_batch_bundle:
+			self.update_warehouse_and_voucher_no()
+			self.set_outgoing_rate()
+
+	def update_warehouse_and_voucher_no(self):
+		voucher_no = self.name if not self.is_cancelled else None
+		frappe.db.set_value(
+			"Serial and Batch Bundle", self.serial_and_batch_bundle, "voucher_no", voucher_no
+		)
+
+		if not self.is_cancelled:
+			frappe.db.sql(
+				f"""
+				UPDATE `tabSerial and Batch Ledger`
+				SET warehouse = {frappe.db.escape(self.warehouse)}
+				WHERE parent = {frappe.db.escape(self.serial_and_batch_bundle)}
+				AND (
+					warehouse is NULL or warehouse = '' or
+					warehouse != {frappe.db.escape(self.warehouse)}
+				)"""
+			)
+
+	def set_outgoing_rate(self):
+		if self.is_cancelled:
+			return
+
+		doc = frappe.get_cached_doc("Serial and Batch Bundle", self.serial_and_batch_bundle)
+		doc.set_outgoing_rate()
+
 	def on_cancel(self):
 		msg = _("Individual Stock Ledger Entry cannot be cancelled.")
 		msg += "<br>" + _("Please cancel related transaction.")
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 8d8b69d..525a0b0 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -60,8 +60,13 @@
 		update_serial_nos_after_submit(self, "items")
 
 	def on_cancel(self):
-		self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Repost Item Valuation")
 		self.validate_reserved_stock()
+		self.ignore_linked_doctypes = (
+			"GL Entry",
+			"Stock Ledger Entry",
+			"Repost Item Valuation",
+			"Serial and Batch Bundle",
+		)
 		self.make_sle_on_cancel()
 		self.make_gl_entries_on_cancel()
 		self.repost_future_sle_and_gle()
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index f3adefb..3b01287 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -19,7 +19,6 @@
 from erpnext.setup.doctype.brand.brand import get_brand_defaults
 from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
 from erpnext.setup.utils import get_exchange_rate
-from erpnext.stock.doctype.batch.batch import get_batch_no
 from erpnext.stock.doctype.item.item import get_item_defaults, get_uom_conv_factor
 from erpnext.stock.doctype.item_manufacturer.item_manufacturer import get_item_manufacturer_part_no
 from erpnext.stock.doctype.price_list.price_list import get_price_list_details
@@ -160,13 +159,6 @@
 		and out.warehouse
 		and out.stock_qty > 0
 	):
-
-		if out.has_batch_no and not args.get("batch_no"):
-			out.batch_no = get_batch_no(out.item_code, out.warehouse, out.qty)
-			actual_batch_qty = get_batch_qty(out.batch_no, out.warehouse, out.item_code)
-			if actual_batch_qty:
-				out.update(actual_batch_qty)
-
 		if out.has_serial_no and args.get("batch_no"):
 			reserved_so = get_so_reservation_for_item(args)
 			out.batch_no = args.get("batch_no")
diff --git a/erpnext/stock/stock_balance.py b/erpnext/stock/stock_balance.py
index e3cbb43..4886755 100644
--- a/erpnext/stock/stock_balance.py
+++ b/erpnext/stock/stock_balance.py
@@ -295,19 +295,3 @@
 				"posting_time": posting_time,
 			}
 		)
-
-
-def reset_serial_no_status_and_warehouse(serial_nos=None):
-	if not serial_nos:
-		serial_nos = frappe.db.sql_list("""select name from `tabSerial No` where docstatus = 0""")
-		for serial_no in serial_nos:
-			try:
-				sr = frappe.get_doc("Serial No", serial_no)
-				last_sle = sr.get_last_sle()
-				if flt(last_sle.actual_qty) > 0:
-					sr.warehouse = last_sle.warehouse
-
-				sr.via_stock_ledger = True
-				sr.save()
-			except Exception:
-				pass
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 2945c3d..e70e7f1 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -69,6 +69,9 @@
 			if sle.serial_no and not via_landed_cost_voucher:
 				validate_serial_no(sle)
 
+			if not cancel and sle["actual_qty"] > 0 and sle.get("serial_and_batch_bundle"):
+				set_incoming_rate_for_serial_and_batch(sle)
+
 			if cancel:
 				sle["actual_qty"] = -flt(sle.get("actual_qty"))
 
@@ -104,6 +107,18 @@
 				)
 
 
+def set_incoming_rate_for_serial_and_batch(row):
+	frappe.db.sql(
+		"""
+		UPDATE `tabSerial and Batch Ledger`
+			SET incoming_rate = %s
+		WHERE
+			parent = %s
+	""",
+		(row.get("incoming_rate"), row.get("serial_and_batch_bundle")),
+	)
+
+
 def repost_current_voucher(args, allow_negative_stock=False, via_landed_cost_voucher=False):
 	if args.get("actual_qty") or args.get("voucher_type") == "Stock Reconciliation":
 		if not args.get("posting_date"):
@@ -659,8 +674,6 @@
 				self.new_items_found = True
 
 	def process_sle(self, sle):
-		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
-
 		# previous sle data for this warehouse
 		self.wh_data = self.data[sle.warehouse]
 		self.affected_transactions.add((sle.voucher_type, sle.voucher_no))
@@ -692,7 +705,7 @@
 		):
 			sle.outgoing_rate = get_incoming_rate_for_inter_company_transfer(sle)
 
-		if get_serial_nos(sle.serial_no):
+		if sle.serial_and_batch_bundle and sle.has_serial_no:
 			self.get_serialized_values(sle)
 			self.wh_data.qty_after_transaction += flt(sle.actual_qty)
 			if sle.voucher_type == "Stock Reconciliation":
@@ -701,9 +714,7 @@
 			self.wh_data.stock_value = flt(self.wh_data.qty_after_transaction) * flt(
 				self.wh_data.valuation_rate
 			)
-		elif sle.batch_no and frappe.db.get_value(
-			"Batch", sle.batch_no, "use_batchwise_valuation", cache=True
-		):
+		elif sle.serial_and_batch_bundle and sle.has_batch_no:
 			self.update_batched_values(sle)
 		else:
 			if sle.voucher_type == "Stock Reconciliation" and not sle.batch_no:
@@ -963,9 +974,22 @@
 				item.db_update()
 
 	def get_serialized_values(self, sle):
-		incoming_rate = flt(sle.incoming_rate)
+		ledger = frappe.db.get_value(
+			"Serial and Batch Bundle",
+			sle.serial_and_batch_bundle,
+			["avg_rate", "total_amount", "total_qty"],
+			as_dict=True,
+		)
+
+		if flt(abs(ledger.total_qty)) - flt(abs(sle.actual_qty)) > 0.001:
+			msg = f"""Actual Qty in Serial and Batch Bundle
+				{sle.serial_and_batch_bundle} does not match with
+				Stock Ledger Entry {sle.name}"""
+
+			frappe.throw(_(msg))
+
 		actual_qty = flt(sle.actual_qty)
-		serial_nos = cstr(sle.serial_no).split("\n")
+		incoming_rate = flt(ledger.avg_rate)
 
 		if incoming_rate < 0:
 			# wrong incoming rate
@@ -977,11 +1001,11 @@
 		else:
 			# In case of delivery/stock issue, get average purchase rate
 			# of serial nos of current entry
+			outgoing_value = flt(ledger.total_amount)
 			if not sle.is_cancelled:
-				outgoing_value = self.get_incoming_value_for_serial_nos(sle, serial_nos)
 				stock_value_change = -1 * outgoing_value
 			else:
-				stock_value_change = actual_qty * sle.outgoing_rate
+				stock_value_change = outgoing_value
 
 		new_stock_qty = self.wh_data.qty_after_transaction + actual_qty
 
@@ -1138,7 +1162,7 @@
 			outgoing_rate = get_batch_incoming_rate(
 				item_code=sle.item_code,
 				warehouse=sle.warehouse,
-				batch_no=sle.batch_no,
+				serial_and_batch_bundle=sle.serial_and_batch_bundle,
 				posting_date=sle.posting_date,
 				posting_time=sle.posting_time,
 				creation=sle.creation,
@@ -1402,10 +1426,11 @@
 
 
 def get_batch_incoming_rate(
-	item_code, warehouse, batch_no, posting_date, posting_time, creation=None
+	item_code, warehouse, serial_and_batch_bundle, posting_date, posting_time, creation=None
 ):
 
 	sle = frappe.qb.DocType("Stock Ledger Entry")
+	batch_ledger = frappe.qb.DocType("Serial and Batch Ledger")
 
 	timestamp_condition = CombineDatetime(sle.posting_date, sle.posting_time) < CombineDatetime(
 		posting_date, posting_time
@@ -1416,18 +1441,36 @@
 			== CombineDatetime(posting_date, posting_time)
 		) & (sle.creation < creation)
 
+	batches = frappe.get_all(
+		"Serial and Batch Ledger", fields=["batch_no"], filters={"parent": serial_and_batch_bundle}
+	)
+
 	batch_details = (
 		frappe.qb.from_(sle)
-		.select(Sum(sle.stock_value_difference).as_("batch_value"), Sum(sle.actual_qty).as_("batch_qty"))
+		.inner_join(batch_ledger)
+		.on(sle.serial_and_batch_bundle == batch_ledger.parent)
+		.select(
+			Sum(
+				Case()
+				.when(sle.actual_qty > 0, batch_ledger.qty * batch_ledger.incoming_rate)
+				.else_(batch_ledger.qty * batch_ledger.outgoing_rate * -1)
+			).as_("batch_value"),
+			Sum(Case().when(sle.actual_qty > 0, batch_ledger.qty).else_(batch_ledger.qty * -1)).as_(
+				"batch_qty"
+			),
+		)
 		.where(
 			(sle.item_code == item_code)
 			& (sle.warehouse == warehouse)
-			& (sle.batch_no == batch_no)
+			& (batch_ledger.batch_no.isin([row.batch_no for row in batches]))
 			& (sle.is_cancelled == 0)
 		)
 		.where(timestamp_condition)
 	).run(as_dict=True)
 
+	print(batch_details)
+
+	print(batch_details[0].batch_value / batch_details[0].batch_qty)
 	if batch_details and batch_details[0].batch_qty:
 		return batch_details[0].batch_value / batch_details[0].batch_qty