Merge branch 'develop' into persistent-indexing
diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
index fef3b56..5e17881 100644
--- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
+++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py
@@ -94,10 +94,13 @@
 			pe.append(reference)
 
 	def update_allocated_amount(self):
-		self.allocated_amount = (
+		allocated_amount = (
 			sum(p.allocated_amount for p in self.payment_entries) if self.payment_entries else 0.0
 		)
-		self.unallocated_amount = abs(flt(self.withdrawal) - flt(self.deposit)) - self.allocated_amount
+		unallocated_amount = abs(flt(self.withdrawal) - flt(self.deposit)) - allocated_amount
+
+		self.allocated_amount = flt(allocated_amount, self.precision("allocated_amount"))
+		self.unallocated_amount = flt(unallocated_amount, self.precision("unallocated_amount"))
 
 	def before_submit(self):
 		self.allocate_payment_entries()
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index e900b81..ef1f6bd 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -14,23 +14,8 @@
 		super.setup(doc);
 	}
 	company() {
+		super.company();
 		erpnext.accounts.dimensions.update_dimension(this.frm, this.frm.doctype);
-
-		let me = this;
-		if (this.frm.doc.company) {
-			frappe.call({
-				method:
-					"erpnext.accounts.party.get_party_account",
-				args: {
-					party_type: 'Customer',
-					party: this.frm.doc.customer,
-					company: this.frm.doc.company
-				},
-				callback: (response) => {
-					if (response) me.frm.set_value("debit_to", response.message);
-				},
-			});
-		}
 	}
 	onload() {
 		var me = this;
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index e3fa5e8..6d77ef5 100644
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -83,7 +83,10 @@
 			self.skip_total_row = 1
 
 		if self.filters.get("in_party_currency"):
-			self.skip_total_row = 1
+			if self.filters.get("party") and len(self.filters.get("party")) == 1:
+				self.skip_total_row = 0
+			else:
+				self.skip_total_row = 1
 
 	def get_data(self):
 		self.get_ple_entries()
diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
index 56ae41a..a4f01fa 100644
--- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
+++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
@@ -357,7 +357,13 @@
 				and ifnull(`tabSales Invoice Payment`.mode_of_payment, '') = %(mode_of_payment)s)"""
 
 	if filters.get("warehouse"):
-		conditions += """and ifnull(`tabSales Invoice Item`.warehouse, '') = %(warehouse)s"""
+		if frappe.db.get_value("Warehouse", filters.get("warehouse"), "is_group"):
+			lft, rgt = frappe.db.get_all(
+				"Warehouse", filters={"name": filters.get("warehouse")}, fields=["lft", "rgt"], as_list=True
+			)[0]
+			conditions += f"and ifnull(`tabSales Invoice Item`.warehouse, '') in (select name from `tabWarehouse` where lft > {lft} and rgt < {rgt}) "
+		else:
+			conditions += """and ifnull(`tabSales Invoice Item`.warehouse, '') = %(warehouse)s"""
 
 	if filters.get("brand"):
 		conditions += """and ifnull(`tabSales Invoice Item`.brand, '') = %(brand)s"""
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 308a134..063c1e3 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -216,6 +216,18 @@
 					)
 				)
 
+			if self.get("is_return") and self.get("return_against"):
+				document_type = "Credit Note" if self.doctype == "Sales Invoice" else "Debit Note"
+				frappe.msgprint(
+					_(
+						"{0} will be treated as a standalone {0}. Post creation use {1} tool to reconcile against {2}."
+					).format(
+						document_type,
+						get_link_to_form("Payment Reconciliation"),
+						get_link_to_form(self.doctype, self.get("return_against")),
+					)
+				)
+
 			pos_check_field = "is_pos" if self.doctype == "Sales Invoice" else "is_paid"
 			if cint(self.allocate_advances_automatically) and not cint(self.get(pos_check_field)):
 				self.set_advances()
@@ -2691,7 +2703,7 @@
 
 	if order_list:
 		q = q.where(
-			(journal_acc.reference_type == order_doctype) & ((journal_acc.reference_type).isin(order_list))
+			(journal_acc.reference_type == order_doctype) & ((journal_acc.reference_name).isin(order_list))
 		)
 
 	q = q.orderby(journal_entry.posting_date)
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index fb68010..27ac9d5 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -217,8 +217,8 @@
 			lc_voucher_data = frappe.db.sql(
 				"""select sum(applicable_charges), cost_center
 				from `tabLanded Cost Item`
-				where docstatus = 1 and purchase_receipt_item = %s""",
-				d.name,
+				where docstatus = 1 and purchase_receipt_item = %s and receipt_document = %s""",
+				(d.name, self.name),
 			)
 			d.landed_cost_voucher_amount = lc_voucher_data[0][0] if lc_voucher_data else 0.0
 			if not d.cost_center and lc_voucher_data and lc_voucher_data[0][1]:
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index dd27200..fdbfd10 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -7,7 +7,7 @@
 
 import frappe
 from frappe import _, bold
-from frappe.utils import cint, flt, get_link_to_form, getdate
+from frappe.utils import cint, cstr, flt, get_link_to_form, getdate
 
 import erpnext
 from erpnext.accounts.general_ledger import (
@@ -162,6 +162,9 @@
 		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 		from erpnext.stock.serial_batch_bundle import SerialBatchCreation
 
+		if self.get("_action") == "update_after_submit":
+			return
+
 		# To handle test cases
 		if frappe.flags.in_test and frappe.flags.use_serial_and_batch_fields:
 			return
@@ -171,13 +174,16 @@
 			table_name = "stock_items"
 
 		for row in self.get(table_name):
+			if row.serial_and_batch_bundle and (row.serial_no or row.batch_no):
+				self.validate_serial_nos_and_batches_with_bundle(row)
+
 			if not row.serial_no and not row.batch_no and not row.get("rejected_serial_no"):
 				continue
 
 			if not row.use_serial_batch_fields and (
 				row.serial_no or row.batch_no or row.get("rejected_serial_no")
 			):
-				frappe.throw(_("Please enable Use Old Serial / Batch Fields to make_bundle"))
+				row.use_serial_batch_fields = 1
 
 			if row.use_serial_batch_fields and (
 				not row.serial_and_batch_bundle and not row.get("rejected_serial_and_batch_bundle")
@@ -219,7 +225,6 @@
 					row.db_set(
 						{
 							"rejected_serial_and_batch_bundle": sn_doc.name,
-							"rejected_serial_no": "",
 						}
 					)
 				else:
@@ -227,11 +232,44 @@
 					row.db_set(
 						{
 							"serial_and_batch_bundle": sn_doc.name,
-							"serial_no": "",
-							"batch_no": "",
 						}
 					)
 
+	def validate_serial_nos_and_batches_with_bundle(self, row):
+		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+
+		throw_error = False
+		if row.serial_no:
+			serial_nos = frappe.get_all(
+				"Serial and Batch Entry", fields=["serial_no"], filters={"parent": row.serial_and_batch_bundle}
+			)
+			serial_nos = sorted([cstr(d.serial_no) for d in serial_nos])
+			parsed_serial_nos = get_serial_nos(row.serial_no)
+
+			if len(serial_nos) != len(parsed_serial_nos):
+				throw_error = True
+			elif serial_nos != parsed_serial_nos:
+				for serial_no in serial_nos:
+					if serial_no not in parsed_serial_nos:
+						throw_error = True
+						break
+
+		elif row.batch_no:
+			batches = frappe.get_all(
+				"Serial and Batch Entry", fields=["batch_no"], filters={"parent": row.serial_and_batch_bundle}
+			)
+			batches = sorted([d.batch_no for d in batches])
+
+			if batches != [row.batch_no]:
+				throw_error = True
+
+		if throw_error:
+			frappe.throw(
+				_(
+					"At row {0}: Serial and Batch Bundle {1} has already created. Please remove the values from the serial no or batch no fields."
+				).format(row.idx, row.serial_and_batch_bundle)
+			)
+
 	def set_use_serial_batch_fields(self):
 		if frappe.db.get_single_value("Stock Settings", "use_serial_batch_fields"):
 			for row in self.items:
diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py
index 17a2b07..eac35b0 100644
--- a/erpnext/controllers/subcontracting_controller.py
+++ b/erpnext/controllers/subcontracting_controller.py
@@ -539,6 +539,10 @@
 	def __add_supplied_item(self, item_row, bom_item, qty):
 		bom_item.conversion_factor = item_row.conversion_factor
 		rm_obj = self.append(self.raw_material_table, bom_item)
+		if rm_obj.get("qty"):
+			# Qty field not exists
+			rm_obj.qty = 0.0
+
 		rm_obj.reference_name = item_row.name
 
 		if self.doctype == self.subcontract_data.order_doctype:
diff --git a/erpnext/controllers/tests/test_subcontracting_controller.py b/erpnext/controllers/tests/test_subcontracting_controller.py
index 47762ac..95a7bcb 100644
--- a/erpnext/controllers/tests/test_subcontracting_controller.py
+++ b/erpnext/controllers/tests/test_subcontracting_controller.py
@@ -401,7 +401,7 @@
 			{
 				"main_item_code": "Subcontracted Item SA4",
 				"item_code": "Subcontracted SRM Item 3",
-				"qty": 1.0,
+				"qty": 3.0,
 				"rate": 100.0,
 				"stock_uom": "Nos",
 				"warehouse": "_Test Warehouse - _TC",
@@ -914,12 +914,6 @@
 		else child_row.get("consumed_qty")
 	)
 
-	if child_row.serial_no:
-		details.serial_no.extend(get_serial_nos(child_row.serial_no))
-
-	if child_row.batch_no:
-		details.batch_no[child_row.batch_no] += child_row.get("qty") or child_row.get("consumed_qty")
-
 	if child_row.serial_and_batch_bundle:
 		doc = frappe.get_doc("Serial and Batch Bundle", child_row.serial_and_batch_bundle)
 		for row in doc.get("entries"):
@@ -928,6 +922,12 @@
 
 			if row.batch_no:
 				details.batch_no[row.batch_no] += row.qty * (-1 if doc.type_of_transaction == "Outward" else 1)
+	else:
+		if child_row.serial_no:
+			details.serial_no.extend(get_serial_nos(child_row.serial_no))
+
+		if child_row.batch_no:
+			details.batch_no[child_row.batch_no] += child_row.get("qty") or child_row.get("consumed_qty")
 
 
 def make_stock_transfer_entry(**args):
diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py
index dcf122c..e44d484 100644
--- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py
+++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py
@@ -41,11 +41,49 @@
 	def validate(self):
 		self.validate_dates()
 		self.validate_duplicate_items()
+		self.set_party_item_code()
 
 	def validate_dates(self):
 		if getdate(self.from_date) > getdate(self.to_date):
 			frappe.throw(_("From date cannot be greater than To date"))
 
+	def set_party_item_code(self):
+		item_ref = {}
+		if self.blanket_order_type == "Selling":
+			item_ref = self.get_customer_items_ref()
+		else:
+			item_ref = self.get_supplier_items_ref()
+
+		if not item_ref:
+			return
+
+		for row in self.items:
+			row.party_item_code = item_ref.get(row.item_code)
+
+	def get_customer_items_ref(self):
+		items = [d.item_code for d in self.items]
+
+		return frappe._dict(
+			frappe.get_all(
+				"Item Customer Detail",
+				filters={"parent": ("in", items), "customer_name": self.customer},
+				fields=["parent", "ref_code"],
+				as_list=True,
+			)
+		)
+
+	def get_supplier_items_ref(self):
+		items = [d.item_code for d in self.items]
+
+		return frappe._dict(
+			frappe.get_all(
+				"Item Supplier",
+				filters={"parent": ("in", items), "supplier": self.supplier},
+				fields=["parent", "supplier_part_no"],
+				as_list=True,
+			)
+		)
+
 	def validate_duplicate_items(self):
 		item_list = []
 		for item in self.items:
diff --git a/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py
index e9fc25b..3f3b6f0 100644
--- a/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py
+++ b/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py
@@ -5,6 +5,7 @@
 from frappe.utils import add_months, today
 
 from erpnext import get_company_currency
+from erpnext.stock.doctype.item.test_item import make_item
 
 from .blanket_order import make_order
 
@@ -90,6 +91,30 @@
 		frappe.db.set_single_value("Buying Settings", "blanket_order_allowance", 10)
 		po.submit()
 
+	def test_party_item_code(self):
+		item_doc = make_item("_Test Item 1 for Blanket Order")
+		item_code = item_doc.name
+
+		customer = "_Test Customer"
+		supplier = "_Test Supplier"
+
+		if not frappe.db.exists(
+			"Item Customer Detail", {"customer_name": customer, "parent": item_code}
+		):
+			item_doc.append("customer_items", {"customer_name": customer, "ref_code": "CUST-REF-1"})
+			item_doc.save()
+
+		if not frappe.db.exists("Item Supplier", {"supplier": supplier, "parent": item_code}):
+			item_doc.append("supplier_items", {"supplier": supplier, "supplier_part_no": "SUPP-PART-1"})
+			item_doc.save()
+
+		# Blanket Order for Selling
+		bo = make_blanket_order(blanket_order_type="Selling", customer=customer, item_code=item_code)
+		self.assertEqual(bo.items[0].party_item_code, "CUST-REF-1")
+
+		bo = make_blanket_order(blanket_order_type="Purchasing", supplier=supplier, item_code=item_code)
+		self.assertEqual(bo.items[0].party_item_code, "SUPP-PART-1")
+
 
 def make_blanket_order(**args):
 	args = frappe._dict(args)
diff --git a/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.json b/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.json
index 977ad54..aa7831f 100644
--- a/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.json
+++ b/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "creation": "2018-05-24 07:20:04.255236",
  "doctype": "DocType",
  "editable_grid": 1,
@@ -6,6 +7,7 @@
  "field_order": [
   "item_code",
   "item_name",
+  "party_item_code",
   "column_break_3",
   "qty",
   "rate",
@@ -62,10 +64,17 @@
    "fieldname": "terms_and_conditions",
    "fieldtype": "Text",
    "label": "Terms and Conditions"
+  },
+  {
+   "fieldname": "party_item_code",
+   "fieldtype": "Data",
+   "label": "Party Item Code",
+   "read_only": 1
   }
  ],
  "istable": 1,
- "modified": "2019-11-18 19:37:46.245878",
+ "links": [],
+ "modified": "2024-02-14 18:25:26.479672",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Blanket Order Item",
@@ -74,5 +83,6 @@
  "quick_entry": 1,
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.py b/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.py
index 068c2e9..316d294 100644
--- a/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.py
+++ b/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.py
@@ -20,6 +20,7 @@
 		parent: DF.Data
 		parentfield: DF.Data
 		parenttype: DF.Data
+		party_item_code: DF.Data | None
 		qty: DF.Float
 		rate: DF.Currency
 		terms_and_conditions: DF.Text | None
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index 079350b..3daec20 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -748,7 +748,7 @@
 			fields=["total_time_in_mins", "hour_rate"],
 			filters={"is_corrective_job_card": 1, "docstatus": 1, "work_order": self.work_order},
 		):
-			wo.corrective_operation_cost += flt(row.total_time_in_mins) * flt(row.hour_rate)
+			wo.corrective_operation_cost += flt(row.total_time_in_mins / 60) * flt(row.hour_rate)
 
 		wo.calculate_operating_cost()
 		wo.flags.ignore_validate_update_after_submit = True
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index b942842..fdef8fe 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -652,7 +652,10 @@
 				"project": self.project,
 			}
 
-			key = (d.item_code, d.sales_order, d.warehouse)
+			key = (d.item_code, d.sales_order, d.sales_order_item, d.warehouse)
+			if self.combine_items:
+				key = (d.item_code, d.sales_order, d.warehouse)
+
 			if not d.sales_order:
 				key = (d.name, d.item_code, d.warehouse)
 
diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js
index 77ecf75..1d0d47e 100644
--- a/erpnext/public/js/controllers/buying.js
+++ b/erpnext/public/js/controllers/buying.js
@@ -368,7 +368,8 @@
 
 											let update_values = {
 												"serial_and_batch_bundle": r.name,
-												"qty": qty
+												"use_serial_batch_fields": 0,
+												"qty": qty / flt(item.conversion_factor || 1, precision("conversion_factor", item))
 											}
 
 											if (r.warehouse) {
@@ -408,7 +409,8 @@
 
 											let update_values = {
 												"serial_and_batch_bundle": r.name,
-												"rejected_qty": qty
+												"use_serial_batch_fields": 0,
+												"rejected_qty": qty / flt(item.conversion_factor || 1, precision("conversion_factor", item))
 											}
 
 											if (r.warehouse) {
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index ba53cf8..2c985a0 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -145,6 +145,12 @@
 			});
 		}
 
+		if(this.frm.fields_dict['items'].grid.get_field('batch_no')) {
+			this.frm.set_query('batch_no', 'items', function(doc, cdt, cdn) {
+				return me.set_query_for_batch(doc, cdt, cdn);
+			});
+		}
+
 		if(
 			this.frm.docstatus < 2
 			&& this.frm.fields_dict["payment_terms_template"]
@@ -234,7 +240,7 @@
 	}
 
 	set_fields_onload_for_line_item() {
-		if (this.frm.is_new && this.frm.doc?.items) {
+		if (this.frm.is_new() && this.frm.doc?.items) {
 			this.frm.doc.items.forEach(item => {
 				if (item.docstatus === 0
 					&& frappe.meta.has_field(item.doctype, "use_serial_batch_fields")
@@ -1633,18 +1639,6 @@
 		return item_list;
 	}
 
-	items_delete() {
-		this.update_localstorage_scanned_data();
-	}
-
-	update_localstorage_scanned_data() {
-		let doctypes = ["Sales Invoice", "Purchase Invoice", "Delivery Note", "Purchase Receipt"];
-		if (this.frm.is_new() && doctypes.includes(this.frm.doc.doctype)) {
-			const barcode_scanner = new erpnext.utils.BarcodeScanner({frm:this.frm});
-			barcode_scanner.update_localstorage_scanned_data();
-		}
-	}
-
 	_set_values_for_item_list(children) {
 		const items_rule_dict = {};
 
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index de46271..68bcafc 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -676,6 +676,7 @@
 			fieldname: frm.doc.doctype == 'Sales Order' ? "delivery_date" : "schedule_date",
 			in_list_view: 1,
 			label: frm.doc.doctype == 'Sales Order' ? __("Delivery Date") : __("Reqd by date"),
+			default: frm.doc.doctype == 'Sales Order' ? frm.doc.delivery_date : frm.doc.schedule_date,
 			reqd: 1
 		})
 		fields.splice(3, 0, {
diff --git a/erpnext/public/js/utils/sales_common.js b/erpnext/public/js/utils/sales_common.js
index b8ec77f..a957530 100644
--- a/erpnext/public/js/utils/sales_common.js
+++ b/erpnext/public/js/utils/sales_common.js
@@ -339,7 +339,8 @@
 
 											frappe.model.set_value(item.doctype, item.name, {
 												"serial_and_batch_bundle": r.name,
-												"qty": qty
+												"use_serial_batch_fields": 0,
+												"qty": qty / flt(item.conversion_factor || 1, precision("conversion_factor", item))
 											});
 										}
 									}
diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js
index 80ade70..fccaf88 100644
--- a/erpnext/public/js/utils/serial_no_batch_selector.js
+++ b/erpnext/public/js/utils/serial_no_batch_selector.js
@@ -71,6 +71,10 @@
 		let warehouse = this.item?.type_of_transaction === "Outward" ?
 			(this.item.warehouse || this.item.s_warehouse) : "";
 
+		if (this.frm.doc.doctype === 'Stock Entry') {
+			warehouse = this.item.s_warehouse || this.item.t_warehouse;
+		}
+
 		if (!warehouse && this.frm.doc.doctype === 'Stock Reconciliation') {
 			warehouse = this.get_warehouse();
 		}
@@ -367,19 +371,11 @@
 					label: __('Batch No'),
 					in_list_view: 1,
 					get_query: () => {
-						if (this.item.type_of_transaction !== "Outward") {
-							return {
-								filters: {
-									'item': this.item.item_code,
-								}
-							}
-						} else {
-							return {
-								query : "erpnext.controllers.queries.get_batch_no",
-								filters: {
-									'item_code': this.item.item_code,
-									'warehouse': this.get_warehouse()
-								}
+						return {
+							query : "erpnext.controllers.queries.get_batch_no",
+							filters: {
+								'item_code': this.item.item_code,
+								'warehouse': this.item.s_warehouse || this.item.t_warehouse,
 							}
 						}
 					},
diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
index dec7506..baff540 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
@@ -65,6 +65,7 @@
 	def validate(self):
 		self.check_mandatory()
 		self.validate_receipt_documents()
+		self.validate_line_items()
 		init_landed_taxes_and_totals(self)
 		self.set_total_taxes_and_charges()
 		if not self.get("items"):
@@ -72,6 +73,26 @@
 
 		self.set_applicable_charges_on_item()
 
+	def validate_line_items(self):
+		for d in self.get("items"):
+			if (
+				d.docstatus == 0
+				and d.purchase_receipt_item
+				and not frappe.db.exists(
+					d.receipt_document_type + " Item",
+					{"name": d.purchase_receipt_item, "parent": d.receipt_document},
+				)
+			):
+				frappe.throw(
+					_("Row {0}: {2} Item {1} does not exist in {2} {3}").format(
+						d.idx,
+						frappe.bold(d.purchase_receipt_item),
+						d.receipt_document_type,
+						frappe.bold(d.receipt_document),
+					),
+					title=_("Incorrect Reference Document (Purchase Receipt Item)"),
+				)
+
 	def check_mandatory(self):
 		if not self.get("purchase_receipts"):
 			frappe.throw(_("Please enter Receipt Document"))
diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js
index 77a3d6d..1b40f2b 100644
--- a/erpnext/stock/doctype/material_request/material_request.js
+++ b/erpnext/stock/doctype/material_request/material_request.js
@@ -250,7 +250,7 @@
 			fields: [
 				{"fieldname":"bom", "fieldtype":"Link", "label":__("BOM"),
 					options:"BOM", reqd: 1, get_query: function() {
-						return {filters: { docstatus:1 }};
+						return {filters: { docstatus:1, "is_active": 1 }};
 					}},
 				{"fieldname":"warehouse", "fieldtype":"Link", "label":__("For Warehouse"),
 					options:"Warehouse", reqd: 1},
diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js
index 3cc2956..3a5daa1 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.js
+++ b/erpnext/stock/doctype/pick_list/pick_list.js
@@ -330,7 +330,8 @@
 									let qty = Math.abs(r.total_qty);
 									frappe.model.set_value(item.doctype, item.name, {
 										"serial_and_batch_bundle": r.name,
-										"qty": qty
+										"use_serial_batch_fields": 0,
+										"qty": qty / flt(item.conversion_factor || 1, precision("conversion_factor", item))
 									});
 								}
 							}
diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py
index 0e1f8d7..8a1f79d 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.py
+++ b/erpnext/stock/doctype/pick_list/pick_list.py
@@ -73,6 +73,10 @@
 		self.update_status()
 		self.set_item_locations()
 
+		if self.get("locations"):
+			self.validate_sales_order_percentage()
+
+	def validate_sales_order_percentage(self):
 		# set percentage picked in SO
 		for location in self.get("locations"):
 			if (
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index c9fe7d2..1e2d8eb 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -686,9 +686,7 @@
 						)
 
 					stock_value_diff = (
-						flt(d.base_net_amount)
-						+ flt(d.item_tax_amount / self.conversion_rate)
-						+ flt(d.landed_cost_voucher_amount)
+						flt(d.base_net_amount) + flt(d.item_tax_amount) + flt(d.landed_cost_voucher_amount)
 					)
 				elif warehouse_account.get(d.warehouse):
 					stock_value_diff = get_stock_value_difference(self.name, d.name, d.warehouse)
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 18e24f4..daa0166 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -2259,7 +2259,7 @@
 		)
 
 		self.assertEqual(pr.items[0].use_serial_batch_fields, 1)
-		self.assertFalse(pr.items[0].serial_no)
+		self.assertTrue(pr.items[0].serial_no)
 		self.assertTrue(pr.items[0].serial_and_batch_bundle)
 
 		sbb_doc = frappe.get_doc("Serial and Batch Bundle", pr.items[0].serial_and_batch_bundle)
@@ -2406,6 +2406,30 @@
 		for index, d in enumerate(data):
 			self.assertEqual(d.qty_after_transaction, 11 + index)
 
+	def test_auto_set_batch_based_on_bundle(self):
+		item_code = make_item(
+			"_Test Auto Set Batch Based on Bundle",
+			properties={
+				"has_batch_no": 1,
+				"batch_number_series": "BATCH-BNU-TASBBB-.#####",
+				"create_new_batch": 1,
+			},
+		).name
+
+		frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1)
+
+		pr = make_purchase_receipt(
+			item_code=item_code,
+			qty=5,
+			rate=100,
+		)
+
+		self.assertTrue(pr.items[0].batch_no)
+		batch_no = get_batch_from_bundle(pr.items[0].serial_and_batch_bundle)
+		self.assertEqual(pr.items[0].batch_no, batch_no)
+
+		frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 0)
+
 
 def prepare_data_for_internal_transfer():
 	from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier
diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
index 31fc2ca..a383798 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
@@ -294,9 +294,20 @@
 		doc.log_error("Unable to repost item valuation")
 
 		message = frappe.message_log.pop() if frappe.message_log else ""
+		if isinstance(message, dict):
+			message = message.get("message")
+
 		if traceback:
-			message += "<br>" + "Traceback: <br>" + traceback
-		frappe.db.set_value(doc.doctype, doc.name, "error_log", message)
+			message += "<br><br>" + "<b>Traceback:</b> <br>" + traceback
+
+		frappe.db.set_value(
+			doc.doctype,
+			doc.name,
+			{
+				"error_log": message,
+				"status": "Failed",
+			},
+		)
 
 		outgoing_email_account = frappe.get_cached_value(
 			"Email Account", {"default_outgoing": 1, "enable_outgoing": 1}, "name"
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 91b7430..1f7bb4d 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
@@ -207,13 +207,24 @@
 			};
 		});
 
-		frm.set_query('batch_no', 'entries', () => {
-			return {
-				filters: {
-					item: frm.doc.item_code,
-					disabled: 0,
+		frm.set_query('batch_no', 'entries', (doc) => {
+
+			if (doc.type_of_transaction ==="Outward") {
+				return {
+					query : "erpnext.controllers.queries.get_batch_no",
+					filters: {
+						item_code: doc.item_code,
+						warehouse: doc.warehouse,
+					}
 				}
-			};
+			} else {
+				return {
+					filters: {
+						item: doc.item_code,
+						disabled: 0,
+					}
+				};
+			}
 		});
 
 		frm.set_query('warehouse', 'entries', () => {
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 8da3e8f..7f79f04 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -543,7 +543,9 @@
 
 		let fields = [
 			{"fieldname":"bom", "fieldtype":"Link", "label":__("BOM"),
-			options:"BOM", reqd: 1, get_query: filters()},
+			options:"BOM", reqd: 1, get_query: () => {
+				return {filters: { docstatus:1, "is_active": 1 }};
+			}},
 			{"fieldname":"source_warehouse", "fieldtype":"Link", "label":__("Source Warehouse"),
 			options:"Warehouse"},
 			{"fieldname":"target_warehouse", "fieldtype":"Link", "label":__("Target Warehouse"),
@@ -936,6 +938,7 @@
 		this.toggle_related_fields(this.frm.doc);
 		this.toggle_enable_bom();
 		this.show_stock_ledger();
+		this.set_fields_onload_for_line_item();
 		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();
@@ -944,6 +947,35 @@
 		erpnext.utils.add_item(this.frm);
 	}
 
+	serial_no(doc, cdt, cdn) {
+		var item = frappe.get_doc(cdt, cdn);
+
+		if (item?.serial_no) {
+			// Replace all occurences of comma with line feed
+			item.serial_no = item.serial_no.replace(/,/g, '\n');
+			item.conversion_factor = item.conversion_factor || 1;
+
+			let valid_serial_nos = [];
+			let serialnos = item.serial_no.split("\n");
+			for (var i = 0; i < serialnos.length; i++) {
+				if (serialnos[i] != "") {
+					valid_serial_nos.push(serialnos[i]);
+				}
+			}
+			frappe.model.set_value(item.doctype, item.name,
+				"qty", valid_serial_nos.length / item.conversion_factor);
+		}
+	}
+
+	set_fields_onload_for_line_item() {
+		if (this.frm.is_new() && this.frm.doc?.items
+			&& cint(frappe.user_defaults?.use_serial_batch_fields) === 1) {
+			this.frm.doc.items.forEach(item => {
+				frappe.model.set_value(item.doctype, item.name, "use_serial_batch_fields", 1);
+			})
+		}
+	}
+
 	scan_barcode() {
 		frappe.flags.dialog_set = false;
 		const barcode_scanner = new erpnext.utils.BarcodeScanner({frm:this.frm});
@@ -1074,6 +1106,10 @@
 
 		if(!row.s_warehouse) row.s_warehouse = this.frm.doc.from_warehouse;
 		if(!row.t_warehouse) row.t_warehouse = this.frm.doc.to_warehouse;
+
+		if (cint(frappe.user_defaults?.use_serial_batch_fields)) {
+			frappe.model.set_value(row.doctype, row.name, "use_serial_batch_fields", 1);
+		}
 	}
 
 	from_warehouse(doc) {
@@ -1144,7 +1180,8 @@
 							if (r) {
 								frappe.model.set_value(item.doctype, item.name, {
 									"serial_and_batch_bundle": r.name,
-									"qty": Math.abs(r.total_qty)
+									"use_serial_batch_fields": 0,
+									"qty": Math.abs(r.total_qty) / flt(item.conversion_factor || 1, precision("conversion_factor", item))
 								});
 							}
 						}
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index fb41745..832894b 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -839,6 +839,7 @@
 					currency=erpnext.get_company_currency(self.company),
 					company=self.company,
 					raise_error_if_no_rate=raise_error_if_no_rate,
+					batch_no=d.batch_no,
 					serial_and_batch_bundle=d.serial_and_batch_bundle,
 				)
 
@@ -867,7 +868,7 @@
 				if reset_outgoing_rate:
 					args = self.get_args_for_incoming_rate(d)
 					rate = get_incoming_rate(args, raise_error_if_no_rate)
-					if rate > 0:
+					if rate >= 0:
 						d.basic_rate = rate
 
 				d.basic_amount = flt(flt(d.transfer_qty) * flt(d.basic_rate), d.precision("basic_amount"))
@@ -890,6 +891,8 @@
 				"allow_zero_valuation": item.allow_zero_valuation_rate,
 				"serial_and_batch_bundle": item.serial_and_batch_bundle,
 				"voucher_detail_no": item.name,
+				"batch_no": item.batch_no,
+				"serial_no": item.serial_no,
 			}
 		)
 
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index 6c3faa6..99c050a 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -1775,7 +1775,7 @@
 		)
 
 		self.assertTrue(se.items[0].use_serial_batch_fields)
-		self.assertFalse(se.items[0].serial_no)
+		self.assertTrue(se.items[0].serial_no)
 		self.assertTrue(se.items[0].serial_and_batch_bundle)
 
 		for serial_no in serial_nos:
@@ -1793,7 +1793,7 @@
 		se1.reload()
 
 		self.assertTrue(se1.items[0].use_serial_batch_fields)
-		self.assertFalse(se1.items[0].serial_no)
+		self.assertTrue(se1.items[0].serial_no)
 		self.assertTrue(se1.items[0].serial_and_batch_bundle)
 
 		for serial_no in serial_nos:
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index ce08615..c69b20b 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -834,6 +834,7 @@
 			if voucher_detail_no != row.name:
 				continue
 
+			val_rate = 0.0
 			current_qty = 0.0
 			if row.current_serial_and_batch_bundle:
 				current_qty = self.get_current_qty_for_serial_or_batch(row)
@@ -843,7 +844,6 @@
 					row.warehouse,
 					self.posting_date,
 					self.posting_time,
-					voucher_no=self.name,
 				)
 
 				current_qty = item_dict.get("qty")
@@ -885,7 +885,7 @@
 					{"voucher_detail_no": row.name, "actual_qty": ("<", 0), "is_cancelled": 0},
 					"name",
 				)
-				and (not row.current_serial_and_batch_bundle and not row.batch_no)
+				and (not row.current_serial_and_batch_bundle)
 			):
 				self.set_current_serial_and_batch_bundle(voucher_detail_no, save=True)
 				row.reload()
diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.js b/erpnext/stock/report/stock_ledger/stock_ledger.js
index b00b422..2ec757b 100644
--- a/erpnext/stock/report/stock_ledger/stock_ledger.js
+++ b/erpnext/stock/report/stock_ledger/stock_ledger.js
@@ -91,6 +91,12 @@
 			"options": "Currency\nFloat",
 			"default": "Currency"
 		},
+		{
+			"fieldname": "segregate_serial_batch_bundle",
+			"label": __("Segregate Serial / Batch Bundle"),
+			"fieldtype": "Check",
+			"default": 0
+		}
 	],
 	"formatter": function (value, row, column, data, default_formatter) {
 		value = default_formatter(value, row, column, data);
diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py
index 13ebfe4..d859f4e 100644
--- a/erpnext/stock/report/stock_ledger/stock_ledger.py
+++ b/erpnext/stock/report/stock_ledger/stock_ledger.py
@@ -2,6 +2,8 @@
 # License: GNU General Public License v3. See license.txt
 
 
+import copy
+
 import frappe
 from frappe import _
 from frappe.query_builder.functions import CombineDatetime
@@ -26,6 +28,10 @@
 	item_details = get_item_details(items, sl_entries, include_uom)
 	opening_row = get_opening_balance(filters, columns, sl_entries)
 	precision = cint(frappe.db.get_single_value("System Settings", "float_precision"))
+	bundle_details = {}
+
+	if filters.get("segregate_serial_batch_bundle"):
+		bundle_details = get_serial_batch_bundle_details(sl_entries)
 
 	data = []
 	conversion_factors = []
@@ -45,6 +51,9 @@
 		item_detail = item_details[sle.item_code]
 
 		sle.update(item_detail)
+		if bundle_info := bundle_details.get(sle.serial_and_batch_bundle):
+			data.extend(get_segregated_bundle_entries(sle, bundle_info))
+			continue
 
 		if filters.get("batch_no") or inventory_dimension_filters_applied:
 			actual_qty += flt(sle.actual_qty, precision)
@@ -76,6 +85,60 @@
 	return columns, data
 
 
+def get_segregated_bundle_entries(sle, bundle_details):
+	segregated_entries = []
+	qty_before_transaction = sle.qty_after_transaction - sle.actual_qty
+	stock_value_before_transaction = sle.stock_value - sle.stock_value_difference
+
+	for row in bundle_details:
+		new_sle = copy.deepcopy(sle)
+		new_sle.update(row)
+
+		new_sle.update(
+			{
+				"in_out_rate": flt(new_sle.stock_value_difference / row.qty) if row.qty else 0,
+				"in_qty": row.qty if row.qty > 0 else 0,
+				"out_qty": row.qty if row.qty < 0 else 0,
+				"qty_after_transaction": qty_before_transaction + row.qty,
+				"stock_value": stock_value_before_transaction + new_sle.stock_value_difference,
+				"incoming_rate": row.incoming_rate if row.qty > 0 else 0,
+			}
+		)
+
+		qty_before_transaction += row.qty
+		stock_value_before_transaction += new_sle.stock_value_difference
+
+		new_sle.valuation_rate = (
+			stock_value_before_transaction / qty_before_transaction if qty_before_transaction else 0
+		)
+
+		segregated_entries.append(new_sle)
+
+	return segregated_entries
+
+
+def get_serial_batch_bundle_details(sl_entries):
+	bundle_details = []
+	for sle in sl_entries:
+		if sle.serial_and_batch_bundle:
+			bundle_details.append(sle.serial_and_batch_bundle)
+
+	if not bundle_details:
+		return frappe._dict({})
+
+	_bundle_details = frappe._dict({})
+	batch_entries = frappe.get_all(
+		"Serial and Batch Entry",
+		filters={"parent": ("in", bundle_details)},
+		fields=["parent", "qty", "incoming_rate", "stock_value_difference", "batch_no", "serial_no"],
+		order_by="parent, idx",
+	)
+	for entry in batch_entries:
+		_bundle_details.setdefault(entry.parent, []).append(entry)
+
+	return _bundle_details
+
+
 def update_available_serial_nos(available_serial_nos, sle):
 	serial_nos = get_serial_nos(sle.serial_no)
 	key = (sle.item_code, sle.warehouse)
@@ -256,7 +319,6 @@
 				"options": "Serial and Batch Bundle",
 				"width": 100,
 			},
-			{"label": _("Balance Serial No"), "fieldname": "balance_serial_no", "width": 100},
 			{
 				"label": _("Project"),
 				"fieldname": "project",
@@ -320,15 +382,45 @@
 	if items:
 		query = query.where(sle.item_code.isin(items))
 
-	for field in ["voucher_no", "batch_no", "project", "company"]:
+	for field in ["voucher_no", "project", "company"]:
 		if filters.get(field) and field not in inventory_dimension_fields:
 			query = query.where(sle[field] == filters.get(field))
 
+	if filters.get("batch_no"):
+		bundles = get_serial_and_batch_bundles(filters)
+
+		if bundles:
+			query = query.where(
+				(sle.serial_and_batch_bundle.isin(bundles)) | (sle.batch_no == filters.batch_no)
+			)
+		else:
+			query = query.where(sle.batch_no == filters.batch_no)
+
 	query = apply_warehouse_filter(query, sle, filters)
 
 	return query.run(as_dict=True)
 
 
+def get_serial_and_batch_bundles(filters):
+	SBB = frappe.qb.DocType("Serial and Batch Bundle")
+	SBE = frappe.qb.DocType("Serial and Batch Entry")
+
+	query = (
+		frappe.qb.from_(SBE)
+		.inner_join(SBB)
+		.on(SBE.parent == SBB.name)
+		.select(SBE.parent)
+		.where(
+			(SBB.docstatus == 1)
+			& (SBB.has_batch_no == 1)
+			& (SBB.voucher_no.notnull())
+			& (SBE.batch_no == filters.batch_no)
+		)
+	)
+
+	return query.run(pluck=SBE.parent)
+
+
 def get_inventory_dimension_fields():
 	return [dimension.fieldname for dimension in get_inventory_dimensions()]
 
diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py
index d8b5b34..fc1cca4 100644
--- a/erpnext/stock/serial_batch_bundle.py
+++ b/erpnext/stock/serial_batch_bundle.py
@@ -5,7 +5,7 @@
 from frappe import _, bold
 from frappe.model.naming import make_autoname
 from frappe.query_builder.functions import CombineDatetime, Sum
-from frappe.utils import cint, flt, get_link_to_form, now, nowtime, today
+from frappe.utils import cint, cstr, flt, get_link_to_form, now, nowtime, today
 
 from erpnext.stock.deprecated_serial_batch import (
 	DeprecatedBatchNoValuation,
@@ -138,9 +138,17 @@
 				self.child_doctype, self.sle.voucher_detail_no, "rejected_serial_and_batch_bundle", sn_doc.name
 			)
 		else:
-			frappe.db.set_value(
-				self.child_doctype, self.sle.voucher_detail_no, "serial_and_batch_bundle", sn_doc.name
-			)
+			values_to_update = {
+				"serial_and_batch_bundle": sn_doc.name,
+			}
+
+			if frappe.db.get_single_value("Stock Settings", "use_serial_batch_fields"):
+				if sn_doc.has_serial_no:
+					values_to_update["serial_no"] = "\n".join(cstr(d.serial_no) for d in sn_doc.entries)
+				elif sn_doc.has_batch_no and len(sn_doc.entries) == 1:
+					values_to_update["batch_no"] = sn_doc.entries[0].batch_no
+
+			frappe.db.set_value(self.child_doctype, self.sle.voucher_detail_no, values_to_update)
 
 	@property
 	def child_doctype(self):
@@ -905,8 +913,6 @@
 			self.batches = get_available_batches(kwargs)
 
 	def set_auto_serial_batch_entries_for_inward(self):
-		print(self.get("serial_nos"))
-
 		if (self.get("batches") and self.has_batch_no) or (
 			self.get("serial_nos") and self.has_serial_no
 		):
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 75fdf3b..e8bd3fc 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -903,7 +903,7 @@
 
 		precision = doc.precision("total_qty")
 		self.wh_data.qty_after_transaction += flt(doc.total_qty, precision)
-		if self.wh_data.qty_after_transaction:
+		if flt(self.wh_data.qty_after_transaction, precision):
 			self.wh_data.valuation_rate = flt(self.wh_data.stock_value, precision) / flt(
 				self.wh_data.qty_after_transaction, precision
 			)
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index 82ff5b8..5dc9dd4 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -262,7 +262,7 @@
 			item_code=args.get("item_code"),
 		)
 
-		in_rate = sn_obj.get_incoming_rate()
+		return sn_obj.get_incoming_rate()
 
 	elif item_details and item_details.has_batch_no and args.get("serial_and_batch_bundle"):
 		args.actual_qty = args.qty
@@ -272,23 +272,33 @@
 			item_code=args.get("item_code"),
 		)
 
-		in_rate = batch_obj.get_incoming_rate()
+		return batch_obj.get_incoming_rate()
 
 	elif (args.get("serial_no") or "").strip() and not args.get("serial_and_batch_bundle"):
-		in_rate = get_avg_purchase_rate(args.get("serial_no"))
+		args.actual_qty = args.qty
+		args.serial_nos = get_serial_nos_data(args.get("serial_no"))
+
+		sn_obj = SerialNoValuation(
+			sle=args, warehouse=args.get("warehouse"), item_code=args.get("item_code")
+		)
+
+		return sn_obj.get_incoming_rate()
 	elif (
 		args.get("batch_no")
 		and frappe.db.get_value("Batch", args.get("batch_no"), "use_batchwise_valuation", cache=True)
 		and not args.get("serial_and_batch_bundle")
 	):
-		in_rate = get_batch_incoming_rate(
-			item_code=args.get("item_code"),
+
+		args.actual_qty = args.qty
+		args.batch_nos = frappe._dict({args.batch_no: args})
+
+		batch_obj = BatchNoValuation(
+			sle=args,
 			warehouse=args.get("warehouse"),
-			batch_no=args.get("batch_no"),
-			posting_date=args.get("posting_date"),
-			posting_time=args.get("posting_time"),
+			item_code=args.get("item_code"),
 		)
 
+		return batch_obj.get_incoming_rate()
 	else:
 		valuation_method = get_valuation_method(args.get("item_code"))
 		previous_sle = get_previous_sle(args)
diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py
index dc1529b..e4c4d60 100644
--- a/erpnext/support/doctype/issue/issue.py
+++ b/erpnext/support/doctype/issue/issue.py
@@ -113,8 +113,8 @@
 				"reference_name": self.name,
 			}
 		)
-		communication.ignore_permissions = True
-		communication.ignore_mandatory = True
+		communication.flags.ignore_permissions = True
+		communication.flags.ignore_mandatory = True
 		communication.save()
 
 	@frappe.whitelist()