Merge pull request #39830 from Nihantra-Patel/fix_check_credit_limit

fix: check_credit_limit on_update_after_submit of Sales Order
diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/br_planilha_de_contas.json b/erpnext/accounts/doctype/account/chart_of_accounts/verified/br_planilha_de_contas.json
index a1dbddc..45be1e3 100644
--- a/erpnext/accounts/doctype/account/chart_of_accounts/verified/br_planilha_de_contas.json
+++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/br_planilha_de_contas.json
@@ -56,7 +56,9 @@
                     "Constru\u00e7\u00f5es em Andamento de Im\u00f3veis Destinados \u00e0 Venda": {}, 
                     "Estoques Destinados \u00e0 Doa\u00e7\u00e3o": {}, 
                     "Im\u00f3veis Destinados \u00e0 Venda": {}, 
-                    "Insumos (materiais diretos)": {}, 
+                    "Insumos (materiais diretos)": {
+                        "account_type": "Stock"
+                    }, 
                     "Insumos Agropecu\u00e1rios": {}, 
                     "Mercadorias para Revenda": {}, 
                     "Outras 11": {}, 
@@ -146,6 +148,65 @@
             "root_type": "Asset"
         }, 
         "CUSTOS DE PRODU\u00c7\u00c3O": {
+            "CUSTO DOS PRODUTOS E SERVI\u00c7OS VENDIDOS": {
+                "CUSTO DOS PRODUTOS VENDIDOS": {
+                    "CUSTO DOS PRODUTOS VENDIDOS PARA AS DEMAIS ATIVIDADES": {
+                        "Custos dos Produtos Vendidos em Geral": {
+                            "account_type": "Cost of Goods Sold"
+                        }, 
+                        "Outros Custos 4": {}, 
+                        "account_type": "Cost of Goods Sold"
+                    }, 
+                    "CUSTO DOS PRODUTOS VENDIDOS PARA ASSIST\u00caNCIA SOCIAL": {
+                        "Custos dos Produtos para Assist\u00eancia Social - Gratuidades": {}, 
+                        "Custos dos Produtos para Assist\u00eancia Social - Vendidos": {}, 
+                        "Outras": {}
+                    }, 
+                    "CUSTO DOS PRODUTOS VENDIDOS PARA EDUCA\u00c7\u00c3O": {
+                        "Custos dos Produtos para Educa\u00e7\u00e3o - Gratuidades": {}, 
+                        "Custos dos Produtos para Educa\u00e7\u00e3o - Vendidos": {}, 
+                        "Outros Custos 6": {}
+                    }, 
+                    "CUSTO DOS PRODUTOS VENDIDOS PARA SA\u00daDE": {
+                        "Custos dos Produtos para Sa\u00fade - Gratuidades": {}, 
+                        "Custos dos Produtos para Sa\u00fade \u2013 Vendidos": {}, 
+                        "Outros Custos 5": {}
+                    }, 
+                    "account_type": "Cost of Goods Sold"
+                }, 
+                "CUSTO DOS SERVI\u00c7OS PRESTADOS": {
+                    "CUSTO DOS SERVI\u00c7OS PRESTADOS PARA AS DEMAIS ATIVIDADES": {
+                        "Custo dos Servi\u00e7os Prestados em Geral": {}, 
+                        "Outros Custos": {}
+                    }, 
+                    "CUSTO DOS SERVI\u00c7OS PRESTADOS PARA ASSIST\u00caNCIA SOCIAL": {
+                        "Custo dos Servi\u00e7os Prestados a Conv\u00eanios/Contratos/Parcerias": {}, 
+                        "Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es 1": {}, 
+                        "Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es/Subven\u00e7\u00f5es Vinculadas 1": {}, 
+                        "Custo dos Servi\u00e7os Prestados a Gratuidade 1": {}, 
+                        "Custo dos Servi\u00e7os Prestados a Pacientes Particulares": {}, 
+                        "Outros Custos 2": {}
+                    }, 
+                    "CUSTO DOS SERVI\u00c7OS PRESTADOS PARA EDUCA\u00c7\u00c3O": {
+                        "Custo dos Servi\u00e7os Prestados a Alunos N\u00e3o Bolsistas": {}, 
+                        "Custo dos Servi\u00e7os Prestados a Conv\u00eanios/Contratos/Parcerias (Exceto PROUNI)": {}, 
+                        "Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es": {}, 
+                        "Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es/Subven\u00e7\u00f5es Vinculadas": {}, 
+                        "Custo dos Servi\u00e7os Prestados a Gratuidade": {}, 
+                        "Custo dos Servi\u00e7os Prestados ao PROUNI": {}, 
+                        "Outros Custos 1": {}
+                    }, 
+                    "CUSTO DOS SERVI\u00c7OS PRESTADOS PARA SA\u00daDE": {
+                        "Custo dos Servi\u00e7os Prestados a Conv\u00eanios SUS": {}, 
+                        "Custo dos Servi\u00e7os Prestados a Conv\u00eanios/Contratos/Parcerias 1": {}, 
+                        "Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es 2": {}, 
+                        "Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es/Subven\u00e7\u00f5es Vinculadas 2": {}, 
+                        "Custo dos Servi\u00e7os Prestados a Gratuidade 2": {}, 
+                        "Custo dos Servi\u00e7os Prestados a Pacientes Particulares 1": {}, 
+                        "Outros Custos 3": {}
+                    }
+                }
+            }, 
             "CUSTO DOS BENS E SERVI\u00c7OS PRODUZIDOS": {
                 "CUSTO DOS PRODUTOS DE FABRICA\u00c7\u00c3O PR\u00d3PRIA PRODUZIDOS": {
                     "Alimenta\u00e7\u00e3o do Trabalhador": {}, 
@@ -621,7 +682,9 @@
                             "Receita das Unidades Imobili\u00e1rias Vendidas": {}, 
                             "Receita de Exporta\u00e7\u00e3o Direta de Mercadorias e Produtos": {}, 
                             "Receita de Exporta\u00e7\u00e3o de Servi\u00e7os": {}, 
-                            "Receita de Loca\u00e7\u00e3o de Bens M\u00f3veis e Im\u00f3veis": {}, 
+                            "Receita de Loca\u00e7\u00e3o de Bens M\u00f3veis e Im\u00f3veis": {
+                                "account_type": "Income Account"
+                            }, 
                             "Receita de Vendas de Mercadorias e Produtos a Comercial Exportadora com Fim Espec\u00edfico de Exporta\u00e7\u00e3o": {}
                         }
                     }
@@ -645,65 +708,6 @@
                 }
             }, 
             "RESULTADO OPERACIONAL": {
-                "CUSTO DOS PRODUTOS E SERVI\u00c7OS VENDIDOS": {
-                    "CUSTO DOS PRODUTOS VENDIDOS": {
-                        "CUSTO DOS PRODUTOS VENDIDOS PARA AS DEMAIS ATIVIDADES": {
-                            "Custos dos Produtos Vendidos em Geral": {
-                                "account_type": "Cost of Goods Sold"
-                            }, 
-                            "Outros Custos 4": {}, 
-                            "account_type": "Cost of Goods Sold"
-                        }, 
-                        "CUSTO DOS PRODUTOS VENDIDOS PARA ASSIST\u00caNCIA SOCIAL": {
-                            "Custos dos Produtos para Assist\u00eancia Social - Gratuidades": {}, 
-                            "Custos dos Produtos para Assist\u00eancia Social - Vendidos": {}, 
-                            "Outras": {}
-                        }, 
-                        "CUSTO DOS PRODUTOS VENDIDOS PARA EDUCA\u00c7\u00c3O": {
-                            "Custos dos Produtos para Educa\u00e7\u00e3o - Gratuidades": {}, 
-                            "Custos dos Produtos para Educa\u00e7\u00e3o - Vendidos": {}, 
-                            "Outros Custos 6": {}
-                        }, 
-                        "CUSTO DOS PRODUTOS VENDIDOS PARA SA\u00daDE": {
-                            "Custos dos Produtos para Sa\u00fade - Gratuidades": {}, 
-                            "Custos dos Produtos para Sa\u00fade \u2013 Vendidos": {}, 
-                            "Outros Custos 5": {}
-                        }, 
-                        "account_type": "Cost of Goods Sold"
-                    }, 
-                    "CUSTO DOS SERVI\u00c7OS PRESTADOS": {
-                        "CUSTO DOS SERVI\u00c7OS PRESTADOS PARA AS DEMAIS ATIVIDADES": {
-                            "Custo dos Servi\u00e7os Prestados em Geral": {}, 
-                            "Outros Custos": {}
-                        }, 
-                        "CUSTO DOS SERVI\u00c7OS PRESTADOS PARA ASSIST\u00caNCIA SOCIAL": {
-                            "Custo dos Servi\u00e7os Prestados a Conv\u00eanios/Contratos/Parcerias": {}, 
-                            "Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es 1": {}, 
-                            "Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es/Subven\u00e7\u00f5es Vinculadas 1": {}, 
-                            "Custo dos Servi\u00e7os Prestados a Gratuidade 1": {}, 
-                            "Custo dos Servi\u00e7os Prestados a Pacientes Particulares": {}, 
-                            "Outros Custos 2": {}
-                        }, 
-                        "CUSTO DOS SERVI\u00c7OS PRESTADOS PARA EDUCA\u00c7\u00c3O": {
-                            "Custo dos Servi\u00e7os Prestados a Alunos N\u00e3o Bolsistas": {}, 
-                            "Custo dos Servi\u00e7os Prestados a Conv\u00eanios/Contratos/Parcerias (Exceto PROUNI)": {}, 
-                            "Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es": {}, 
-                            "Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es/Subven\u00e7\u00f5es Vinculadas": {}, 
-                            "Custo dos Servi\u00e7os Prestados a Gratuidade": {}, 
-                            "Custo dos Servi\u00e7os Prestados ao PROUNI": {}, 
-                            "Outros Custos 1": {}
-                        }, 
-                        "CUSTO DOS SERVI\u00c7OS PRESTADOS PARA SA\u00daDE": {
-                            "Custo dos Servi\u00e7os Prestados a Conv\u00eanios SUS": {}, 
-                            "Custo dos Servi\u00e7os Prestados a Conv\u00eanios/Contratos/Parcerias 1": {}, 
-                            "Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es 2": {}, 
-                            "Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es/Subven\u00e7\u00f5es Vinculadas 2": {}, 
-                            "Custo dos Servi\u00e7os Prestados a Gratuidade 2": {}, 
-                            "Custo dos Servi\u00e7os Prestados a Pacientes Particulares 1": {}, 
-                            "Outros Custos 3": {}
-                        }
-                    }
-                }, 
                 "DESPESAS OPERACIONAIS": {
                     "DESPESAS OPERACIONAIS 1": {
                         "DESPESAS OPERACIONAIS 2": {
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/dunning/dunning.py b/erpnext/accounts/doctype/dunning/dunning.py
index c61c332..e3897bf 100644
--- a/erpnext/accounts/doctype/dunning/dunning.py
+++ b/erpnext/accounts/doctype/dunning/dunning.py
@@ -85,7 +85,14 @@
 				frappe.throw(
 					_(
 						"The currency of invoice {} ({}) is different from the currency of this dunning ({})."
-					).format(row.sales_invoice, invoice_currency, self.currency)
+					).format(
+						frappe.get_desk_link(
+							"Sales Invoice",
+							row.sales_invoice,
+						),
+						invoice_currency,
+						self.currency,
+					)
 				)
 
 	def validate_overdue_payments(self):
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/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index abc0694..3a930e0 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -270,7 +270,7 @@
 		super(SalesInvoice, self).validate()
 		self.validate_auto_set_posting_time()
 
-		if not self.is_pos:
+		if not (self.is_pos or self.is_debit_note):
 			self.so_dn_required()
 
 		self.set_tax_withholding()
@@ -1478,9 +1478,7 @@
 								"credit_in_account_currency": payment_mode.base_amount
 								if self.party_account_currency == self.company_currency
 								else payment_mode.amount,
-								"against_voucher": self.return_against
-								if cint(self.is_return) and self.return_against
-								else self.name,
+								"against_voucher": self.name,
 								"against_voucher_type": self.doctype,
 								"cost_center": self.cost_center,
 							},
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 8c3aede..c6a8362 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -1105,6 +1105,44 @@
 		self.assertEqual(pos.grand_total, 100.0)
 		self.assertEqual(pos.write_off_amount, 10)
 
+	def test_ledger_entries_of_return_pos_invoice(self):
+		make_pos_profile()
+
+		pos = create_sales_invoice(do_not_save=True)
+		pos.is_pos = 1
+		pos.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 100})
+		pos.save().submit()
+		self.assertEqual(pos.outstanding_amount, 0.0)
+		self.assertEqual(pos.status, "Paid")
+
+		from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_sales_return
+
+		pos_return = make_sales_return(pos.name)
+		pos_return.save().submit()
+		pos_return.reload()
+		pos.reload()
+		self.assertEqual(pos_return.is_return, 1)
+		self.assertEqual(pos_return.return_against, pos.name)
+		self.assertEqual(pos_return.outstanding_amount, 0.0)
+		self.assertEqual(pos_return.status, "Return")
+		self.assertEqual(pos.outstanding_amount, 0.0)
+		self.assertEqual(pos.status, "Credit Note Issued")
+
+		expected = (
+			("Cash - _TC", 0.0, 100.0, pos_return.name, None),
+			("Debtors - _TC", 0.0, 100.0, pos_return.name, pos_return.name),
+			("Debtors - _TC", 100.0, 0.0, pos_return.name, pos_return.name),
+			("Sales - _TC", 100.0, 0.0, pos_return.name, None),
+		)
+		res = frappe.db.get_all(
+			"GL Entry",
+			filters={"voucher_no": pos_return.name, "is_cancelled": 0},
+			fields=["account", "debit", "credit", "voucher_no", "against_voucher"],
+			order_by="account, debit, credit",
+			as_list=1,
+		)
+		self.assertEqual(expected, res)
+
 	def test_pos_with_no_gl_entry_for_change_amount(self):
 		frappe.db.set_single_value("Accounts Settings", "post_change_gl_entries", 0)
 
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index fc9034b..d8ae2a4 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -9,7 +9,7 @@
 from frappe.contacts.doctype.address.address import get_company_address, get_default_address
 from frappe.core.doctype.user_permission.user_permission import get_permitted_documents
 from frappe.model.utils import get_fetch_values
-from frappe.query_builder.functions import Abs, Date, Sum
+from frappe.query_builder.functions import Abs, Count, Date, Sum
 from frappe.utils import (
 	add_days,
 	add_months,
@@ -784,34 +784,37 @@
 	from frappe.desk.form.load import get_communication_data
 
 	out = {}
-	fields = "creation, count(*)"
 	after = add_years(None, -1).strftime("%Y-%m-%d")
-	group_by = "group by Date(creation)"
 
 	data = get_communication_data(
 		doctype,
 		name,
 		after=after,
-		group_by="group by creation",
-		fields="C.creation as creation, count(C.name)",
+		group_by="group by communication_date",
+		fields="C.communication_date as communication_date, count(C.name)",
 		as_dict=False,
 	)
 
 	# fetch and append data from Activity Log
-	data += frappe.db.sql(
-		"""select {fields}
-		from `tabActivity Log`
-		where (reference_doctype=%(doctype)s and reference_name=%(name)s)
-		or (timeline_doctype in (%(doctype)s) and timeline_name=%(name)s)
-		or (reference_doctype in ("Quotation", "Opportunity") and timeline_name=%(name)s)
-		and status!='Success' and creation > {after}
-		{group_by} order by creation desc
-		""".format(
-			fields=fields, group_by=group_by, after=after
-		),
-		{"doctype": doctype, "name": name},
-		as_dict=False,
-	)
+	activity_log = frappe.qb.DocType("Activity Log")
+	data += (
+		frappe.qb.from_(activity_log)
+		.select(activity_log.communication_date, Count(activity_log.name))
+		.where(
+			(
+				((activity_log.reference_doctype == doctype) & (activity_log.reference_name == name))
+				| ((activity_log.timeline_doctype == doctype) & (activity_log.timeline_name == name))
+				| (
+					(activity_log.reference_doctype.isin(["Quotation", "Opportunity"]))
+					& (activity_log.timeline_name == name)
+				)
+			)
+			& (activity_log.status != "Success")
+			& (activity_log.creation > after)
+		)
+		.groupby(activity_log.communication_date)
+		.orderby(activity_log.communication_date, order=frappe.qb.desc)
+	).run()
 
 	timeline_items = dict(data)
 
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/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py
index e4efefe..7162aef 100644
--- a/erpnext/accounts/report/gross_profit/gross_profit.py
+++ b/erpnext/accounts/report/gross_profit/gross_profit.py
@@ -975,7 +975,7 @@
 						& (sle.is_cancelled == 0)
 					)
 					.orderby(sle.item_code)
-					.orderby(sle.warehouse, sle.posting_date, sle.posting_time, sle.creation, order=Order.desc)
+					.orderby(sle.warehouse, sle.posting_datetime, sle.creation, order=Order.desc)
 					.run(as_dict=True)
 				)
 
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/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py
index 3f178f4..eaeaa62 100644
--- a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py
+++ b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py
@@ -163,7 +163,7 @@
 		"""select
 		voucher_type, voucher_no, party_type, party, posting_date, debit, credit, remarks, against_voucher
 		from `tabGL Entry`
-		where company=%(company)s and voucher_type in ('Journal Entry', 'Payment Entry') {0}
+		where company=%(company)s and voucher_type in ('Journal Entry', 'Payment Entry') and is_cancelled = 0 {0}
 	""".format(
 			get_conditions(filters)
 		),
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 64bc39a..157cfdd 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -982,46 +982,6 @@
 	return precision
 
 
-def get_stock_rbnb_difference(posting_date, company):
-	stock_items = frappe.db.sql_list(
-		"""select distinct item_code
-		from `tabStock Ledger Entry` where company=%s""",
-		company,
-	)
-
-	pr_valuation_amount = frappe.db.sql(
-		"""
-		select sum(pr_item.valuation_rate * pr_item.qty * pr_item.conversion_factor)
-		from `tabPurchase Receipt Item` pr_item, `tabPurchase Receipt` pr
-		where pr.name = pr_item.parent and pr.docstatus=1 and pr.company=%s
-		and pr.posting_date <= %s and pr_item.item_code in (%s)"""
-		% ("%s", "%s", ", ".join(["%s"] * len(stock_items))),
-		tuple([company, posting_date] + stock_items),
-	)[0][0]
-
-	pi_valuation_amount = frappe.db.sql(
-		"""
-		select sum(pi_item.valuation_rate * pi_item.qty * pi_item.conversion_factor)
-		from `tabPurchase Invoice Item` pi_item, `tabPurchase Invoice` pi
-		where pi.name = pi_item.parent and pi.docstatus=1 and pi.company=%s
-		and pi.posting_date <= %s and pi_item.item_code in (%s)"""
-		% ("%s", "%s", ", ".join(["%s"] * len(stock_items))),
-		tuple([company, posting_date] + stock_items),
-	)[0][0]
-
-	# Balance should be
-	stock_rbnb = flt(pr_valuation_amount, 2) - flt(pi_valuation_amount, 2)
-
-	# Balance as per system
-	stock_rbnb_account = "Stock Received But Not Billed - " + frappe.get_cached_value(
-		"Company", company, "abbr"
-	)
-	sys_bal = get_balance_on(stock_rbnb_account, posting_date, in_account_currency=False)
-
-	# Amount should be credited
-	return flt(stock_rbnb) + flt(sys_bal)
-
-
 def get_held_invoices(party_type, party):
 	"""
 	Returns a list of names Purchase Invoices for the given party that are on hold
@@ -1428,8 +1388,7 @@
 		.select(sle.voucher_type, sle.voucher_no, sle.posting_date, sle.posting_time, sle.creation)
 		.where((sle.is_cancelled == 0) & (sle.voucher_no.isin(voucher_nos)))
 		.groupby(sle.voucher_type, sle.voucher_no)
-		.orderby(sle.posting_date)
-		.orderby(sle.posting_time)
+		.orderby(sle.posting_datetime)
 		.orderby(sle.creation)
 	).run(as_dict=True)
 	sorted_vouchers = [(sle.voucher_type, sle.voucher_no) for sle in sles]
diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py
index df4593b..191675c 100644
--- a/erpnext/assets/doctype/asset/depreciation.py
+++ b/erpnext/assets/doctype/asset/depreciation.py
@@ -561,15 +561,14 @@
 def reverse_depreciation_entry_made_after_disposal(asset, date):
 	for row in asset.get("finance_books"):
 		asset_depr_schedule_doc = get_asset_depr_schedule_doc(asset.name, "Active", row.finance_book)
-		if not asset_depr_schedule_doc:
+		if not asset_depr_schedule_doc or not asset_depr_schedule_doc.get("depreciation_schedule"):
 			continue
 
 		for schedule_idx, schedule in enumerate(asset_depr_schedule_doc.get("depreciation_schedule")):
-			if schedule.schedule_date == date:
+			if schedule.schedule_date == date and schedule.journal_entry:
 				if not disposal_was_made_on_original_schedule_date(
 					schedule_idx, row, date
 				) or disposal_happens_in_the_future(date):
-
 					reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry)
 					reverse_journal_entry.posting_date = nowdate()
 
diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js
index 2f0de97..110f2c4 100644
--- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js
+++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js
@@ -6,7 +6,7 @@
 
 erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.stock.StockController {
 	setup() {
-		this.frm.ignore_doctypes_on_cancel_all = ['Serial and Batch Bundle'];
+		this.frm.ignore_doctypes_on_cancel_all = ['Serial and Batch Bundle', 'Asset Movement'];
 		this.setup_posting_date_time_check();
 	}
 
diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
index c9ed806..e27a492 100644
--- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
+++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
@@ -138,6 +138,7 @@
 			"Repost Item Valuation",
 			"Serial and Batch Bundle",
 			"Asset",
+			"Asset Movement",
 		)
 		self.cancel_target_asset()
 		self.update_stock_ledger()
@@ -147,7 +148,7 @@
 	def cancel_target_asset(self):
 		if self.entry_type == "Capitalization" and self.target_asset:
 			asset_doc = frappe.get_doc("Asset", self.target_asset)
-			frappe.db.set_value("Asset", self.target_asset, "capitalized_in", None)
+			asset_doc.db_set("capitalized_in", None)
 			if asset_doc.docstatus == 1:
 				asset_doc.cancel()
 
diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py
index 146c03e..77469df 100644
--- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py
+++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py
@@ -418,14 +418,13 @@
 			)
 
 			# Adjust depreciation amount in the last period based on the expected value after useful life
-			if row.expected_value_after_useful_life and (
-				(
-					n == cint(final_number_of_depreciations) - 1
-					and value_after_depreciation != row.expected_value_after_useful_life
+			if (
+				n == cint(final_number_of_depreciations) - 1
+				and flt(value_after_depreciation) != flt(row.expected_value_after_useful_life)
+			) or flt(value_after_depreciation) < flt(row.expected_value_after_useful_life):
+				depreciation_amount += flt(value_after_depreciation) - flt(
+					row.expected_value_after_useful_life
 				)
-				or value_after_depreciation < row.expected_value_after_useful_life
-			):
-				depreciation_amount += value_after_depreciation - row.expected_value_after_useful_life
 				skip_row = True
 
 			if flt(depreciation_amount, asset_doc.precision("gross_purchase_amount")) > 0:
@@ -813,15 +812,11 @@
 	asset_depr_schedules_names = []
 
 	for row in asset_doc.get("finance_books"):
-		draft_asset_depr_schedule_name = get_asset_depr_schedule_name(
-			asset_doc.name, "Draft", row.finance_book
+		asset_depr_schedule = get_asset_depr_schedule_name(
+			asset_doc.name, ["Draft", "Active"], row.finance_book
 		)
 
-		active_asset_depr_schedule_name = get_asset_depr_schedule_name(
-			asset_doc.name, "Active", row.finance_book
-		)
-
-		if not draft_asset_depr_schedule_name and not active_asset_depr_schedule_name:
+		if not asset_depr_schedule:
 			name = make_draft_asset_depr_schedule(asset_doc, row)
 			asset_depr_schedules_names.append(name)
 
@@ -997,16 +992,20 @@
 
 
 def get_asset_depr_schedule_name(asset_name, status, finance_book=None):
-	finance_book_filter = ["finance_book", "is", "not set"]
-	if finance_book:
+	if finance_book is None:
+		finance_book_filter = ["finance_book", "is", "not set"]
+	else:
 		finance_book_filter = ["finance_book", "=", finance_book]
 
+	if isinstance(status, str):
+		status = [status]
+
 	return frappe.db.get_value(
 		doctype="Asset Depreciation Schedule",
 		filters=[
 			["asset", "=", asset_name],
 			finance_book_filter,
-			["status", "=", status],
+			["status", "in", status],
 		],
 	)
 
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 308a134..32a5a61 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -216,6 +216,19 @@
 					)
 				)
 
+			if self.get("is_return") and self.get("return_against") and not self.get("is_pos"):
+				# 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()
@@ -333,6 +346,12 @@
 			ple = frappe.qb.DocType("Payment Ledger Entry")
 			frappe.qb.from_(ple).delete().where(
 				(ple.voucher_type == self.doctype) & (ple.voucher_no == self.name)
+				| (
+					(ple.against_voucher_type == self.doctype)
+					& (ple.against_voucher_no == self.name)
+					& ple.delinked
+					== 1
+				)
 			).run()
 			frappe.db.sql(
 				"delete from `tabGL Entry` where voucher_type=%s and voucher_no=%s", (self.doctype, self.name)
@@ -2691,7 +2710,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..91ee53a 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]:
@@ -824,7 +824,8 @@
 		if self.doctype == "Purchase Invoice" and not self.get("update_stock"):
 			return
 
-		frappe.db.sql("delete from `tabAsset Movement` where reference_name=%s", self.name)
+		asset_movement = frappe.db.get_value("Asset Movement", {"reference_name": self.name}, "name")
+		frappe.delete_doc("Asset Movement", asset_movement, force=1)
 
 	def validate_schedule_date(self):
 		if not self.get("items"):
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index dc49023..c8d40ed 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -28,7 +28,8 @@
 	def validate(self):
 		super(SellingController, self).validate()
 		self.validate_items()
-		self.validate_max_discount()
+		if not self.get("is_debit_note"):
+			self.validate_max_discount()
 		self.validate_selling_price()
 		self.set_qty_as_per_stock_uom()
 		self.set_po_nos(for_validate=True)
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 74c835c..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 (
@@ -46,6 +46,9 @@
 class StockController(AccountsController):
 	def validate(self):
 		super(StockController, self).validate()
+
+		if self.docstatus == 0:
+			self.validate_duplicate_serial_and_batch_bundle()
 		if not self.get("is_return"):
 			self.validate_inspection()
 		self.validate_serialized_batch()
@@ -55,6 +58,32 @@
 		self.validate_internal_transfer()
 		self.validate_putaway_capacity()
 
+	def validate_duplicate_serial_and_batch_bundle(self):
+		if sbb_list := [
+			item.get("serial_and_batch_bundle")
+			for item in self.items
+			if item.get("serial_and_batch_bundle")
+		]:
+			SLE = frappe.qb.DocType("Stock Ledger Entry")
+			data = (
+				frappe.qb.from_(SLE)
+				.select(SLE.voucher_type, SLE.voucher_no, SLE.serial_and_batch_bundle)
+				.where(
+					(SLE.docstatus == 1)
+					& (SLE.serial_and_batch_bundle.notnull())
+					& (SLE.serial_and_batch_bundle.isin(sbb_list))
+				)
+				.limit(1)
+			).run(as_dict=True)
+
+			if data:
+				data = data[0]
+				frappe.throw(
+					_("Serial and Batch Bundle {0} is already used in {1} {2}.").format(
+						frappe.bold(data.serial_and_batch_bundle), data.voucher_type, data.voucher_no
+					)
+				)
+
 	def make_gl_entries(self, gl_entries=None, from_repost=False):
 		if self.docstatus == 2:
 			make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
@@ -133,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
@@ -142,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")
@@ -156,14 +191,18 @@
 				if self.doctype == "Stock Reconciliation":
 					qty = row.qty
 					type_of_transaction = "Inward"
+					warehouse = row.warehouse
 				else:
-					qty = row.stock_qty
+					qty = row.stock_qty if self.doctype != "Stock Entry" else row.transfer_qty
 					type_of_transaction = get_type_of_transaction(self, row)
+					warehouse = (
+						row.warehouse if self.doctype != "Stock Entry" else row.s_warehouse or row.t_warehouse
+					)
 
 				sn_doc = SerialBatchCreation(
 					{
 						"item_code": row.item_code,
-						"warehouse": row.warehouse,
+						"warehouse": warehouse,
 						"posting_date": self.posting_date,
 						"posting_time": self.posting_time,
 						"voucher_type": self.doctype,
@@ -186,7 +225,6 @@
 					row.db_set(
 						{
 							"rejected_serial_and_batch_bundle": sn_doc.name,
-							"rejected_serial_no": "",
 						}
 					)
 				else:
@@ -194,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:
@@ -938,6 +1009,9 @@
 			"Stock Reconciliation",
 		)
 
+		if not frappe.get_all("Putaway Rule", limit=1):
+			return
+
 		if self.doctype == "Purchase Invoice" and self.get("update_stock") == 0:
 			valid_doctype = False
 
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/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.py b/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.py
index dea3f2d..4f7436f 100644
--- a/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.py
+++ b/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.py
@@ -41,7 +41,9 @@
 			month_list = self.get_month_list()
 
 			for month in month_list:
-				self.columns.append({"fieldname": month, "fieldtype": based_on, "label": month, "width": 200})
+				self.columns.append(
+					{"fieldname": month, "fieldtype": based_on, "label": _(month), "width": 200}
+				)
 
 		elif self.filters.get("range") == "Quarterly":
 			for quarter in range(1, 5):
@@ -156,7 +158,7 @@
 
 		for column in self.columns:
 			if column["fieldname"] != "opportunity_owner" and column["fieldname"] != "sales_stage":
-				labels.append(column["fieldname"])
+				labels.append(_(column["fieldname"]))
 
 		self.chart = {"data": {"labels": labels, "datasets": datasets}, "type": "line"}
 
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/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index 6f35206..27c8493 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -1071,8 +1071,7 @@
 			frappe.qb.from_(sle)
 			.select(sle.valuation_rate)
 			.where((sle.item_code == item_code) & (sle.valuation_rate > 0) & (sle.is_cancelled == 0))
-			.orderby(sle.posting_date, order=frappe.qb.desc)
-			.orderby(sle.posting_time, order=frappe.qb.desc)
+			.orderby(sle.posting_datetime, order=frappe.qb.desc)
 			.orderby(sle.creation, order=frappe.qb.desc)
 			.limit(1)
 		).run(as_dict=True)
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/manufacturing_settings/manufacturing_settings.json b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json
index d3ad51f..63e3fa3 100644
--- a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json
+++ b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json
@@ -7,6 +7,7 @@
  "field_order": [
   "raw_materials_consumption_section",
   "material_consumption",
+  "get_rm_cost_from_consumption_entry",
   "column_break_3",
   "backflush_raw_materials_based_on",
   "capacity_planning",
@@ -202,13 +203,20 @@
    "fieldname": "set_op_cost_and_scrape_from_sub_assemblies",
    "fieldtype": "Check",
    "label": "Set Operating Cost / Scrape Items From Sub-assemblies"
+  },
+  {
+   "default": "0",
+   "depends_on": "eval: doc.material_consumption",
+   "fieldname": "get_rm_cost_from_consumption_entry",
+   "fieldtype": "Check",
+   "label": "Get Raw Materials Cost from Consumption Entry"
   }
  ],
  "icon": "icon-wrench",
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2023-12-28 16:37:44.874096",
+ "modified": "2024-02-08 19:00:37.561244",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Manufacturing Settings",
diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py
index 463ba9f..9a50111 100644
--- a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py
+++ b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py
@@ -26,6 +26,7 @@
 		default_scrap_warehouse: DF.Link | None
 		default_wip_warehouse: DF.Link | None
 		disable_capacity_planning: DF.Check
+		get_rm_cost_from_consumption_entry: DF.Check
 		job_card_excess_transfer: DF.Check
 		make_serial_no_batch_from_work_order: DF.Check
 		material_consumption: DF.Check
diff --git a/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json b/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
index d07bf0f..06c1b49 100644
--- a/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
+++ b/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json
@@ -38,7 +38,8 @@
    "in_list_view": 1,
    "label": "Item Code",
    "options": "Item",
-   "reqd": 1
+   "reqd": 1,
+   "search_index": 1
   },
   {
    "fieldname": "item_name",
@@ -53,7 +54,8 @@
    "in_standard_filter": 1,
    "label": "For Warehouse",
    "options": "Warehouse",
-   "reqd": 1
+   "reqd": 1,
+   "search_index": 1
   },
   {
    "columns": 1,
@@ -141,7 +143,8 @@
    "fieldname": "from_warehouse",
    "fieldtype": "Link",
    "label": "From Warehouse",
-   "options": "Warehouse"
+   "options": "Warehouse",
+   "search_index": 1
   },
   {
    "fetch_from": "item_code.safety_stock",
@@ -199,7 +202,7 @@
  ],
  "istable": 1,
  "links": [],
- "modified": "2023-09-12 12:09:08.358326",
+ "modified": "2024-02-11 16:21:11.977018",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Material Request Plan Item",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json
index 257b60c..54c3893 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.json
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json
@@ -298,7 +298,8 @@
    "no_copy": 1,
    "options": "\nDraft\nSubmitted\nNot Started\nIn Process\nCompleted\nClosed\nCancelled\nMaterial Requested",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "search_index": 1
   },
   {
    "fieldname": "amended_from",
@@ -436,7 +437,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2023-12-26 16:31:13.740777",
+ "modified": "2024-02-11 15:42:47.642481",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Production Plan",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index 6e9d1fc..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)
 
@@ -1768,23 +1771,23 @@
 	return reserved_qty_for_production_plan - reserved_qty_for_production
 
 
+@frappe.request_cache
 def get_non_completed_production_plans():
 	table = frappe.qb.DocType("Production Plan")
 	child = frappe.qb.DocType("Production Plan Item")
 
-	query = (
+	return (
 		frappe.qb.from_(table)
 		.inner_join(child)
 		.on(table.name == child.parent)
 		.select(table.name)
+		.distinct()
 		.where(
 			(table.docstatus == 1)
 			& (table.status.notin(["Completed", "Closed"]))
 			& (child.planned_qty > child.ordered_qty)
 		)
-	).run(as_dict=True)
-
-	return list(set([d.name for d in query]))
+	).run(pluck="name")
 
 
 def get_raw_materials_of_sub_assembly_items(
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index f6e9a07..efe9f53 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -1776,6 +1776,52 @@
 			"Manufacturing Settings", "set_op_cost_and_scrape_from_sub_assemblies", 0
 		)
 
+	@change_settings(
+		"Manufacturing Settings", {"material_consumption": 1, "get_rm_cost_from_consumption_entry": 1}
+	)
+	def test_get_rm_cost_from_consumption_entry(self):
+		from erpnext.stock.doctype.stock_entry.test_stock_entry import (
+			make_stock_entry as make_stock_entry_test_record,
+		)
+
+		rm = make_item(properties={"is_stock_item": 1}).name
+		fg = make_item(properties={"is_stock_item": 1}).name
+
+		make_stock_entry_test_record(
+			purpose="Material Receipt",
+			item_code=rm,
+			target="Stores - _TC",
+			qty=10,
+			basic_rate=100,
+		)
+		make_stock_entry_test_record(
+			purpose="Material Receipt",
+			item_code=rm,
+			target="Stores - _TC",
+			qty=10,
+			basic_rate=200,
+		)
+
+		bom = make_bom(item=fg, raw_materials=[rm], rate=150).name
+		wo = make_wo_order_test_record(
+			production_item=fg,
+			bom_no=bom,
+			qty=10,
+		)
+
+		mte = frappe.get_doc(make_stock_entry(wo.name, "Material Transfer for Manufacture", 10))
+		mte.items[0].s_warehouse = "Stores - _TC"
+		mte.insert().submit()
+
+		mce = frappe.get_doc(make_stock_entry(wo.name, "Material Consumption for Manufacture", 10))
+		mce.insert().submit()
+
+		me = frappe.get_doc(make_stock_entry(wo.name, "Manufacture", 10))
+		me.insert().submit()
+
+		valuation_rate = sum([item.valuation_rate * item.transfer_qty for item in mce.items]) / 10
+		self.assertEqual(me.items[0].valuation_rate, valuation_rate)
+
 
 def prepare_boms_for_sub_assembly_test():
 	if not frappe.db.exists("BOM", {"item": "Test Final SF Item 1"}):
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.json b/erpnext/manufacturing/doctype/work_order/work_order.json
index 1996e19..63c74b6 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.json
+++ b/erpnext/manufacturing/doctype/work_order/work_order.json
@@ -447,7 +447,8 @@
    "no_copy": 1,
    "options": "Production Plan",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "search_index": 1
   },
   {
    "fieldname": "production_plan_item",
@@ -592,7 +593,7 @@
  "image_field": "image",
  "is_submittable": 1,
  "links": [],
- "modified": "2023-08-11 18:35:49.852069",
+ "modified": "2024-02-11 15:47:13.454422",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Work Order",
diff --git a/erpnext/manufacturing/doctype/work_order_item/work_order_item.json b/erpnext/manufacturing/doctype/work_order_item/work_order_item.json
index f354d45..0f4d693 100644
--- a/erpnext/manufacturing/doctype/work_order_item/work_order_item.json
+++ b/erpnext/manufacturing/doctype/work_order_item/work_order_item.json
@@ -36,7 +36,8 @@
    "fieldtype": "Link",
    "in_list_view": 1,
    "label": "Item Code",
-   "options": "Item"
+   "options": "Item",
+   "search_index": 1
   },
   {
    "fieldname": "source_warehouse",
@@ -141,7 +142,7 @@
  ],
  "istable": 1,
  "links": [],
- "modified": "2022-09-28 10:50:43.512562",
+ "modified": "2024-02-11 15:45:32.318374",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Work Order Item",
diff --git a/erpnext/manufacturing/report/completed_work_orders/completed_work_orders.json b/erpnext/manufacturing/report/completed_work_orders/completed_work_orders.json
index be50e93..7925b8a 100644
--- a/erpnext/manufacturing/report/completed_work_orders/completed_work_orders.json
+++ b/erpnext/manufacturing/report/completed_work_orders/completed_work_orders.json
@@ -1,25 +1,28 @@
 {
- "add_total_row": 0, 
- "apply_user_permissions": 1, 
- "creation": "2013-08-12 12:44:27", 
- "disabled": 0, 
- "docstatus": 0, 
- "doctype": "Report", 
- "idx": 3, 
- "is_standard": "Yes", 
- "modified": "2018-02-13 04:58:51.549413", 
- "modified_by": "Administrator", 
- "module": "Manufacturing", 
- "name": "Completed Work Orders", 
- "owner": "Administrator", 
- "query": "SELECT\n  `tabWork Order`.name as \"Work Order:Link/Work Order:200\",\n  `tabWork Order`.creation as \"Date:Date:120\",\n  `tabWork Order`.production_item as \"Item:Link/Item:150\",\n  `tabWork Order`.qty as \"To Produce:Int:100\",\n  `tabWork Order`.produced_qty as \"Produced:Int:100\",\n  `tabWork Order`.company as \"Company:Link/Company:\"\nFROM\n  `tabWork Order`\nWHERE\n  `tabWork Order`.docstatus=1\n  AND ifnull(`tabWork Order`.produced_qty,0) = `tabWork Order`.qty", 
- "ref_doctype": "Work Order", 
- "report_name": "Completed Work Orders", 
- "report_type": "Query Report", 
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2013-08-12 12:44:27",
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 3,
+ "is_standard": "Yes",
+ "letterhead": null,
+ "modified": "2024-02-21 14:35:14.301848",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "Completed Work Orders",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "query": "SELECT\n  `tabWork Order`.name as \"Work Order:Link/Work Order:200\",\n  `tabWork Order`.creation as \"Date:Date:120\",\n  `tabWork Order`.production_item as \"Item:Link/Item:150\",\n  `tabWork Order`.qty as \"To Produce:Int:100\",\n  `tabWork Order`.produced_qty as \"Produced:Int:100\",\n  `tabWork Order`.company as \"Company:Link/Company:\"\nFROM\n  `tabWork Order`\nWHERE\n  `tabWork Order`.docstatus=1\n  AND ifnull(`tabWork Order`.produced_qty,0) >= `tabWork Order`.qty",
+ "ref_doctype": "Work Order",
+ "report_name": "Completed Work Orders",
+ "report_type": "Query Report",
  "roles": [
   {
    "role": "Manufacturing User"
-  }, 
+  },
   {
    "role": "Stock User"
   }
diff --git a/erpnext/manufacturing/report/work_order_summary/work_order_summary.py b/erpnext/manufacturing/report/work_order_summary/work_order_summary.py
index 97f30ef..8d37708 100644
--- a/erpnext/manufacturing/report/work_order_summary/work_order_summary.py
+++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.py
@@ -58,7 +58,7 @@
 		query_filters["creation"] = ("between", [filters.get("from_date"), filters.get("to_date")])
 
 	data = frappe.get_all(
-		"Work Order", fields=fields, filters=query_filters, order_by="planned_start_date asc", debug=1
+		"Work Order", fields=fields, filters=query_filters, order_by="planned_start_date asc"
 	)
 
 	res = []
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 4ead7e7..5dab93b 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -263,6 +263,7 @@
 
 [post_model_sync]
 execute:frappe.delete_doc_if_exists('Workspace', 'ERPNext Integrations Settings')
+erpnext.patches.v14_0.update_posting_datetime_and_dropped_indexes #22-02-2024
 erpnext.patches.v14_0.rename_ongoing_status_in_sla_documents
 erpnext.patches.v14_0.delete_shopify_doctypes
 erpnext.patches.v14_0.delete_healthcare_doctypes
@@ -357,3 +358,4 @@
 erpnext.patches.v14_0.migrate_gl_to_payment_ledger
 erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2023-12-20
 erpnext.patches.v14_0.set_maintain_stock_for_bom_item
+erpnext.patches.v15_0.delete_orphaned_asset_movement_item_records
\ No newline at end of file
diff --git a/erpnext/patches/v14_0/update_posting_datetime_and_dropped_indexes.py b/erpnext/patches/v14_0/update_posting_datetime_and_dropped_indexes.py
new file mode 100644
index 0000000..ca126a4
--- /dev/null
+++ b/erpnext/patches/v14_0/update_posting_datetime_and_dropped_indexes.py
@@ -0,0 +1,19 @@
+import frappe
+
+
+def execute():
+	frappe.db.sql(
+		"""
+		UPDATE `tabStock Ledger Entry`
+			SET posting_datetime = DATE_FORMAT(timestamp(posting_date, posting_time), '%Y-%m-%d %H:%i:%s')
+	"""
+	)
+
+	drop_indexes()
+
+
+def drop_indexes():
+	if not frappe.db.has_index("tabStock Ledger Entry", "posting_sort_index"):
+		return
+
+	frappe.db.sql_ddl("ALTER TABLE `tabStock Ledger Entry` DROP INDEX `posting_sort_index`")
diff --git a/erpnext/patches/v15_0/delete_orphaned_asset_movement_item_records.py b/erpnext/patches/v15_0/delete_orphaned_asset_movement_item_records.py
new file mode 100644
index 0000000..a1d7dc9
--- /dev/null
+++ b/erpnext/patches/v15_0/delete_orphaned_asset_movement_item_records.py
@@ -0,0 +1,11 @@
+import frappe
+
+
+def execute():
+	# nosemgrep
+	frappe.db.sql(
+		"""
+		DELETE FROM `tabAsset Movement Item`
+		WHERE parent NOT IN (SELECT name FROM `tabAsset Movement`)
+		"""
+	)
diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py
index b9d801c..e26d04a 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.py
+++ b/erpnext/projects/doctype/timesheet/timesheet.py
@@ -83,7 +83,7 @@
 	def set_status(self):
 		self.status = {"0": "Draft", "1": "Submitted", "2": "Cancelled"}[str(self.docstatus or 0)]
 
-		if self.per_billed == 100:
+		if flt(self.per_billed, self.precision("per_billed")) >= 100.0:
 			self.status = "Billed"
 
 		if self.sales_invoice:
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..5d24af5 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")
@@ -936,25 +942,35 @@
 	due_date() {
 		// due_date is to be changed, payment terms template and/or payment schedule must
 		// be removed as due_date is automatically changed based on payment terms
-		if (this.frm.doc.due_date && !this.frm.updating_party_details && !this.frm.doc.is_pos) {
-			if (this.frm.doc.payment_terms_template ||
-				(this.frm.doc.payment_schedule && this.frm.doc.payment_schedule.length)) {
-				var message1 = "";
-				var message2 = "";
-				var final_message = __("Please clear the") + " ";
-
-				if (this.frm.doc.payment_terms_template) {
-					message1 = __("selected Payment Terms Template");
-					final_message = final_message + message1;
-				}
-
-				if ((this.frm.doc.payment_schedule || []).length) {
-					message2 = __("Payment Schedule Table");
-					if (message1.length !== 0) message2 = " and " + message2;
-					final_message = final_message + message2;
-				}
-				frappe.msgprint(final_message);
+		if (
+			this.frm.doc.due_date &&
+			!this.frm.updating_party_details &&
+			!this.frm.doc.is_pos &&
+			(
+				this.frm.doc.payment_terms_template ||
+				this.frm.doc.payment_schedule?.length
+			)
+		) {
+			const to_clear = [];
+			if (this.frm.doc.payment_terms_template) {
+				to_clear.push("Payment Terms Template");
 			}
+
+			if (this.frm.doc.payment_schedule?.length) {
+				to_clear.push("Payment Schedule Table");
+			}
+
+			frappe.confirm(
+				__(
+					"Do you want to clear the selected {0}?",
+					[frappe.utils.comma_and(to_clear.map(dt => __(dt)))]
+				),
+				() => {
+					this.frm.set_value("payment_terms_template", "");
+					this.frm.clear_table("payment_schedule");
+					this.frm.refresh_field("payment_schedule");
+				}
+			);
 		}
 	}
 
@@ -1503,31 +1519,33 @@
 	}
 
 	remove_pricing_rule_for_item(item) {
-		let me = this;
-		return this.frm.call({
-			method: "erpnext.accounts.doctype.pricing_rule.pricing_rule.remove_pricing_rule_for_item",
-			args: {
-				pricing_rules: item.pricing_rules,
-				item_details: {
-					"doctype": item.doctype,
-					"name": item.name,
-					"item_code": item.item_code,
-					"pricing_rules": item.pricing_rules,
-					"parenttype": item.parenttype,
-					"parent": item.parent,
-					"price_list_rate": item.price_list_rate
+		if (item.pricing_rules){
+			let me = this;
+			return this.frm.call({
+				method: "erpnext.accounts.doctype.pricing_rule.pricing_rule.remove_pricing_rule_for_item",
+				args: {
+					pricing_rules: item.pricing_rules,
+					item_details: {
+						"doctype": item.doctype,
+						"name": item.name,
+						"item_code": item.item_code,
+						"pricing_rules": item.pricing_rules,
+						"parenttype": item.parenttype,
+						"parent": item.parent,
+						"price_list_rate": item.price_list_rate
+					},
+					item_code: item.item_code,
+					rate: item.price_list_rate,
 				},
-				item_code: item.item_code,
-				rate: item.price_list_rate,
-			},
-			callback: function(r) {
-				if (!r.exc && r.message) {
-					me.remove_pricing_rule(r.message);
-					me.calculate_taxes_and_totals();
-					if(me.frm.doc.apply_discount_on) me.frm.trigger("apply_discount_on");
+				callback: function(r) {
+					if (!r.exc && r.message) {
+						me.remove_pricing_rule(r.message);
+						me.calculate_taxes_and_totals();
+						if(me.frm.doc.apply_discount_on) me.frm.trigger("apply_discount_on");
+					}
 				}
-			}
-		});
+			});
+		}
 	}
 
 	apply_pricing_rule(item, calculate_taxes_and_totals) {
@@ -1633,18 +1651,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/item/item.py b/erpnext/stock/doctype/item/item.py
index feb4583..949c109 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -1122,6 +1122,7 @@
 		frappe.throw(_("Item {0} is cancelled").format(item_code))
 
 
+@frappe.request_cache
 def get_last_purchase_details(item_code, doc_name=None, conversion_rate=1.0):
 	"""returns last purchase details in stock uom"""
 	# get last purchase order item details
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..a913e28 100644
--- a/erpnext/stock/doctype/material_request/material_request.js
+++ b/erpnext/stock/doctype/material_request/material_request.js
@@ -199,6 +199,7 @@
 
 	get_item_data: function(frm, item, overwrite_warehouse=false) {
 		if (item && !item.item_code) { return; }
+
 		frappe.call({
 			method: "erpnext.stock.get_item_details.get_item_details",
 			args: {
@@ -225,20 +226,22 @@
 			},
 			callback: function(r) {
 				const d = item;
-				const qty_fields = ['actual_qty', 'projected_qty', 'min_order_qty'];
+				const allow_to_change_fields = ['actual_qty', 'projected_qty', 'min_order_qty', 'item_name', 'description', 'stock_uom', 'uom', 'conversion_factor', 'stock_qty'];
 
 				if(!r.exc) {
 					$.each(r.message, function(key, value) {
-						if(!d[key] || qty_fields.includes(key)) {
+						if(!d[key] || allow_to_change_fields.includes(key)) {
 							d[key] = value;
 						}
 					});
 
 					if (d.price_list_rate != r.message.price_list_rate) {
+						d.rate = 0.0;
 						d.price_list_rate = r.message.price_list_rate;
-
 						frappe.model.set_value(d.doctype, d.name, "rate", d.price_list_rate);
 					}
+
+					refresh_field("items");
 				}
 			}
 		});
@@ -250,7 +253,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},
@@ -435,7 +438,7 @@
 		frm.events.get_item_data(frm, item, false);
 	},
 
-	rate: function(frm, doctype, name) {
+	rate(frm, doctype, name) {
 		const item = locals[doctype][name];
 		item.amount = flt(item.qty) * flt(item.rate);
 		frappe.model.set_value(doctype, name, "amount", item.amount);
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 2d20922..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)
@@ -2317,6 +2317,119 @@
 			serial_no_status = frappe.db.get_value("Serial No", sn, "status")
 			self.assertTrue(serial_no_status != "Active")
 
+	def test_sle_qty_after_transaction(self):
+		item = make_item(
+			"_Test Item Qty After Transaction",
+			properties={"is_stock_item": 1, "valuation_method": "FIFO"},
+		).name
+
+		posting_date = today()
+		posting_time = nowtime()
+
+		# Step 1: Create Purchase Receipt
+		pr = make_purchase_receipt(
+			item_code=item,
+			qty=1,
+			rate=100,
+			posting_date=posting_date,
+			posting_time=posting_time,
+			do_not_save=1,
+		)
+
+		for i in range(9):
+			pr.append(
+				"items",
+				{
+					"item_code": item,
+					"qty": 1,
+					"rate": 100,
+					"warehouse": pr.items[0].warehouse,
+					"cost_center": pr.items[0].cost_center,
+					"expense_account": pr.items[0].expense_account,
+					"uom": pr.items[0].uom,
+					"stock_uom": pr.items[0].stock_uom,
+					"conversion_factor": pr.items[0].conversion_factor,
+				},
+			)
+
+		self.assertEqual(len(pr.items), 10)
+		pr.save()
+		pr.submit()
+
+		data = frappe.get_all(
+			"Stock Ledger Entry",
+			fields=["qty_after_transaction", "creation", "posting_datetime"],
+			filters={"voucher_no": pr.name, "is_cancelled": 0},
+			order_by="creation",
+		)
+
+		for index, d in enumerate(data):
+			self.assertEqual(d.qty_after_transaction, 1 + index)
+
+		# Step 2: Create Purchase Receipt
+		pr = make_purchase_receipt(
+			item_code=item,
+			qty=1,
+			rate=100,
+			posting_date=posting_date,
+			posting_time=posting_time,
+			do_not_save=1,
+		)
+
+		for i in range(9):
+			pr.append(
+				"items",
+				{
+					"item_code": item,
+					"qty": 1,
+					"rate": 100,
+					"warehouse": pr.items[0].warehouse,
+					"cost_center": pr.items[0].cost_center,
+					"expense_account": pr.items[0].expense_account,
+					"uom": pr.items[0].uom,
+					"stock_uom": pr.items[0].stock_uom,
+					"conversion_factor": pr.items[0].conversion_factor,
+				},
+			)
+
+		self.assertEqual(len(pr.items), 10)
+		pr.save()
+		pr.submit()
+
+		data = frappe.get_all(
+			"Stock Ledger Entry",
+			fields=["qty_after_transaction", "creation", "posting_datetime"],
+			filters={"voucher_no": pr.name, "is_cancelled": 0},
+			order_by="creation",
+		)
+
+		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/serial_and_batch_bundle/test_serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py
index f430943..b932c13 100644
--- a/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py
+++ b/erpnext/stock/doctype/serial_and_batch_bundle/test_serial_and_batch_bundle.py
@@ -4,8 +4,8 @@
 import json
 
 import frappe
-from frappe.tests.utils import FrappeTestCase
-from frappe.utils import add_days, add_to_date, flt, nowdate, nowtime, today
+from frappe.tests.utils import FrappeTestCase, change_settings
+from frappe.utils import flt, nowtime, today
 
 from erpnext.stock.doctype.item.test_item import make_item
 from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import (
@@ -191,6 +191,7 @@
 			doc.flags.ignore_links = True
 			doc.flags.ignore_validate = True
 			doc.submit()
+			doc.reload()
 
 		bundle_doc = make_serial_batch_bundle(
 			{
@@ -521,6 +522,24 @@
 		make_serial_nos(item_code, serial_nos)
 		self.assertTrue(frappe.db.exists("Serial No", serial_no_id))
 
+	@change_settings("Stock Settings", {"auto_create_serial_and_batch_bundle_for_outward": 1})
+	def test_duplicate_serial_and_batch_bundle(self):
+		from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
+
+		item_code = make_item(properties={"is_stock_item": 1, "has_serial_no": 1}).name
+
+		serial_no = f"{item_code}-001"
+		serial_nos = [{"serial_no": serial_no, "qty": 1}]
+		make_serial_nos(item_code, serial_nos)
+
+		pr1 = make_purchase_receipt(item=item_code, qty=1, rate=500, serial_no=[serial_no])
+		pr2 = make_purchase_receipt(item=item_code, qty=1, rate=500, do_not_save=True)
+
+		pr1.reload()
+		pr2.items[0].serial_and_batch_bundle = pr1.items[0].serial_and_batch_bundle
+
+		self.assertRaises(frappe.exceptions.ValidationError, pr2.save)
+
 
 def get_batch_from_bundle(bundle):
 	from erpnext.stock.serial_batch_bundle import get_batch_nos
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 10e3522..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,
 			}
 		)
 
@@ -904,14 +907,62 @@
 				return flt(outgoing_items_cost / total_fg_qty)
 
 	def get_basic_rate_for_manufactured_item(self, finished_item_qty, outgoing_items_cost=0) -> float:
+		settings = frappe.get_single("Manufacturing Settings")
 		scrap_items_cost = sum([flt(d.basic_amount) for d in self.get("items") if d.is_scrap_item])
 
-		# Get raw materials cost from BOM if multiple material consumption entries
-		if not outgoing_items_cost and frappe.db.get_single_value(
-			"Manufacturing Settings", "material_consumption", cache=True
-		):
-			bom_items = self.get_bom_raw_materials(finished_item_qty)
-			outgoing_items_cost = sum([flt(row.qty) * flt(row.rate) for row in bom_items.values()])
+		if settings.material_consumption:
+			if settings.get_rm_cost_from_consumption_entry and self.work_order:
+
+				# Validate only if Material Consumption Entry exists for the Work Order.
+				if frappe.db.exists(
+					"Stock Entry",
+					{
+						"docstatus": 1,
+						"work_order": self.work_order,
+						"purpose": "Material Consumption for Manufacture",
+					},
+				):
+					for item in self.items:
+						if not item.is_finished_item and not item.is_scrap_item:
+							label = frappe.get_meta(settings.doctype).get_label("get_rm_cost_from_consumption_entry")
+							frappe.throw(
+								_(
+									"Row {0}: As {1} is enabled, raw materials cannot be added to {2} entry. Use {3} entry to consume raw materials."
+								).format(
+									item.idx,
+									frappe.bold(label),
+									frappe.bold("Manufacture"),
+									frappe.bold("Material Consumption for Manufacture"),
+								)
+							)
+
+					if frappe.db.exists(
+						"Stock Entry", {"docstatus": 1, "work_order": self.work_order, "purpose": "Manufacture"}
+					):
+						frappe.throw(
+							_("Only one {0} entry can be created against the Work Order {1}").format(
+								frappe.bold("Manufacture"), frappe.bold(self.work_order)
+							)
+						)
+
+					SE = frappe.qb.DocType("Stock Entry")
+					SE_ITEM = frappe.qb.DocType("Stock Entry Detail")
+
+					outgoing_items_cost = (
+						frappe.qb.from_(SE)
+						.left_join(SE_ITEM)
+						.on(SE.name == SE_ITEM.parent)
+						.select(Sum(SE_ITEM.valuation_rate * SE_ITEM.transfer_qty))
+						.where(
+							(SE.docstatus == 1)
+							& (SE.work_order == self.work_order)
+							& (SE.purpose == "Material Consumption for Manufacture")
+						)
+					).run()[0][0] or 0
+
+			elif not outgoing_items_cost:
+				bom_items = self.get_bom_raw_materials(finished_item_qty)
+				outgoing_items_cost = sum([flt(row.qty) * flt(row.rate) for row in bom_items.values()])
 
 		return flt((outgoing_items_cost - scrap_items_cost) / finished_item_qty)
 
@@ -982,6 +1033,9 @@
 		already_picked_serial_nos = []
 
 		for row in self.items:
+			if row.use_serial_batch_fields and (row.serial_no or row.batch_no):
+				continue
+
 			if not row.s_warehouse:
 				continue
 
@@ -989,7 +1043,7 @@
 				continue
 
 			bundle_doc = None
-			if row.serial_and_batch_bundle and abs(row.qty) != abs(
+			if row.serial_and_batch_bundle and abs(row.transfer_qty) != abs(
 				frappe.get_cached_value("Serial and Batch Bundle", row.serial_and_batch_bundle, "total_qty")
 			):
 				bundle_doc = SerialBatchCreation(
@@ -999,7 +1053,7 @@
 						"serial_and_batch_bundle": row.serial_and_batch_bundle,
 						"type_of_transaction": "Outward",
 						"ignore_serial_nos": already_picked_serial_nos,
-						"qty": row.qty * -1,
+						"qty": row.transfer_qty * -1,
 					}
 				).update_serial_and_batch_entries()
 			elif not row.serial_and_batch_bundle:
@@ -1011,7 +1065,7 @@
 						"posting_time": self.posting_time,
 						"voucher_type": self.doctype,
 						"voucher_detail_no": row.name,
-						"qty": row.qty * -1,
+						"qty": row.transfer_qty * -1,
 						"ignore_serial_nos": already_picked_serial_nos,
 						"type_of_transaction": "Outward",
 						"company": self.company,
@@ -1848,6 +1902,7 @@
 			return
 
 		id = create_serial_and_batch_bundle(
+			self,
 			row,
 			frappe._dict(
 				{
@@ -2118,7 +2173,7 @@
 			"to_warehouse": "",
 			"qty": qty,
 			"item_name": item.item_name,
-			"serial_and_batch_bundle": create_serial_and_batch_bundle(row, item, "Outward"),
+			"serial_and_batch_bundle": create_serial_and_batch_bundle(self, row, item, "Outward"),
 			"description": item.description,
 			"stock_uom": item.stock_uom,
 			"expense_account": item.expense_account,
@@ -2496,6 +2551,7 @@
 					row = frappe._dict({"serial_nos": serial_nos[0 : cint(d.qty)]})
 
 					id = create_serial_and_batch_bundle(
+						self,
 						row,
 						frappe._dict(
 							{
@@ -3019,7 +3075,7 @@
 	return data
 
 
-def create_serial_and_batch_bundle(row, child, type_of_transaction=None):
+def create_serial_and_batch_bundle(parent_doc, row, child, type_of_transaction=None):
 	item_details = frappe.get_cached_value(
 		"Item", child.item_code, ["has_serial_no", "has_batch_no"], as_dict=1
 	)
@@ -3037,6 +3093,8 @@
 			"item_code": child.item_code,
 			"warehouse": child.warehouse,
 			"type_of_transaction": type_of_transaction,
+			"posting_date": parent_doc.posting_date,
+			"posting_time": parent_doc.posting_time,
 		}
 	)
 
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
index 0f67e47..271cbbc 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry_utils.py
@@ -92,9 +92,6 @@
 		else:
 			args.qty = cint(args.qty)
 
-	if args.serial_no or args.batch_no:
-		args.use_serial_batch_fields = True
-
 	# purpose
 	if not args.purpose:
 		if args.source and args.target:
@@ -136,7 +133,7 @@
 	serial_number = args.serial_no
 
 	bundle_id = None
-	if args.serial_no or args.batch_no or args.batches:
+	if not args.use_serial_batch_fields and (args.serial_no or args.batch_no or args.batches):
 		batches = frappe._dict({})
 		if args.batch_no:
 			batches = frappe._dict({args.batch_no: args.qty})
@@ -164,7 +161,11 @@
 			.name
 		)
 
-	args.serial_no = serial_number
+		args["serial_no"] = ""
+		args["batch_no"] = ""
+
+	else:
+		args.serial_no = serial_number
 
 	s.append(
 		"items",
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index af91536..9d1a3f7 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -1596,6 +1596,7 @@
 			qty=4,
 			to_warehouse="_Test Warehouse - _TC",
 			batch_no=batch.name,
+			use_serial_batch_fields=1,
 			do_not_save=True,
 		)
 
@@ -1610,24 +1611,22 @@
 		item_code = "Test Negative Item - 001"
 		item_doc = create_item(item_code=item_code, is_stock_item=1, valuation_rate=10)
 
-		make_stock_entry(
+		se1 = make_stock_entry(
 			item_code=item_code,
 			posting_date=add_days(today(), -3),
 			posting_time="00:00:00",
-			purpose="Material Receipt",
+			target="_Test Warehouse - _TC",
 			qty=10,
 			to_warehouse="_Test Warehouse - _TC",
-			do_not_save=True,
 		)
 
-		make_stock_entry(
+		se2 = make_stock_entry(
 			item_code=item_code,
 			posting_date=today(),
 			posting_time="00:00:00",
-			purpose="Material Receipt",
+			source="_Test Warehouse - _TC",
 			qty=8,
 			from_warehouse="_Test Warehouse - _TC",
-			do_not_save=True,
 		)
 
 		sr_doc = create_stock_reconciliation(
@@ -1754,6 +1753,51 @@
 			mr.cancel()
 			mr.delete()
 
+	def test_use_serial_and_batch_fields(self):
+		item = make_item(
+			"Test Use Serial and Batch Item SN Item",
+			{"has_serial_no": 1, "is_stock_item": 1},
+		)
+
+		serial_nos = [
+			"Test Use Serial and Batch Item SN Item - SN 001",
+			"Test Use Serial and Batch Item SN Item - SN 002",
+		]
+
+		se = make_stock_entry(
+			item_code=item.name,
+			qty=2,
+			to_warehouse="_Test Warehouse - _TC",
+			use_serial_batch_fields=1,
+			serial_no="\n".join(serial_nos),
+		)
+
+		self.assertTrue(se.items[0].use_serial_batch_fields)
+		self.assertTrue(se.items[0].serial_no)
+		self.assertTrue(se.items[0].serial_and_batch_bundle)
+
+		for serial_no in serial_nos:
+			self.assertTrue(frappe.db.exists("Serial No", serial_no))
+			self.assertEqual(frappe.db.get_value("Serial No", serial_no, "status"), "Active")
+
+		se1 = make_stock_entry(
+			item_code=item.name,
+			qty=2,
+			from_warehouse="_Test Warehouse - _TC",
+			use_serial_batch_fields=1,
+			serial_no="\n".join(serial_nos),
+		)
+
+		se1.reload()
+
+		self.assertTrue(se1.items[0].use_serial_batch_fields)
+		self.assertTrue(se1.items[0].serial_no)
+		self.assertTrue(se1.items[0].serial_and_batch_bundle)
+
+		for serial_no in serial_nos:
+			self.assertTrue(frappe.db.exists("Serial No", serial_no))
+			self.assertEqual(frappe.db.get_value("Serial No", serial_no, "status"), "Delivered")
+
 
 def make_serialized_item(**args):
 	args = frappe._dict(args)
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 be37994..3a094f1 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
@@ -11,6 +11,7 @@
   "warehouse",
   "posting_date",
   "posting_time",
+  "posting_datetime",
   "is_adjustment_entry",
   "auto_created_serial_and_batch_bundle",
   "column_break_6",
@@ -100,7 +101,6 @@
    "oldfieldtype": "Date",
    "print_width": "100px",
    "read_only": 1,
-   "search_index": 1,
    "width": "100px"
   },
   {
@@ -253,7 +253,6 @@
    "options": "Company",
    "print_width": "150px",
    "read_only": 1,
-   "search_index": 1,
    "width": "150px"
   },
   {
@@ -348,6 +347,11 @@
    "fieldname": "auto_created_serial_and_batch_bundle",
    "fieldtype": "Check",
    "label": "Auto Created Serial and Batch Bundle"
+  },
+  {
+   "fieldname": "posting_datetime",
+   "fieldtype": "Datetime",
+   "label": "Posting Datetime"
   }
  ],
  "hide_toolbar": 1,
@@ -356,7 +360,7 @@
  "in_create": 1,
  "index_web_pages_for_search": 1,
  "links": [],
- "modified": "2023-11-14 16:47:39.791967",
+ "modified": "2024-02-07 09:18:13.999231",
  "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 04441f0..a3e51ca 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -51,6 +51,7 @@
 		item_code: DF.Link | None
 		outgoing_rate: DF.Currency
 		posting_date: DF.Date | None
+		posting_datetime: DF.Datetime | None
 		posting_time: DF.Time | None
 		project: DF.Link | None
 		qty_after_transaction: DF.Float
@@ -92,6 +93,12 @@
 		self.validate_with_last_transaction_posting_time()
 		self.validate_inventory_dimension_negative_stock()
 
+	def set_posting_datetime(self):
+		from erpnext.stock.utils import get_combine_datetime
+
+		self.posting_datetime = get_combine_datetime(self.posting_date, self.posting_time)
+		self.db_set("posting_datetime", self.posting_datetime)
+
 	def validate_inventory_dimension_negative_stock(self):
 		if self.is_cancelled:
 			return
@@ -162,6 +169,7 @@
 		return inv_dimension_dict
 
 	def on_submit(self):
+		self.set_posting_datetime()
 		self.check_stock_frozen_date()
 
 		# Added to handle few test cases where serial_and_batch_bundles are not required
@@ -332,9 +340,7 @@
 
 
 def on_doctype_update():
-	frappe.db.add_index(
-		"Stock Ledger Entry", fields=["posting_date", "posting_time"], index_name="posting_sort_index"
-	)
 	frappe.db.add_index("Stock Ledger Entry", ["voucher_no", "voucher_type"])
 	frappe.db.add_index("Stock Ledger Entry", ["batch_no", "item_code", "warehouse"])
 	frappe.db.add_index("Stock Ledger Entry", ["warehouse", "item_code"], "item_warehouse")
+	frappe.db.add_index("Stock Ledger Entry", ["posting_datetime", "creation"])
diff --git a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
index c099953..40a2d5a 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
@@ -2,6 +2,7 @@
 # See license.txt
 
 import json
+import time
 from uuid import uuid4
 
 import frappe
@@ -1077,7 +1078,7 @@
 				frappe.qb.from_(sle)
 				.select("qty_after_transaction")
 				.where((sle.item_code == item) & (sle.warehouse == warehouse) & (sle.is_cancelled == 0))
-				.orderby(CombineDatetime(sle.posting_date, sle.posting_time))
+				.orderby(sle.posting_datetime)
 				.orderby(sle.creation)
 			).run(pluck=True)
 
@@ -1154,6 +1155,89 @@
 		except Exception as e:
 			self.fail("Double processing of qty for clashing timestamp.")
 
+	def test_previous_sle_with_clashed_timestamp(self):
+
+		item = make_item().name
+		warehouse = "_Test Warehouse - _TC"
+
+		reciept1 = make_stock_entry(
+			item_code=item,
+			to_warehouse=warehouse,
+			qty=100,
+			rate=10,
+			posting_date="2021-01-01",
+			posting_time="02:00:00",
+		)
+
+		time.sleep(3)
+
+		reciept2 = make_stock_entry(
+			item_code=item,
+			to_warehouse=warehouse,
+			qty=5,
+			posting_date="2021-01-01",
+			rate=10,
+			posting_time="02:00:00.1234",
+		)
+
+		sle = frappe.get_all(
+			"Stock Ledger Entry",
+			filters={"voucher_no": reciept1.name},
+			fields=["qty_after_transaction", "actual_qty"],
+		)
+		self.assertEqual(sle[0].qty_after_transaction, 100)
+		self.assertEqual(sle[0].actual_qty, 100)
+
+		sle = frappe.get_all(
+			"Stock Ledger Entry",
+			filters={"voucher_no": reciept2.name},
+			fields=["qty_after_transaction", "actual_qty"],
+		)
+		self.assertEqual(sle[0].qty_after_transaction, 105)
+		self.assertEqual(sle[0].actual_qty, 5)
+
+	def test_backdated_sle_with_same_timestamp(self):
+
+		item = make_item().name
+		warehouse = "_Test Warehouse - _TC"
+
+		reciept1 = make_stock_entry(
+			item_code=item,
+			to_warehouse=warehouse,
+			qty=5,
+			posting_date="2021-01-01",
+			rate=10,
+			posting_time="02:00:00.1234",
+		)
+
+		time.sleep(3)
+
+		# backdated entry with same timestamp but different ms part
+		reciept2 = make_stock_entry(
+			item_code=item,
+			to_warehouse=warehouse,
+			qty=100,
+			rate=10,
+			posting_date="2021-01-01",
+			posting_time="02:00:00",
+		)
+
+		sle = frappe.get_all(
+			"Stock Ledger Entry",
+			filters={"voucher_no": reciept1.name},
+			fields=["qty_after_transaction", "actual_qty"],
+		)
+		self.assertEqual(sle[0].qty_after_transaction, 5)
+		self.assertEqual(sle[0].actual_qty, 5)
+
+		sle = frappe.get_all(
+			"Stock Ledger Entry",
+			filters={"voucher_no": reciept2.name},
+			fields=["qty_after_transaction", "actual_qty"],
+		)
+		self.assertEqual(sle[0].qty_after_transaction, 105)
+		self.assertEqual(sle[0].actual_qty, 100)
+
 	@change_settings("System Settings", {"float_precision": 3, "currency_precision": 2})
 	def test_transfer_invariants(self):
 		"""Extact stock value should be transferred."""
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index ce08615..06fd5f9 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()
@@ -906,8 +906,13 @@
 
 	def has_negative_stock_allowed(self):
 		allow_negative_stock = cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock"))
+		if allow_negative_stock:
+			return True
 
-		if all(d.serial_and_batch_bundle and flt(d.qty) == flt(d.current_qty) for d in self.items):
+		if any(
+			((d.serial_and_batch_bundle or d.batch_no) and flt(d.qty) == flt(d.current_qty))
+			for d in self.items
+		):
 			allow_negative_stock = True
 
 		return allow_negative_stock
diff --git a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py
index 7e03ac3..26fe8e1 100644
--- a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py
+++ b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py
@@ -7,7 +7,7 @@
 from frappe import _
 from frappe.model.document import Document
 from frappe.query_builder.functions import Sum
-from frappe.utils import cint, flt
+from frappe.utils import cint, flt, nowdate, nowtime
 
 from erpnext.stock.utils import get_or_make_bin, get_stock_balance
 
@@ -866,6 +866,8 @@
 		bundle = frappe.new_doc("Serial and Batch Bundle")
 		bundle.type_of_transaction = "Outward"
 		bundle.voucher_type = "Delivery Note"
+		bundle.posting_date = nowdate()
+		bundle.posting_time = nowtime()
 
 		for field in ("item_code", "warehouse", "has_serial_no", "has_batch_no"):
 			setattr(bundle, field, sre[field])
diff --git a/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.py b/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.py
index e4f657c..da958a8 100644
--- a/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.py
+++ b/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.py
@@ -5,7 +5,7 @@
 import frappe
 from frappe import _
 from frappe.query_builder import Field
-from frappe.query_builder.functions import CombineDatetime, Min
+from frappe.query_builder.functions import Min
 from frappe.utils import add_days, getdate, today
 
 import erpnext
@@ -75,7 +75,7 @@
 			& (sle.company == report_filters.company)
 			& (sle.is_cancelled == 0)
 		)
-		.orderby(CombineDatetime(sle.posting_date, sle.posting_time), sle.creation)
+		.orderby(sle.posting_datetime, sle.creation)
 	).run(as_dict=True)
 
 	for d in data:
diff --git a/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py b/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py
index 9e75201..dd79e7f 100644
--- a/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py
+++ b/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py
@@ -213,13 +213,11 @@
 
 	query = (
 		frappe.qb.from_(sle)
-		.force_index("posting_sort_index")
 		.left_join(sle2)
 		.on(
 			(sle.item_code == sle2.item_code)
 			& (sle.warehouse == sle2.warehouse)
-			& (sle.posting_date < sle2.posting_date)
-			& (sle.posting_time < sle2.posting_time)
+			& (sle.posting_datetime < sle2.posting_datetime)
 			& (sle.name < sle2.name)
 		)
 		.select(sle.item_code, sle.warehouse, sle.qty_after_transaction, sle.company)
diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py
index 2693238..500affa 100644
--- a/erpnext/stock/report/stock_balance/stock_balance.py
+++ b/erpnext/stock/report/stock_balance/stock_balance.py
@@ -8,7 +8,7 @@
 import frappe
 from frappe import _
 from frappe.query_builder import Order
-from frappe.query_builder.functions import Coalesce, CombineDatetime
+from frappe.query_builder.functions import Coalesce
 from frappe.utils import add_days, cint, date_diff, flt, getdate
 from frappe.utils.nestedset import get_descendants_of
 
@@ -300,7 +300,7 @@
 				item_table.item_name,
 			)
 			.where((sle.docstatus < 2) & (sle.is_cancelled == 0))
-			.orderby(CombineDatetime(sle.posting_date, sle.posting_time))
+			.orderby(sle.posting_datetime)
 			.orderby(sle.creation)
 			.orderby(sle.actual_qty)
 		)
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 e59f2fe..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",
@@ -283,7 +345,7 @@
 		frappe.qb.from_(sle)
 		.select(
 			sle.item_code,
-			CombineDatetime(sle.posting_date, sle.posting_time).as_("date"),
+			sle.posting_datetime.as_("date"),
 			sle.warehouse,
 			sle.posting_date,
 			sle.posting_time,
@@ -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 e88b192..2ae6c19 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -9,7 +9,7 @@
 import frappe
 from frappe import _, scrub
 from frappe.model.meta import get_field_precision
-from frappe.query_builder.functions import CombineDatetime, Sum
+from frappe.query_builder.functions import Sum
 from frappe.utils import (
 	cint,
 	cstr,
@@ -33,6 +33,7 @@
 	get_sre_reserved_serial_nos_details,
 )
 from erpnext.stock.utils import (
+	get_combine_datetime,
 	get_incoming_outgoing_rate_for_cancel,
 	get_incoming_rate,
 	get_or_make_bin,
@@ -95,6 +96,7 @@
 				sle_doc = make_entry(sle, allow_negative_stock, via_landed_cost_voucher)
 
 			args = sle_doc.as_dict()
+			args["posting_datetime"] = get_combine_datetime(args.posting_date, args.posting_time)
 
 			if sle.get("voucher_type") == "Stock Reconciliation":
 				# preserve previous_qty_after_transaction for qty reposting
@@ -616,12 +618,14 @@
 			self.process_sle(sle)
 
 	def get_sle_against_current_voucher(self):
-		self.args["time_format"] = "%H:%i:%s"
+		self.args["posting_datetime"] = get_combine_datetime(
+			self.args.posting_date, self.args.posting_time
+		)
 
 		return frappe.db.sql(
 			"""
 			select
-				*, timestamp(posting_date, posting_time) as "timestamp"
+				*, posting_datetime as "timestamp"
 			from
 				`tabStock Ledger Entry`
 			where
@@ -629,8 +633,7 @@
 				and warehouse = %(warehouse)s
 				and is_cancelled = 0
 				and (
-					posting_date = %(posting_date)s and
-					time_format(posting_time, %(time_format)s) = time_format(%(posting_time)s, %(time_format)s)
+					posting_datetime = %(posting_datetime)s
 				)
 			order by
 				creation ASC
@@ -899,7 +902,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
 			)
@@ -1399,11 +1402,11 @@
 def get_previous_sle_of_current_voucher(args, operator="<", exclude_current_voucher=False):
 	"""get stock ledger entries filtered by specific posting datetime conditions"""
 
-	args["time_format"] = "%H:%i:%s"
 	if not args.get("posting_date"):
-		args["posting_date"] = "1900-01-01"
-	if not args.get("posting_time"):
-		args["posting_time"] = "00:00"
+		args["posting_datetime"] = "1900-01-01 00:00:00"
+
+	if not args.get("posting_datetime"):
+		args["posting_datetime"] = get_combine_datetime(args["posting_date"], args["posting_time"])
 
 	voucher_condition = ""
 	if exclude_current_voucher:
@@ -1412,23 +1415,20 @@
 
 	sle = frappe.db.sql(
 		"""
-		select *, timestamp(posting_date, posting_time) as "timestamp"
+		select *, posting_datetime as "timestamp"
 		from `tabStock Ledger Entry`
 		where item_code = %(item_code)s
 			and warehouse = %(warehouse)s
 			and is_cancelled = 0
 			{voucher_condition}
 			and (
-				posting_date < %(posting_date)s or
-				(
-					posting_date = %(posting_date)s and
-					time_format(posting_time, %(time_format)s) {operator} time_format(%(posting_time)s, %(time_format)s)
-				)
+				posting_datetime {operator} %(posting_datetime)s
 			)
-		order by timestamp(posting_date, posting_time) desc, creation desc
+		order by posting_datetime desc, creation desc
 		limit 1
 		for update""".format(
-			operator=operator, voucher_condition=voucher_condition
+			operator=operator,
+			voucher_condition=voucher_condition,
 		),
 		args,
 		as_dict=1,
@@ -1469,9 +1469,7 @@
 	extra_cond=None,
 ):
 	"""get stock ledger entries filtered by specific posting datetime conditions"""
-	conditions = " and timestamp(posting_date, posting_time) {0} timestamp(%(posting_date)s, %(posting_time)s)".format(
-		operator
-	)
+	conditions = " and posting_datetime {0} %(posting_datetime)s".format(operator)
 	if previous_sle.get("warehouse"):
 		conditions += " and warehouse = %(warehouse)s"
 	elif previous_sle.get("warehouse_condition"):
@@ -1497,9 +1495,11 @@
 		)
 
 	if not previous_sle.get("posting_date"):
-		previous_sle["posting_date"] = "1900-01-01"
-	if not previous_sle.get("posting_time"):
-		previous_sle["posting_time"] = "00:00"
+		previous_sle["posting_datetime"] = "1900-01-01 00:00:00"
+	else:
+		previous_sle["posting_datetime"] = get_combine_datetime(
+			previous_sle["posting_date"], previous_sle["posting_time"]
+		)
 
 	if operator in (">", "<=") and previous_sle.get("name"):
 		conditions += " and name!=%(name)s"
@@ -1509,12 +1509,12 @@
 
 	return frappe.db.sql(
 		"""
-		select *, timestamp(posting_date, posting_time) as "timestamp"
+		select *, posting_datetime as "timestamp"
 		from `tabStock Ledger Entry`
 		where item_code = %%(item_code)s
 		and is_cancelled = 0
 		%(conditions)s
-		order by timestamp(posting_date, posting_time) %(order)s, creation %(order)s
+		order by posting_datetime %(order)s, creation %(order)s
 		%(limit)s %(for_update)s"""
 		% {
 			"conditions": conditions,
@@ -1540,7 +1540,7 @@
 			"posting_date",
 			"posting_time",
 			"voucher_detail_no",
-			"timestamp(posting_date, posting_time) as timestamp",
+			"posting_datetime as timestamp",
 		],
 		as_dict=1,
 	)
@@ -1552,13 +1552,10 @@
 
 	sle = frappe.qb.DocType("Stock Ledger Entry")
 
-	timestamp_condition = CombineDatetime(sle.posting_date, sle.posting_time) < CombineDatetime(
-		posting_date, posting_time
-	)
+	timestamp_condition = sle.posting_datetime < get_combine_datetime(posting_date, posting_time)
 	if creation:
 		timestamp_condition |= (
-			CombineDatetime(sle.posting_date, sle.posting_time)
-			== CombineDatetime(posting_date, posting_time)
+			sle.posting_datetime == get_combine_datetime(posting_date, posting_time)
 		) & (sle.creation < creation)
 
 	batch_details = (
@@ -1639,7 +1636,7 @@
 			AND valuation_rate >= 0
 			AND is_cancelled = 0
 			AND NOT (voucher_no = %s AND voucher_type = %s)
-		order by posting_date desc, posting_time desc, name desc limit 1""",
+		order by posting_datetime desc, name desc limit 1""",
 		(item_code, warehouse, voucher_no, voucher_type),
 	):
 		return flt(last_valuation_rate[0][0])
@@ -1698,7 +1695,7 @@
 	datetime_limit_condition = ""
 	qty_shift = args.actual_qty
 
-	args["time_format"] = "%H:%i:%s"
+	args["posting_datetime"] = get_combine_datetime(args["posting_date"], args["posting_time"])
 
 	# find difference/shift in qty caused by stock reconciliation
 	if args.voucher_type == "Stock Reconciliation":
@@ -1708,8 +1705,6 @@
 	next_stock_reco_detail = get_next_stock_reco(args)
 	if next_stock_reco_detail:
 		detail = next_stock_reco_detail[0]
-
-		# add condition to update SLEs before this date & time
 		datetime_limit_condition = get_datetime_limit_condition(detail)
 
 	frappe.db.sql(
@@ -1722,13 +1717,9 @@
 			and voucher_no != %(voucher_no)s
 			and is_cancelled = 0
 			and (
-				posting_date > %(posting_date)s or
-				(
-					posting_date = %(posting_date)s and
-					time_format(posting_time, %(time_format)s) > time_format(%(posting_time)s, %(time_format)s)
-				)
+				posting_datetime > %(posting_datetime)s
 			)
-		{datetime_limit_condition}
+			{datetime_limit_condition}
 		""",
 		args,
 	)
@@ -1785,20 +1776,11 @@
 			& (sle.voucher_no != kwargs.get("voucher_no"))
 			& (sle.is_cancelled == 0)
 			& (
-				(
-					CombineDatetime(sle.posting_date, sle.posting_time)
-					> CombineDatetime(kwargs.get("posting_date"), kwargs.get("posting_time"))
-				)
-				| (
-					(
-						CombineDatetime(sle.posting_date, sle.posting_time)
-						== CombineDatetime(kwargs.get("posting_date"), kwargs.get("posting_time"))
-					)
-					& (sle.creation > kwargs.get("creation"))
-				)
+				sle.posting_datetime
+				>= get_combine_datetime(kwargs.get("posting_date"), kwargs.get("posting_time"))
 			)
 		)
-		.orderby(CombineDatetime(sle.posting_date, sle.posting_time))
+		.orderby(sle.posting_datetime)
 		.orderby(sle.creation)
 		.limit(1)
 	)
@@ -1810,11 +1792,13 @@
 
 
 def get_datetime_limit_condition(detail):
+	posting_datetime = get_combine_datetime(detail.posting_date, detail.posting_time)
+
 	return f"""
 		and
-		(timestamp(posting_date, posting_time) < timestamp('{detail.posting_date}', '{detail.posting_time}')
+		(posting_datetime < '{posting_datetime}'
 			or (
-				timestamp(posting_date, posting_time) = timestamp('{detail.posting_date}', '{detail.posting_time}')
+				posting_datetime = '{posting_datetime}'
 				and creation < '{detail.creation}'
 			)
 		)"""
@@ -1888,10 +1872,10 @@
 			item_code = %(item_code)s
 			and warehouse = %(warehouse)s
 			and voucher_no != %(voucher_no)s
-			and timestamp(posting_date, posting_time) >= timestamp(%(posting_date)s, %(posting_time)s)
+			and posting_datetime >= %(posting_datetime)s
 			and is_cancelled = 0
 			and qty_after_transaction < 0
-		order by timestamp(posting_date, posting_time) asc
+		order by posting_datetime asc
 		limit 1
 	""",
 		args,
@@ -1904,20 +1888,20 @@
 		"""
 		with batch_ledger as (
 			select
-				posting_date, posting_time, voucher_type, voucher_no,
-				sum(actual_qty) over (order by posting_date, posting_time, creation) as cumulative_total
+				posting_date, posting_time, posting_datetime, voucher_type, voucher_no,
+				sum(actual_qty) over (order by posting_datetime, creation) as cumulative_total
 			from `tabStock Ledger Entry`
 			where
 				item_code = %(item_code)s
 				and warehouse = %(warehouse)s
 				and batch_no=%(batch_no)s
 				and is_cancelled = 0
-			order by posting_date, posting_time, creation
+			order by posting_datetime, creation
 		)
 		select * from batch_ledger
 		where
 			cumulative_total < 0.0
-			and timestamp(posting_date, posting_time) >= timestamp(%(posting_date)s, %(posting_time)s)
+			and posting_datetime >= %(posting_datetime)s
 		limit 1
 	""",
 		args,
@@ -2059,6 +2043,7 @@
 
 def get_stock_value_difference(item_code, warehouse, posting_date, posting_time, voucher_no=None):
 	table = frappe.qb.DocType("Stock Ledger Entry")
+	posting_datetime = get_combine_datetime(posting_date, posting_time)
 
 	query = (
 		frappe.qb.from_(table)
@@ -2067,10 +2052,7 @@
 			(table.is_cancelled == 0)
 			& (table.item_code == item_code)
 			& (table.warehouse == warehouse)
-			& (
-				(table.posting_date < posting_date)
-				| ((table.posting_date == posting_date) & (table.posting_time <= posting_time))
-			)
+			& (table.posting_datetime <= posting_datetime)
 		)
 	)
 
diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py
index 9eac172..93e2fa4 100644
--- a/erpnext/stock/utils.py
+++ b/erpnext/stock/utils.py
@@ -8,7 +8,7 @@
 import frappe
 from frappe import _
 from frappe.query_builder.functions import CombineDatetime, IfNull, Sum
-from frappe.utils import cstr, flt, get_link_to_form, nowdate, nowtime
+from frappe.utils import cstr, flt, get_link_to_form, get_time, getdate, nowdate, nowtime
 
 import erpnext
 from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import (
@@ -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)
@@ -647,3 +657,18 @@
 		):
 			scan_result.update(item_info)
 	return scan_result
+
+
+def get_combine_datetime(posting_date, posting_time):
+	import datetime
+
+	if isinstance(posting_date, str):
+		posting_date = getdate(posting_date)
+
+	if isinstance(posting_time, str):
+		posting_time = get_time(posting_time)
+
+	if isinstance(posting_time, datetime.timedelta):
+		posting_time = (datetime.datetime.min + posting_time).time()
+
+	return datetime.datetime.combine(posting_date, posting_time).replace(microsecond=0)
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py
index 5523c31..0450038 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py
@@ -643,10 +643,6 @@
 		)
 		scr = make_subcontracting_receipt(sco.name)
 		scr.save()
-		for row in scr.supplied_items:
-			self.assertNotEqual(row.rate, 300.00)
-			self.assertFalse(row.serial_and_batch_bundle)
-
 		scr.submit()
 		scr.reload()
 
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()