Merge pull request #40035 from GursheenK/delete-ple-on-deletion-of-transactions

fix: delete PLE containing invoice in against
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index abc0694..25477cc 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -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/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/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/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 19697b2..32a5a61 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -216,7 +216,8 @@
 					)
 				)
 
-			if self.get("is_return") and self.get("return_against"):
+			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(
 					_(
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/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/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 a259540..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
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/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/transaction.js b/erpnext/public/js/controllers/transaction.js
index 2c985a0..5d24af5 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -942,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");
+				}
+			);
 		}
 	}
 
@@ -1509,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) {
diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js
index 1b40f2b..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");
 				}
 			}
 		});
@@ -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/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index d5bc14b..daa0166 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -2317,6 +2317,95 @@
 			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",
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 88b262a..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
@@ -5,7 +5,7 @@
 
 import frappe
 from frappe.tests.utils import FrappeTestCase, change_settings
-from frappe.utils import add_days, add_to_date, flt, nowdate, nowtime, today
+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(
 			{
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 9f1e523..832894b 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -1902,6 +1902,7 @@
 			return
 
 		id = create_serial_and_batch_bundle(
+			self,
 			row,
 			frappe._dict(
 				{
@@ -2172,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,
@@ -2550,6 +2551,7 @@
 					row = frappe._dict({"serial_nos": serial_nos[0 : cint(d.qty)]})
 
 					id = create_serial_and_batch_bundle(
+						self,
 						row,
 						frappe._dict(
 							{
@@ -3073,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
 	)
@@ -3091,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/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index 99c050a..9d1a3f7 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -1611,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(
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 c69b20b..06fd5f9 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -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.py b/erpnext/stock/report/stock_ledger/stock_ledger.py
index 5076435..d859f4e 100644
--- a/erpnext/stock/report/stock_ledger/stock_ledger.py
+++ b/erpnext/stock/report/stock_ledger/stock_ledger.py
@@ -345,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,
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index d0815c9..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
@@ -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 00f030e..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 (
@@ -657,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()