fix: Added report 'Serial and Batch Summary' to view serial / batch nos
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 543d0e9..6410333 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -358,12 +358,14 @@
 	}
 
 	refresh() {
+
 		erpnext.toggle_naming_series();
 		erpnext.hide_company();
 		this.set_dynamic_labels();
 		this.setup_sms();
 		this.setup_quality_inspection();
 		this.validate_has_items();
+		erpnext.utils.view_serial_batch_nos(this.frm);
 	}
 
 	scan_barcode() {
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index a859a67..29c8aa0 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -113,6 +113,23 @@
 		}
 	},
 
+	view_serial_batch_nos: function(frm) {
+		let bundle_ids = frm.doc.items.filter(d => d.serial_and_batch_bundle);
+
+		if (bundle_ids?.length) {
+			frm.add_custom_button(__('Serial / Batch Nos'), () => {
+				frappe.route_options = {
+					"voucher_no": frm.doc.name,
+					"voucher_type": frm.doc.doctype,
+					"from_date": frm.doc.posting_date || frm.doc.transaction_date,
+					"to_date": frm.doc.posting_date || frm.doc.transaction_date,
+					"company": frm.doc.company,
+				};
+				frappe.set_route("query-report", "Serial and Batch Summary");
+			}, __('View'));
+		}
+	},
+
 	add_indicator_for_multicompany: function(frm, info) {
 		frm.dashboard.stats_area.show();
 		frm.dashboard.stats_area_row.addClass('flex');
@@ -1011,4 +1028,4 @@
 	$btn.on("click", function() {
 		context.show_serial_batch_selector(grid_row.frm, grid_row.doc, "", "", true);
 	});
-}
+}
\ 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 6955c76..c5b96ff 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
@@ -193,7 +193,7 @@
    "fieldname": "naming_series",
    "fieldtype": "Select",
    "label": "Naming Series",
-   "options": "SBB-.####"
+   "options": "SABB-.########"
   },
   {
    "default": "0",
@@ -244,7 +244,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2023-04-10 20:02:42.964309",
+ "modified": "2023-07-16 10:53:04.045605",
  "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 0c6d33b..43bd7ac 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
@@ -889,13 +889,16 @@
 
 
 @frappe.whitelist()
-def get_serial_batch_ledgers(item_code, docstatus=None, voucher_no=None, name=None):
-	filters = get_filters_for_bundle(item_code, docstatus=docstatus, voucher_no=voucher_no, name=name)
+def get_serial_batch_ledgers(item_code=None, docstatus=None, voucher_no=None, name=None):
+	filters = get_filters_for_bundle(
+		item_code=item_code, docstatus=docstatus, voucher_no=voucher_no, name=name
+	)
 
 	return frappe.get_all(
 		"Serial and Batch Bundle",
 		fields=[
 			"`tabSerial and Batch Bundle`.`name`",
+			"`tabSerial and Batch Bundle`.`item_code`",
 			"`tabSerial and Batch Entry`.`qty`",
 			"`tabSerial and Batch Entry`.`warehouse`",
 			"`tabSerial and Batch Entry`.`batch_no`",
@@ -906,12 +909,14 @@
 	)
 
 
-def get_filters_for_bundle(item_code, docstatus=None, voucher_no=None, name=None):
+def get_filters_for_bundle(item_code=None, docstatus=None, voucher_no=None, name=None):
 	filters = [
-		["Serial and Batch Bundle", "item_code", "=", item_code],
 		["Serial and Batch Bundle", "is_cancelled", "=", 0],
 	]
 
+	if item_code:
+		filters.append(["Serial and Batch Bundle", "item_code", "=", item_code])
+
 	if not docstatus:
 		docstatus = [0, 1]
 
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 403e04a..3e83faf 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -925,6 +925,7 @@
 		this.toggle_related_fields(this.frm.doc);
 		this.toggle_enable_bom();
 		this.show_stock_ledger();
+		erpnext.utils.view_serial_batch_nos(this.frm);
 		if (this.frm.doc.docstatus===1 && erpnext.is_perpetual_inventory_enabled(this.frm.doc.company)) {
 			this.show_general_ledger();
 		}
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
index 0664c29..cb2adf1 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js
@@ -337,6 +337,7 @@
 	refresh() {
 		if(this.frm.doc.docstatus > 0) {
 			this.show_stock_ledger();
+			erpnext.utils.view_serial_batch_nos(this.frm);
 			if (erpnext.is_perpetual_inventory_enabled(this.frm.doc.company)) {
 				this.show_general_ledger();
 			}
diff --git a/erpnext/stock/report/serial_and_batch_summary/__init__.py b/erpnext/stock/report/serial_and_batch_summary/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/stock/report/serial_and_batch_summary/__init__.py
diff --git a/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.js b/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.js
new file mode 100644
index 0000000..10e5925
--- /dev/null
+++ b/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.js
@@ -0,0 +1,95 @@
+// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Serial and Batch Summary"] = {
+	"filters": [
+		{
+			"fieldname":"company",
+			"label": __("Company"),
+			"fieldtype": "Link",
+			"options": "Company",
+			"default": frappe.defaults.get_user_default("Company"),
+		},
+		{
+			"fieldname":"from_date",
+			"label": __("From Date"),
+			"fieldtype": "Date",
+			"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
+		},
+		{
+			"fieldname":"to_date",
+			"label": __("To Date"),
+			"fieldtype": "Date",
+			"default": frappe.datetime.get_today()
+		},
+		{
+			"fieldname":"item_code",
+			"label": __("Item"),
+			"fieldtype": "Link",
+			"options": "Item",
+		},
+		{
+			"fieldname":"warehouse",
+			"label": __("Warehouse"),
+			"fieldtype": "Link",
+			"options": "Warehouse",
+		},
+		{
+			"fieldname":"voucher_type",
+			"label": __("Voucher Type"),
+			"fieldtype": "Link",
+			"options": "DocType",
+			get_query: function() {
+				return {
+					query: "erpnext.stock.report.serial_and_batch_summary.serial_and_batch_summary.get_voucher_type",
+				}
+			}
+		},
+		{
+			"fieldname":"voucher_no",
+			"label": __("Voucher No"),
+			"fieldtype": "MultiSelectList",
+			get_data: function(txt) {
+				if (!frappe.query_report.filters) return;
+
+				let voucher_type = frappe.query_report.get_filter_value('voucher_type');
+				if (!voucher_type) return;
+
+				return frappe.db.get_link_options(voucher_type, txt);
+			},
+		},
+		{
+			"fieldname":"serial_no",
+			"label": __("Serial No"),
+			"fieldtype": "Link",
+			"options": "Serial No",
+			get_query: function() {
+				return {
+					query: "erpnext.stock.report.serial_and_batch_summary.serial_and_batch_summary.get_serial_nos",
+					filters: {
+						"item_code": frappe.query_report.get_filter_value('item_code'),
+						"voucher_type": frappe.query_report.get_filter_value('voucher_type'),
+						"voucher_no": frappe.query_report.get_filter_value('voucher_no'),
+					}
+				}
+			}
+		},
+		{
+			"fieldname":"batch_no",
+			"label": __("Batch No"),
+			"fieldtype": "Link",
+			"options": "Batch",
+			get_query: function() {
+				return {
+					query: "erpnext.stock.report.serial_and_batch_summary.serial_and_batch_summary.get_batch_nos",
+					filters: {
+						"item_code": frappe.query_report.get_filter_value('item_code'),
+						"voucher_type": frappe.query_report.get_filter_value('voucher_type'),
+						"voucher_no": frappe.query_report.get_filter_value('voucher_no'),
+					}
+				}
+			}
+		}
+	]
+};
diff --git a/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.json b/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.json
new file mode 100644
index 0000000..7511e3a
--- /dev/null
+++ b/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.json
@@ -0,0 +1,38 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2023-07-13 16:53:27.735091",
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "json": "{}",
+ "modified": "2023-07-13 16:53:33.204591",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Serial and Batch Summary",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Serial and Batch Bundle",
+ "report_name": "Serial and Batch Summary",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "System Manager"
+  },
+  {
+   "role": "Sales User"
+  },
+  {
+   "role": "Purchase User"
+  },
+  {
+   "role": "Stock User"
+  },
+  {
+   "role": "Maintenance User"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.py b/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.py
new file mode 100644
index 0000000..3ea5e82
--- /dev/null
+++ b/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.py
@@ -0,0 +1,245 @@
+# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import frappe
+from frappe import _
+
+
+def execute(filters=None):
+	data = get_data(filters)
+	columns = get_columns(filters, data)
+
+	return columns, data
+
+
+def get_data(filters):
+	filter_conditions = get_filter_conditions(filters)
+
+	return frappe.get_all(
+		"Serial and Batch Bundle",
+		fields=[
+			"`tabSerial and Batch Bundle`.`voucher_type`",
+			"`tabSerial and Batch Bundle`.`posting_date`",
+			"`tabSerial and Batch Bundle`.`name`",
+			"`tabSerial and Batch Bundle`.`company`",
+			"`tabSerial and Batch Bundle`.`voucher_no`",
+			"`tabSerial and Batch Bundle`.`item_code`",
+			"`tabSerial and Batch Bundle`.`item_name`",
+			"`tabSerial and Batch Entry`.`serial_no`",
+			"`tabSerial and Batch Entry`.`batch_no`",
+			"`tabSerial and Batch Entry`.`warehouse`",
+			"`tabSerial and Batch Entry`.`incoming_rate`",
+			"`tabSerial and Batch Entry`.`stock_value_difference`",
+			"`tabSerial and Batch Entry`.`qty`",
+		],
+		filters=filter_conditions,
+		order_by="posting_date",
+	)
+
+
+def get_filter_conditions(filters):
+	filter_conditions = [
+		["Serial and Batch Bundle", "docstatus", "=", 1],
+		["Serial and Batch Bundle", "is_cancelled", "=", 0],
+	]
+
+	for field in ["voucher_type", "voucher_no", "item_code", "warehouse", "company"]:
+		if filters.get(field):
+			if field == "voucher_no":
+				filter_conditions.append(["Serial and Batch Bundle", field, "in", filters.get(field)])
+			else:
+				filter_conditions.append(["Serial and Batch Bundle", field, "=", filters.get(field)])
+
+	if filters.get("from_date") and filters.get("to_date"):
+		filter_conditions.append(
+			[
+				"Serial and Batch Bundle",
+				"posting_date",
+				"between",
+				[filters.get("from_date"), filters.get("to_date")],
+			]
+		)
+
+	for field in ["serial_no", "batch_no"]:
+		if filters.get(field):
+			filter_conditions.append(["Serial and Batch Entry", field, "=", filters.get(field)])
+
+	return filter_conditions
+
+
+def get_columns(filters, data):
+	columns = [
+		{
+			"label": _("Company"),
+			"fieldname": "company",
+			"fieldtype": "Link",
+			"options": "Company",
+			"width": 120,
+		},
+		{
+			"label": _("Serial and Batch Bundle"),
+			"fieldname": "name",
+			"fieldtype": "Link",
+			"options": "Serial and Batch Bundle",
+			"width": 110,
+		},
+		{"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 100},
+	]
+
+	item_details = {}
+
+	item_codes = []
+	if filters.get("voucher_type"):
+		item_codes = [d.item_code for d in data]
+
+	if filters.get("item_code") or (item_codes and len(list(set(item_codes))) == 1):
+		item_details = frappe.get_cached_value(
+			"Item",
+			filters.get("item_code") or item_codes[0],
+			["has_serial_no", "has_batch_no"],
+			as_dict=True,
+		)
+
+	if not filters.get("voucher_no"):
+		columns.extend(
+			[
+				{
+					"label": _("Voucher Type"),
+					"fieldname": "voucher_type",
+					"fieldtype": "Link",
+					"options": "DocType",
+					"width": 120,
+				},
+				{
+					"label": _("Voucher No"),
+					"fieldname": "voucher_no",
+					"fieldtype": "Dynamic Link",
+					"options": "voucher_type",
+					"width": 160,
+				},
+			]
+		)
+
+	if not filters.get("item_code"):
+		columns.extend(
+			[
+				{
+					"label": _("Item Code"),
+					"fieldname": "item_code",
+					"fieldtype": "Link",
+					"options": "Item",
+					"width": 120,
+				},
+				{"label": _("Item Name"), "fieldname": "item_name", "fieldtype": "Data", "width": 120},
+			]
+		)
+
+	if not filters.get("warehouse"):
+		columns.append(
+			{
+				"label": _("Warehouse"),
+				"fieldname": "warehouse",
+				"fieldtype": "Link",
+				"options": "Warehouse",
+				"width": 120,
+			}
+		)
+
+	if not item_details or item_details.get("has_serial_no"):
+		columns.append(
+			{"label": _("Serial No"), "fieldname": "serial_no", "fieldtype": "Data", "width": 120}
+		)
+
+	if not item_details or item_details.get("has_batch_no"):
+		columns.extend(
+			[
+				{"label": _("Batch No"), "fieldname": "batch_no", "fieldtype": "Data", "width": 120},
+				{"label": _("Batch Qty"), "fieldname": "qty", "fieldtype": "Float", "width": 120},
+			]
+		)
+
+	columns.extend(
+		[
+			{"label": _("Incoming Rate"), "fieldname": "incoming_rate", "fieldtype": "Float", "width": 120},
+			{
+				"label": _("Change in Stock Value"),
+				"fieldname": "stock_value_difference",
+				"fieldtype": "Float",
+				"width": 120,
+			},
+		]
+	)
+
+	return columns
+
+
+@frappe.whitelist()
+@frappe.validate_and_sanitize_search_inputs
+def get_voucher_type(doctype, txt, searchfield, start, page_len, filters):
+	child_doctypes = frappe.get_all(
+		"DocField",
+		filters={"fieldname": "serial_and_batch_bundle"},
+		fields=["distinct parent as parent"],
+	)
+
+	query_filters = {"options": ["in", [d.parent for d in child_doctypes]]}
+	if txt:
+		query_filters["parent"] = ["like", "%{}%".format(txt)]
+
+	return frappe.get_all("DocField", filters=query_filters, fields=["distinct parent"], as_list=True)
+
+
+@frappe.whitelist()
+@frappe.validate_and_sanitize_search_inputs
+def get_serial_nos(doctype, txt, searchfield, start, page_len, filters):
+	query_filters = {}
+
+	if txt:
+		query_filters["serial_no"] = ["like", f"%{txt}%"]
+
+	if filters.get("voucher_no"):
+		serial_batch_bundle = frappe.get_cached_value(
+			"Serial and Batch Bundle",
+			{"voucher_no": ("in", filters.get("voucher_no")), "docstatus": 1, "is_cancelled": 0},
+			"name",
+		)
+
+		query_filters["parent"] = serial_batch_bundle
+		if not txt:
+			query_filters["serial_no"] = ("is", "set")
+
+		return frappe.get_all(
+			"Serial and Batch Entry", filters=query_filters, fields=["serial_no"], as_list=True
+		)
+
+	else:
+		query_filters["item_code"] = filters.get("item_code")
+		return frappe.get_all("Serial No", filters=query_filters, as_list=True)
+
+
+@frappe.whitelist()
+@frappe.validate_and_sanitize_search_inputs
+def get_batch_nos(doctype, txt, searchfield, start, page_len, filters):
+	query_filters = {}
+
+	if txt:
+		query_filters["batch_no"] = ["like", f"%{txt}%"]
+
+	if filters.get("voucher_no"):
+		serial_batch_bundle = frappe.get_cached_value(
+			"Serial and Batch Bundle",
+			{"voucher_no": ("in", filters.get("voucher_no")), "docstatus": 1, "is_cancelled": 0},
+			"name",
+		)
+
+		query_filters["parent"] = serial_batch_bundle
+		if not txt:
+			query_filters["batch_no"] = ("is", "set")
+
+		return frappe.get_all(
+			"Serial and Batch Entry", filters=query_filters, fields=["batch_no"], as_list=True
+		)
+
+	else:
+		query_filters["item"] = filters.get("item_code")
+		return frappe.get_all("Batch", filters=query_filters, as_list=True)