Merge pull request #40208 from barredterra/refactor-semgrep

refactor: reduce usage of `cur_frm`
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.json b/erpnext/accounts/doctype/gl_entry/gl_entry.json
index c071193..991a08b 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.json
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.json
@@ -179,7 +179,8 @@
    "fieldname": "voucher_detail_no",
    "fieldtype": "Data",
    "label": "Voucher Detail No",
-   "read_only": 1
+   "read_only": 1,
+   "search_index": 1
   },
   {
    "fieldname": "project",
@@ -290,7 +291,7 @@
  "idx": 1,
  "in_create": 1,
  "links": [],
- "modified": "2023-09-26 12:03:23.031733",
+ "modified": "2024-03-19 18:43:42.235373",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "GL Entry",
@@ -325,4 +326,4 @@
  "sort_field": "modified",
  "sort_order": "DESC",
  "states": []
-}
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py
index 3fa8710..a6f6d4e 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py
@@ -32,8 +32,6 @@
 		account: DF.Link | None
 		account_currency: DF.Link | None
 		against: DF.Text | None
-		against_link: DF.DynamicLink | None
-		against_type: DF.Link | None
 		against_voucher: DF.DynamicLink | None
 		against_voucher_type: DF.Link | None
 		company: DF.Link | None
diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py
index 2a84d97..76c0a09 100644
--- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py
+++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py
@@ -141,7 +141,8 @@
 		previous_fiscal_year = get_fiscal_year(last_year_closing, company=self.company, boolean=True)
 
 		if previous_fiscal_year and not frappe.db.exists(
-			"GL Entry", {"posting_date": ("<=", last_year_closing), "company": self.company}
+			"GL Entry",
+			{"posting_date": ("<=", last_year_closing), "company": self.company, "is_cancelled": 0},
 		):
 			return
 
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
index 955b66a..d7b1736 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
@@ -775,7 +775,7 @@
   },
   {
    "fieldname": "other_charges_calculation",
-   "fieldtype": "Long Text",
+   "fieldtype": "Text Editor",
    "label": "Taxes and Charges Calculation",
    "no_copy": 1,
    "oldfieldtype": "HTML",
@@ -1563,7 +1563,7 @@
  "icon": "fa fa-file-text",
  "is_submittable": 1,
  "links": [],
- "modified": "2023-11-20 12:27:12.848149",
+ "modified": "2024-03-20 16:00:34.268756",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "POS Invoice",
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
index 1a7eef6..5db3da1 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
@@ -109,7 +109,7 @@
 		loyalty_redemption_cost_center: DF.Link | None
 		naming_series: DF.Literal["ACC-PSINV-.YYYY.-"]
 		net_total: DF.Currency
-		other_charges_calculation: DF.LongText | None
+		other_charges_calculation: DF.TextEditor | None
 		outstanding_amount: DF.Currency
 		packed_items: DF.Table[PackedItem]
 		paid_amount: DF.Currency
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index 22f2d13..44d3d48 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -760,7 +760,7 @@
   },
   {
    "fieldname": "other_charges_calculation",
-   "fieldtype": "Long Text",
+   "fieldtype": "Text Editor",
    "label": "Taxes and Charges Calculation",
    "no_copy": 1,
    "oldfieldtype": "HTML",
@@ -1638,7 +1638,7 @@
  "idx": 204,
  "is_submittable": 1,
  "links": [],
- "modified": "2024-03-11 14:46:30.298184",
+ "modified": "2024-03-20 15:57:00.736868",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Purchase Invoice",
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 75f0d08..19b7092 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -147,7 +147,7 @@
 		net_total: DF.Currency
 		on_hold: DF.Check
 		only_include_allocated_payments: DF.Check
-		other_charges_calculation: DF.LongText | None
+		other_charges_calculation: DF.TextEditor | None
 		outstanding_amount: DF.Currency
 		paid_amount: DF.Currency
 		party_account_currency: DF.Link | None
@@ -904,7 +904,6 @@
 				"Company", self.company, "enable_provisional_accounting_for_non_stock_items"
 			)
 		)
-		self.provisional_enpenses_booked_in_pr = False
 		if provisional_accounting_for_non_stock_items:
 			self.get_provisional_accounts()
 
@@ -1149,37 +1148,36 @@
 			fields=["name", "provisional_expense_account", "qty", "base_rate"],
 		)
 		default_provisional_account = self.get_company_default("default_provisional_account")
+		provisional_accounts = set(
+			[
+				d.provisional_expense_account if d.provisional_expense_account else default_provisional_account
+				for d in pr_items
+			]
+		)
+
+		provisional_gl_entries = frappe.get_all(
+			"GL Entry",
+			filters={
+				"voucher_type": "Purchase Receipt",
+				"voucher_no": ("in", linked_purchase_receipts),
+				"account": ("in", provisional_accounts),
+				"is_cancelled": 0,
+			},
+			fields=["voucher_detail_no"],
+		)
+		rows_with_provisional_entries = [d.voucher_detail_no for d in provisional_gl_entries]
 		for item in pr_items:
 			self.provisional_accounts[item.name] = {
 				"provisional_account": item.provisional_expense_account or default_provisional_account,
 				"qty": item.qty,
 				"base_rate": item.base_rate,
+				"has_provisional_entry": item.name in rows_with_provisional_entries,
 			}
 
 	def make_provisional_gl_entry(self, gl_entries, item):
 		if item.purchase_receipt:
 			pr_item = self.provisional_accounts.get(item.pr_detail, {})
-			provisional_account = pr_item.get("provisional_account")
-			pr_qty = pr_item.get("qty")
-			pr_base_rate = pr_item.get("base_rate")
-
-			if not self.provisional_enpenses_booked_in_pr:
-				# Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt
-				provision_gle_against_pr = frappe.db.get_value(
-					"GL Entry",
-					{
-						"is_cancelled": 0,
-						"voucher_type": "Purchase Receipt",
-						"voucher_no": item.purchase_receipt,
-						"voucher_detail_no": item.pr_detail,
-						"account": provisional_account,
-					},
-					["name"],
-				)
-				if provision_gle_against_pr:
-					self.provisional_enpenses_booked_in_pr = True
-
-			if self.provisional_enpenses_booked_in_pr:
+			if pr_item.get("has_provisional_entry"):
 				purchase_receipt_doc = frappe.get_cached_doc("Purchase Receipt", item.purchase_receipt)
 
 				# Intentionally passing purchase invoice item to handle partial billing
@@ -1187,9 +1185,9 @@
 					item,
 					gl_entries,
 					self.posting_date,
-					provisional_account,
+					pr_item.get("provisional_account"),
 					reverse=1,
-					item_amount=(min(item.qty, pr_qty) * pr_base_rate),
+					item_amount=(min(item.qty, pr_item.get("qty")) * pr_item.get("base_rate")),
 				)
 
 	def update_gross_purchase_amount_for_linked_assets(self, item):
diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
index 3ee4214..66df76a 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
@@ -745,6 +745,7 @@
    "fieldtype": "Currency",
    "label": "Landed Cost Voucher Amount",
    "no_copy": 1,
+   "options": "Company:company:default_currency",
    "print_hide": 1,
    "read_only": 1
   },
@@ -938,7 +939,7 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2024-02-04 14:11:52.742228",
+ "modified": "2024-03-19 19:09:47.210965",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Purchase Invoice Item",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index 37b2752..1d8983e 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -946,7 +946,7 @@
   },
   {
    "fieldname": "other_charges_calculation",
-   "fieldtype": "Long Text",
+   "fieldtype": "Text Editor",
    "hide_days": 1,
    "hide_seconds": 1,
    "label": "Taxes and Charges Calculation",
@@ -2193,7 +2193,7 @@
    "link_fieldname": "consolidated_invoice"
   }
  ],
- "modified": "2024-03-15 16:44:17.778370",
+ "modified": "2024-03-20 16:02:52.237732",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Sales Invoice",
@@ -2248,4 +2248,4 @@
  "title_field": "title",
  "track_changes": 1,
  "track_seen": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index bf50e77..1228bbb 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -146,7 +146,7 @@
 		naming_series: DF.Literal["ACC-SINV-.YYYY.-", "ACC-SINV-RET-.YYYY.-"]
 		net_total: DF.Currency
 		only_include_allocated_payments: DF.Check
-		other_charges_calculation: DF.LongText | None
+		other_charges_calculation: DF.TextEditor | None
 		outstanding_amount: DF.Currency
 		packed_items: DF.Table[PackedItem]
 		paid_amount: DF.Currency
diff --git a/erpnext/accounts/doctype/transaction_deletion_record_details/__init__.py b/erpnext/accounts/doctype/transaction_deletion_record_details/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/accounts/doctype/transaction_deletion_record_details/__init__.py
diff --git a/erpnext/accounts/doctype/transaction_deletion_record_details/transaction_deletion_record_details.json b/erpnext/accounts/doctype/transaction_deletion_record_details/transaction_deletion_record_details.json
new file mode 100644
index 0000000..fe4b085
--- /dev/null
+++ b/erpnext/accounts/doctype/transaction_deletion_record_details/transaction_deletion_record_details.json
@@ -0,0 +1,58 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "creation": "2024-02-04 10:53:32.307930",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "doctype_name",
+  "docfield_name",
+  "no_of_docs",
+  "done"
+ ],
+ "fields": [
+  {
+   "fieldname": "doctype_name",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "DocType",
+   "options": "DocType",
+   "read_only": 1,
+   "reqd": 1
+  },
+  {
+   "fieldname": "docfield_name",
+   "fieldtype": "Data",
+   "label": "DocField",
+   "read_only": 1
+  },
+  {
+   "fieldname": "no_of_docs",
+   "fieldtype": "Int",
+   "in_list_view": 1,
+   "label": "No of Docs",
+   "read_only": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "done",
+   "fieldtype": "Check",
+   "in_list_view": 1,
+   "label": "Done",
+   "read_only": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2024-02-05 17:35:09.556054",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Transaction Deletion Record Details",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": []
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/transaction_deletion_record_details/transaction_deletion_record_details.py b/erpnext/accounts/doctype/transaction_deletion_record_details/transaction_deletion_record_details.py
new file mode 100644
index 0000000..bc5b5c4
--- /dev/null
+++ b/erpnext/accounts/doctype/transaction_deletion_record_details/transaction_deletion_record_details.py
@@ -0,0 +1,26 @@
+# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class TransactionDeletionRecordDetails(Document):
+	# begin: auto-generated types
+	# This code is auto-generated. Do not modify anything in this block.
+
+	from typing import TYPE_CHECKING
+
+	if TYPE_CHECKING:
+		from frappe.types import DF
+
+		docfield_name: DF.Data | None
+		doctype_name: DF.Link
+		done: DF.Check
+		no_of_docs: DF.Int
+		parent: DF.Data
+		parentfield: DF.Data
+		parenttype: DF.Data
+	# end: auto-generated types
+
+	pass
diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py
index 7162aef..25958692 100644
--- a/erpnext/accounts/report/gross_profit/gross_profit.py
+++ b/erpnext/accounts/report/gross_profit/gross_profit.py
@@ -669,20 +669,20 @@
 			elif row.sales_order and row.so_detail:
 				incoming_amount = self.get_buying_amount_from_so_dn(row.sales_order, row.so_detail, item_code)
 				if incoming_amount:
-					return incoming_amount
+					return flt(row.qty) * incoming_amount
 			else:
 				return flt(row.qty) * self.get_average_buying_rate(row, item_code)
 
 		return flt(row.qty) * self.get_average_buying_rate(row, item_code)
 
 	def get_buying_amount_from_so_dn(self, sales_order, so_detail, item_code):
-		from frappe.query_builder.functions import Sum
+		from frappe.query_builder.functions import Avg
 
 		delivery_note_item = frappe.qb.DocType("Delivery Note Item")
 
 		query = (
 			frappe.qb.from_(delivery_note_item)
-			.select(Sum(delivery_note_item.incoming_rate * delivery_note_item.stock_qty))
+			.select(Avg(delivery_note_item.incoming_rate))
 			.where(delivery_note_item.docstatus == 1)
 			.where(delivery_note_item.item_code == item_code)
 			.where(delivery_note_item.against_sales_order == sales_order)
diff --git a/erpnext/accounts/report/gross_profit/test_gross_profit.py b/erpnext/accounts/report/gross_profit/test_gross_profit.py
index 82fe1a0..aa820aa 100644
--- a/erpnext/accounts/report/gross_profit/test_gross_profit.py
+++ b/erpnext/accounts/report/gross_profit/test_gross_profit.py
@@ -460,3 +460,95 @@
 		}
 		gp_entry = [x for x in data if x.parent_invoice == sinv.name]
 		self.assertDictContainsSubset(expected_entry, gp_entry[0])
+
+	def test_different_rates_in_si_and_dn(self):
+		from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
+
+		"""
+			Test gp calculation when invoice and delivery note differ in qty and aren't connected
+			SO -- INV
+			|
+			DN
+		"""
+		se = make_stock_entry(
+			company=self.company,
+			item_code=self.item,
+			target=self.warehouse,
+			qty=3,
+			basic_rate=700,
+			do_not_submit=True,
+		)
+		item = se.items[0]
+		se.append(
+			"items",
+			{
+				"item_code": item.item_code,
+				"s_warehouse": item.s_warehouse,
+				"t_warehouse": item.t_warehouse,
+				"qty": 10,
+				"basic_rate": 700,
+				"conversion_factor": item.conversion_factor or 1.0,
+				"transfer_qty": flt(item.qty) * (flt(item.conversion_factor) or 1.0),
+				"serial_no": item.serial_no,
+				"batch_no": item.batch_no,
+				"cost_center": item.cost_center,
+				"expense_account": item.expense_account,
+			},
+		)
+		se = se.save().submit()
+
+		so = make_sales_order(
+			customer=self.customer,
+			company=self.company,
+			warehouse=self.warehouse,
+			item=self.item,
+			rate=800,
+			qty=10,
+			do_not_save=False,
+			do_not_submit=False,
+		)
+
+		from erpnext.selling.doctype.sales_order.sales_order import (
+			make_delivery_note,
+			make_sales_invoice,
+		)
+
+		dn1 = make_delivery_note(so.name)
+		dn1.items[0].qty = 4
+		dn1.items[0].rate = 800
+		dn1.save().submit()
+
+		dn2 = make_delivery_note(so.name)
+		dn2.items[0].qty = 6
+		dn2.items[0].rate = 800
+		dn2.save().submit()
+
+		sinv = make_sales_invoice(so.name)
+		sinv.items[0].qty = 4
+		sinv.items[0].rate = 800
+		sinv.save().submit()
+
+		filters = frappe._dict(
+			company=self.company, from_date=nowdate(), to_date=nowdate(), group_by="Invoice"
+		)
+
+		columns, data = execute(filters=filters)
+		expected_entry = {
+			"parent_invoice": sinv.name,
+			"currency": "INR",
+			"sales_invoice": self.item,
+			"customer": self.customer,
+			"posting_date": frappe.utils.datetime.date.fromisoformat(nowdate()),
+			"item_code": self.item,
+			"item_name": self.item,
+			"warehouse": "Stores - _GP",
+			"qty": 4.0,
+			"avg._selling_rate": 800.0,
+			"valuation_rate": 700.0,
+			"selling_amount": 3200.0,
+			"buying_amount": 2800.0,
+			"gross_profit": 400.0,
+			"gross_profit_%": 12.5,
+		}
+		gp_entry = [x for x in data if x.parent_invoice == sinv.name]
+		self.assertDictContainsSubset(expected_entry, gp_entry[0])
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json
index 9da49a7..0916ff5 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.json
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.json
@@ -642,7 +642,7 @@
   },
   {
    "fieldname": "other_charges_calculation",
-   "fieldtype": "Long Text",
+   "fieldtype": "Text Editor",
    "label": "Taxes and Charges Calculation",
    "no_copy": 1,
    "oldfieldtype": "HTML",
@@ -1288,7 +1288,7 @@
  "idx": 105,
  "is_submittable": 1,
  "links": [],
- "modified": "2023-10-10 13:37:40.158761",
+ "modified": "2024-03-20 16:03:31.611808",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Purchase Order",
@@ -1343,4 +1343,4 @@
  "timeline_field": "supplier",
  "title_field": "supplier_name",
  "track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index 4d94868..4f24ec2 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -57,6 +57,7 @@
 		additional_discount_percentage: DF.Float
 		address_display: DF.SmallText | None
 		advance_paid: DF.Currency
+		advance_payment_status: DF.Literal["Not Initiated", "Initiated", "Partially Paid", "Fully Paid"]
 		amended_from: DF.Link | None
 		apply_discount_on: DF.Literal["", "Grand Total", "Net Total"]
 		apply_tds: DF.Check
@@ -109,7 +110,7 @@
 		net_total: DF.Currency
 		order_confirmation_date: DF.Date | None
 		order_confirmation_no: DF.Data | None
-		other_charges_calculation: DF.LongText | None
+		other_charges_calculation: DF.TextEditor | None
 		party_account_currency: DF.Link | None
 		payment_schedule: DF.Table[PaymentSchedule]
 		payment_terms_template: DF.Link | None
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
index 1891261..09be247 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
@@ -462,7 +462,7 @@
   },
   {
    "fieldname": "other_charges_calculation",
-   "fieldtype": "Long Text",
+   "fieldtype": "Markdown Editor",
    "label": "Taxes and Charges Calculation",
    "no_copy": 1,
    "oldfieldtype": "HTML",
@@ -928,7 +928,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2023-11-17 12:34:30.083077",
+ "modified": "2024-03-20 16:03:59.069145",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Supplier Quotation",
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
index e2b737b..b716f7f 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
@@ -71,7 +71,7 @@
 		naming_series: DF.Literal["PUR-SQTN-.YYYY.-"]
 		net_total: DF.Currency
 		opportunity: DF.Link | None
-		other_charges_calculation: DF.LongText | None
+		other_charges_calculation: DF.MarkdownEditor | None
 		plc_conversion_rate: DF.Float
 		price_list_currency: DF.Link | None
 		pricing_rules: DF.Table[PricingRuleDetail]
diff --git a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js
index c109abd..f7d0d94 100644
--- a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js
+++ b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js
@@ -77,7 +77,10 @@
 			fieldname: "group_by",
 			label: __("Group by"),
 			fieldtype: "Select",
-			options: [__("Group by Supplier"), __("Group by Item")],
+			options: [
+				{ label: __("Group by Supplier"), value: "Group by Supplier" },
+				{ label: __("Group by Item"), value: "Group by Item" },
+			],
 			default: __("Group by Supplier"),
 		},
 		{
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 9b996fe..0d70476 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -306,7 +306,10 @@
 
 doc_events = {
 	"*": {
-		"validate": "erpnext.support.doctype.service_level_agreement.service_level_agreement.apply",
+		"validate": [
+			"erpnext.support.doctype.service_level_agreement.service_level_agreement.apply",
+			"erpnext.setup.doctype.transaction_deletion_record.transaction_deletion_record.check_for_running_deletion_job",
+		],
 	},
 	tuple(period_closing_doctypes): {
 		"validate": "erpnext.accounts.doctype.accounting_period.accounting_period.validate_accounting_period_on_doc_save",
diff --git a/erpnext/public/js/bom_configurator/bom_configurator.bundle.js b/erpnext/public/js/bom_configurator/bom_configurator.bundle.js
index 454a2a4..5061be9 100644
--- a/erpnext/public/js/bom_configurator/bom_configurator.bundle.js
+++ b/erpnext/public/js/bom_configurator/bom_configurator.bundle.js
@@ -107,7 +107,7 @@
 				this.frm?.doc.docstatus === 0
 					? [
 							{
-								label: __(frappe.utils.icon("edit", "sm") + " Qty"),
+								label: `${frappe.utils.icon("edit", "sm")} ${__("Qty")}`,
 								click: function (node) {
 									let view = frappe.views.trees["BOM Configurator"];
 									view.events.edit_qty(node, view);
@@ -115,7 +115,7 @@
 								btnClass: "hidden-xs",
 							},
 							{
-								label: __(frappe.utils.icon("add", "sm") + " Raw Material"),
+								label: `${frappe.utils.icon("add", "sm")} ${__("Raw Material")}`,
 								click: function (node) {
 									let view = frappe.views.trees["BOM Configurator"];
 									view.events.add_item(node, view);
@@ -126,7 +126,7 @@
 								btnClass: "hidden-xs",
 							},
 							{
-								label: __(frappe.utils.icon("add", "sm") + " Sub Assembly"),
+								label: `${frappe.utils.icon("add", "sm")} ${__("Sub Assembly")}`,
 								click: function (node) {
 									let view = frappe.views.trees["BOM Configurator"];
 									view.events.add_sub_assembly(node, view);
@@ -156,7 +156,7 @@
 								btnClass: "hidden-xs expand-all-btn",
 							},
 							{
-								label: __(frappe.utils.icon("move", "sm") + " Sub Assembly"),
+								label: `${frappe.utils.icon("move", "sm")} ${__("Sub Assembly")}`,
 								click: function (node) {
 									let view = frappe.views.trees["BOM Configurator"];
 									view.events.convert_to_sub_assembly(node, view);
@@ -167,7 +167,7 @@
 								btnClass: "hidden-xs",
 							},
 							{
-								label: __(frappe.utils.icon("delete", "sm") + __(" Item")),
+								label: `${frappe.utils.icon("delete", "sm")} ${__("Item")}`,
 								click: function (node) {
 									let view = frappe.views.trees["BOM Configurator"];
 									view.events.delete_node(node, view);
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index c756d53..63f39b5 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -1276,8 +1276,11 @@
 
 	calculate_stock_uom_rate(doc, cdt, cdn) {
 		let item = frappe.get_doc(cdt, cdn);
-		item.stock_uom_rate = flt(item.rate)/flt(item.conversion_factor);
-		refresh_field("stock_uom_rate", item.name, item.parentfield);
+
+		if (item?.rate) {
+			item.stock_uom_rate = flt(item.rate) / flt(item.conversion_factor);
+			refresh_field("stock_uom_rate", item.name, item.parentfield);
+		}
 	}
 	service_stop_date(frm, cdt, cdn) {
 		var child = locals[cdt][cdn];
diff --git a/erpnext/public/scss/erpnext.scss b/erpnext/public/scss/erpnext.scss
index e45ae50..03dd311 100644
--- a/erpnext/public/scss/erpnext.scss
+++ b/erpnext/public/scss/erpnext.scss
@@ -548,3 +548,7 @@
 	align-items: center;
 	justify-content: center;
 }
+
+.frappe-control[data-fieldname="other_charges_calculation"] .ql-editor {
+	white-space: normal;
+}
diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json
index 8c816cf..982e732 100644
--- a/erpnext/selling/doctype/quotation/quotation.json
+++ b/erpnext/selling/doctype/quotation/quotation.json
@@ -557,7 +557,7 @@
   },
   {
    "fieldname": "other_charges_calculation",
-   "fieldtype": "Long Text",
+   "fieldtype": "Text Editor",
    "label": "Taxes and Charges Calculation",
    "no_copy": 1,
    "oldfieldtype": "HTML",
@@ -1073,7 +1073,7 @@
  "idx": 82,
  "is_submittable": 1,
  "links": [],
- "modified": "2023-06-03 16:21:04.980033",
+ "modified": "2024-03-20 16:04:21.567847",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Quotation",
diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py
index 13d17d6..633e5f5 100644
--- a/erpnext/selling/doctype/quotation/quotation.py
+++ b/erpnext/selling/doctype/quotation/quotation.py
@@ -78,7 +78,7 @@
 		opportunity: DF.Link | None
 		order_lost_reason: DF.SmallText | None
 		order_type: DF.Literal["", "Sales", "Maintenance", "Shopping Cart"]
-		other_charges_calculation: DF.LongText | None
+		other_charges_calculation: DF.TextEditor | None
 		packed_items: DF.Table[PackedItem]
 		party_name: DF.DynamicLink | None
 		payment_schedule: DF.Table[PaymentSchedule]
diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json
index 3c516d0..1fb1ae0 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.json
+++ b/erpnext/selling/doctype/sales_order/sales_order.json
@@ -777,7 +777,7 @@
   },
   {
    "fieldname": "other_charges_calculation",
-   "fieldtype": "Long Text",
+   "fieldtype": "Text Editor",
    "hide_days": 1,
    "hide_seconds": 1,
    "label": "Taxes and Charges Calculation",
@@ -1657,7 +1657,7 @@
  "idx": 105,
  "is_submittable": 1,
  "links": [],
- "modified": "2023-10-18 12:41:54.813462",
+ "modified": "2024-03-20 16:04:43.627183",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Sales Order",
@@ -1735,4 +1735,4 @@
  "title_field": "customer_name",
  "track_changes": 1,
  "track_seen": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index 4956f29..79df4d3 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -66,6 +66,7 @@
 		additional_discount_percentage: DF.Float
 		address_display: DF.SmallText | None
 		advance_paid: DF.Currency
+		advance_payment_status: DF.Literal["Not Requested", "Requested", "Partially Paid", "Fully Paid"]
 		amended_from: DF.Link | None
 		amount_eligible_for_commission: DF.Currency
 		apply_discount_on: DF.Literal["", "Grand Total", "Net Total"]
@@ -122,7 +123,7 @@
 		naming_series: DF.Literal["SAL-ORD-.YYYY.-"]
 		net_total: DF.Currency
 		order_type: DF.Literal["", "Sales", "Maintenance", "Shopping Cart"]
-		other_charges_calculation: DF.LongText | None
+		other_charges_calculation: DF.TextEditor | None
 		packed_items: DF.Table[PackedItem]
 		party_account_currency: DF.Link | None
 		payment_schedule: DF.Table[PaymentSchedule]
@@ -155,6 +156,7 @@
 			"",
 			"Draft",
 			"On Hold",
+			"To Pay",
 			"To Deliver and Bill",
 			"To Bill",
 			"To Deliver",
diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
index 9599980..d451768 100644
--- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json
+++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
@@ -834,7 +834,8 @@
    "label": "Purchase Order",
    "options": "Purchase Order",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "search_index": 1
   },
   {
    "fieldname": "column_break_89",
@@ -909,7 +910,7 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2024-01-25 14:24:00.330219",
+ "modified": "2024-03-21 18:15:56.625005",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Sales Order Item",
diff --git a/erpnext/setup/demo.py b/erpnext/setup/demo.py
index 688d45a..f48402e 100644
--- a/erpnext/setup/demo.py
+++ b/erpnext/setup/demo.py
@@ -181,8 +181,10 @@
 def create_transaction_deletion_record(company):
 	transaction_deletion_record = frappe.new_doc("Transaction Deletion Record")
 	transaction_deletion_record.company = company
+	transaction_deletion_record.process_in_single_transaction = True
 	transaction_deletion_record.save(ignore_permissions=True)
 	transaction_deletion_record.submit()
+	transaction_deletion_record.start_deletion_tasks()
 
 
 def clear_masters():
diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js
index 3917005..9b1a41a 100644
--- a/erpnext/setup/doctype/company/company.js
+++ b/erpnext/setup/doctype/company/company.js
@@ -168,7 +168,7 @@
 
 	delete_company_transactions: function (frm) {
 		frappe.call({
-			method: "erpnext.setup.doctype.company.company.is_deletion_job_running",
+			method: "erpnext.setup.doctype.transaction_deletion_record.transaction_deletion_record.is_deletion_doc_running",
 			args: {
 				company: frm.doc.name,
 			},
diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py
index 876b6a4..3ca14e6 100644
--- a/erpnext/setup/doctype/company/company.py
+++ b/erpnext/setup/doctype/company/company.py
@@ -12,7 +12,6 @@
 from frappe.custom.doctype.property_setter.property_setter import make_property_setter
 from frappe.desk.page.setup_wizard.setup_wizard import make_records
 from frappe.utils import cint, formatdate, get_link_to_form, get_timestamp, today
-from frappe.utils.background_jobs import get_job, is_job_enqueued
 from frappe.utils.nestedset import NestedSet, rebuild_tree
 
 from erpnext.accounts.doctype.account.account import get_account_currency
@@ -901,37 +900,21 @@
 		return None
 
 
-def generate_id_for_deletion_job(company):
-	return "delete_company_transactions_" + company
-
-
-@frappe.whitelist()
-def is_deletion_job_running(company):
-	job_id = generate_id_for_deletion_job(company)
-	if is_job_enqueued(job_id):
-		job_name = get_job(job_id).get_id()  # job name will have site prefix
-		frappe.throw(
-			_("A Transaction Deletion Job: {0} is already running for {1}").format(
-				frappe.bold(get_link_to_form("RQ Job", job_name)), frappe.bold(company)
-			)
-		)
-
-
 @frappe.whitelist()
 def create_transaction_deletion_request(company):
-	is_deletion_job_running(company)
-	job_id = generate_id_for_deletion_job(company)
+	from erpnext.setup.doctype.transaction_deletion_record.transaction_deletion_record import (
+		is_deletion_doc_running,
+	)
+
+	is_deletion_doc_running(company)
 
 	tdr = frappe.get_doc({"doctype": "Transaction Deletion Record", "company": company})
-	tdr.insert()
+	tdr.submit()
+	tdr.start_deletion_tasks()
 
-	frappe.enqueue(
-		"frappe.utils.background_jobs.run_doc_method",
-		doctype=tdr.doctype,
-		name=tdr.name,
-		doc_method="submit",
-		job_id=job_id,
-		queue="long",
-		enqueue_after_commit=True,
+	frappe.msgprint(
+		_("A Transaction Deletion Document: {0} is triggered for {0}").format(
+			get_link_to_form("Transaction Deletion Record", tdr.name)
+		),
+		frappe.bold(company),
 	)
-	frappe.msgprint(_("A Transaction Deletion Job is triggered for {0}").format(frappe.bold(company)))
diff --git a/erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py
index 844e786..432438b 100644
--- a/erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py
+++ b/erpnext/setup/doctype/transaction_deletion_record/test_transaction_deletion_record.py
@@ -29,6 +29,7 @@
 		for i in range(5):
 			create_task("Dunder Mifflin Paper Co")
 		tdr = create_transaction_deletion_doc("Dunder Mifflin Paper Co")
+		tdr.reload()
 		for doctype in tdr.doctypes:
 			if doctype.doctype_name == "Task":
 				self.assertEqual(doctype.no_of_docs, 5)
@@ -60,7 +61,9 @@
 def create_transaction_deletion_doc(company):
 	tdr = frappe.get_doc({"doctype": "Transaction Deletion Record", "company": company})
 	tdr.insert()
+	tdr.process_in_single_transaction = True
 	tdr.submit()
+	tdr.start_deletion_tasks()
 	return tdr
 
 
diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js
index 527c753..9aa0278 100644
--- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js
+++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js
@@ -10,20 +10,24 @@
 				callback: function (r) {
 					doctypes_to_be_ignored_array = r.message;
 					populate_doctypes_to_be_ignored(doctypes_to_be_ignored_array, frm);
-					frm.fields_dict["doctypes_to_be_ignored"].grid.set_column_disp("no_of_docs", false);
 					frm.refresh_field("doctypes_to_be_ignored");
 				},
 			});
 		}
-
-		frm.get_field("doctypes_to_be_ignored").grid.cannot_add_rows = true;
-		frm.fields_dict["doctypes_to_be_ignored"].grid.set_column_disp("no_of_docs", false);
-		frm.refresh_field("doctypes_to_be_ignored");
 	},
 
 	refresh: function (frm) {
-		frm.fields_dict["doctypes_to_be_ignored"].grid.set_column_disp("no_of_docs", false);
-		frm.refresh_field("doctypes_to_be_ignored");
+		if (frm.doc.docstatus == 1 && ["Queued", "Failed"].find((x) => x == frm.doc.status)) {
+			let execute_btn = frm.doc.status == "Queued" ? __("Start Deletion") : __("Retry");
+
+			frm.add_custom_button(execute_btn, () => {
+				// Entry point for chain of events
+				frm.call({
+					method: "start_deletion_tasks",
+					doc: frm.doc,
+				});
+			});
+		}
 	},
 });
 
diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json
index 23e5947..b9f911d 100644
--- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json
+++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.json
@@ -7,10 +7,21 @@
  "engine": "InnoDB",
  "field_order": [
   "company",
+  "section_break_qpwb",
+  "status",
+  "error_log",
+  "tasks_section",
+  "delete_bin_data",
+  "delete_leads_and_addresses",
+  "reset_company_default_values",
+  "clear_notifications",
+  "initialize_doctypes_table",
+  "delete_transactions",
+  "section_break_tbej",
   "doctypes",
   "doctypes_to_be_ignored",
   "amended_from",
-  "status"
+  "process_in_single_transaction"
  ],
  "fields": [
   {
@@ -25,14 +36,16 @@
    "fieldname": "doctypes",
    "fieldtype": "Table",
    "label": "Summary",
-   "options": "Transaction Deletion Record Item",
+   "no_copy": 1,
+   "options": "Transaction Deletion Record Details",
    "read_only": 1
   },
   {
    "fieldname": "doctypes_to_be_ignored",
    "fieldtype": "Table",
    "label": "Excluded DocTypes",
-   "options": "Transaction Deletion Record Item"
+   "options": "Transaction Deletion Record Item",
+   "read_only": 1
   },
   {
    "fieldname": "amended_from",
@@ -46,18 +59,96 @@
   {
    "fieldname": "status",
    "fieldtype": "Select",
-   "hidden": 1,
    "label": "Status",
-   "options": "Draft\nCompleted"
+   "no_copy": 1,
+   "options": "Queued\nRunning\nFailed\nCompleted\nCancelled",
+   "read_only": 1
+  },
+  {
+   "fieldname": "section_break_tbej",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "tasks_section",
+   "fieldtype": "Section Break",
+   "label": "Tasks"
+  },
+  {
+   "default": "0",
+   "fieldname": "delete_bin_data",
+   "fieldtype": "Check",
+   "label": "Delete Bins",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "delete_leads_and_addresses",
+   "fieldtype": "Check",
+   "label": "Delete Leads and Addresses",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "clear_notifications",
+   "fieldtype": "Check",
+   "label": "Clear Notifications",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "reset_company_default_values",
+   "fieldtype": "Check",
+   "label": "Reset Company Default Values",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "delete_transactions",
+   "fieldtype": "Check",
+   "label": "Delete Transactions",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "initialize_doctypes_table",
+   "fieldtype": "Check",
+   "label": "Initialize Summary Table",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "depends_on": "eval: doc.error_log",
+   "fieldname": "error_log",
+   "fieldtype": "Long Text",
+   "label": "Error Log"
+  },
+  {
+   "fieldname": "section_break_qpwb",
+   "fieldtype": "Section Break"
+  },
+  {
+   "default": "0",
+   "fieldname": "process_in_single_transaction",
+   "fieldtype": "Check",
+   "hidden": 1,
+   "label": "Process in Single Transaction",
+   "no_copy": 1,
+   "read_only": 1
   }
  ],
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-08-04 20:15:59.071493",
+ "modified": "2024-03-21 10:29:19.456413",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "Transaction Deletion Record",
+ "naming_rule": "Expression (old style)",
  "owner": "Administrator",
  "permissions": [
   {
@@ -76,5 +167,6 @@
  ],
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py
index 88c4b07..00fad5f 100644
--- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py
+++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py
@@ -1,12 +1,14 @@
 # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
 # For license information, please see license.txt
 
+from collections import OrderedDict
 
 import frappe
 from frappe import _, qb
 from frappe.desk.notifications import clear_notifications
 from frappe.model.document import Document
-from frappe.utils import cint, create_batch
+from frappe.utils import cint, comma_and, create_batch, get_link_to_form
+from frappe.utils.background_jobs import get_job, is_job_enqueued
 
 
 class TransactionDeletionRecord(Document):
@@ -18,20 +20,42 @@
 	if TYPE_CHECKING:
 		from frappe.types import DF
 
+		from erpnext.accounts.doctype.transaction_deletion_record_details.transaction_deletion_record_details import (
+			TransactionDeletionRecordDetails,
+		)
 		from erpnext.setup.doctype.transaction_deletion_record_item.transaction_deletion_record_item import (
 			TransactionDeletionRecordItem,
 		)
 
 		amended_from: DF.Link | None
+		clear_notifications: DF.Check
 		company: DF.Link
-		doctypes: DF.Table[TransactionDeletionRecordItem]
+		delete_bin_data: DF.Check
+		delete_leads_and_addresses: DF.Check
+		delete_transactions: DF.Check
+		doctypes: DF.Table[TransactionDeletionRecordDetails]
 		doctypes_to_be_ignored: DF.Table[TransactionDeletionRecordItem]
-		status: DF.Literal["Draft", "Completed"]
+		error_log: DF.LongText | None
+		initialize_doctypes_table: DF.Check
+		process_in_single_transaction: DF.Check
+		reset_company_default_values: DF.Check
+		status: DF.Literal["Queued", "Running", "Failed", "Completed", "Cancelled"]
 	# end: auto-generated types
 
 	def __init__(self, *args, **kwargs):
 		super(TransactionDeletionRecord, self).__init__(*args, **kwargs)
 		self.batch_size = 5000
+		# Tasks are listed by their execution order
+		self.task_to_internal_method_map = OrderedDict(
+			{
+				"Delete Bins": "delete_bins",
+				"Delete Leads and Addresses": "delete_lead_addresses",
+				"Reset Company Values": "reset_company_values",
+				"Clear Notifications": "delete_notifications",
+				"Initialize Summary Table": "initialize_doctypes_to_be_deleted_table",
+				"Delete Transactions": "delete_company_transactions",
+			}
+		)
 
 	def validate(self):
 		frappe.only_for("System Manager")
@@ -48,104 +72,266 @@
 					title=_("Not Allowed"),
 				)
 
+	def generate_job_name_for_task(self, task=None):
+		method = self.task_to_internal_method_map[task]
+		return f"{self.name}_{method}"
+
+	def generate_job_name_for_next_tasks(self, task=None):
+		job_names = []
+		current_task_idx = list(self.task_to_internal_method_map).index(task)
+		for idx, task in enumerate(self.task_to_internal_method_map.keys(), 0):
+			# generate job_name for next tasks
+			if idx > current_task_idx:
+				job_names.append(self.generate_job_name_for_task(task))
+		return job_names
+
+	def generate_job_name_for_all_tasks(self):
+		job_names = []
+		for task in self.task_to_internal_method_map.keys():
+			job_names.append(self.generate_job_name_for_task(task))
+		return job_names
+
 	def before_submit(self):
+		if queued_docs := frappe.db.get_all(
+			"Transaction Deletion Record",
+			filters={"company": self.company, "status": ("in", ["Running", "Queued"]), "docstatus": 1},
+			pluck="name",
+		):
+			frappe.throw(
+				_(
+					"Cannot enqueue multi docs for one company. {0} is already queued/running for company: {1}"
+				).format(
+					comma_and([get_link_to_form("Transaction Deletion Record", x) for x in queued_docs]),
+					frappe.bold(self.company),
+				)
+			)
+
 		if not self.doctypes_to_be_ignored:
 			self.populate_doctypes_to_be_ignored_table()
 
-		self.delete_bins()
-		self.delete_lead_addresses()
-		self.reset_company_values()
-		clear_notifications()
-		self.delete_company_transactions()
+	def reset_task_flags(self):
+		self.clear_notifications = 0
+		self.delete_bin_data = 0
+		self.delete_leads_and_addresses = 0
+		self.delete_transactions = 0
+		self.initialize_doctypes_table = 0
+		self.reset_company_default_values = 0
+
+	def before_save(self):
+		self.status = ""
+		self.doctypes.clear()
+		self.reset_task_flags()
+
+	def on_submit(self):
+		self.db_set("status", "Queued")
+
+	def on_cancel(self):
+		self.db_set("status", "Cancelled")
+
+	def enqueue_task(self, task: str | None = None):
+		if task and task in self.task_to_internal_method_map:
+			# make sure that none of next tasks are already running
+			job_names = self.generate_job_name_for_next_tasks(task=task)
+			self.validate_running_task_for_doc(job_names=job_names)
+
+			# Generate Job Id to uniquely identify each task for this document
+			job_id = self.generate_job_name_for_task(task)
+
+			if self.process_in_single_transaction:
+				self.execute_task(task_to_execute=task)
+			else:
+				frappe.enqueue(
+					"frappe.utils.background_jobs.run_doc_method",
+					doctype=self.doctype,
+					name=self.name,
+					doc_method="execute_task",
+					job_id=job_id,
+					queue="long",
+					enqueue_after_commit=True,
+					task_to_execute=task,
+				)
+
+	def execute_task(self, task_to_execute: str | None = None):
+		if task_to_execute:
+			method = self.task_to_internal_method_map[task_to_execute]
+			if task := getattr(self, method, None):
+				try:
+					task()
+				except Exception as err:
+					frappe.db.rollback()
+					traceback = frappe.get_traceback(with_context=True)
+					if traceback:
+						message = "Traceback: <br>" + traceback
+						frappe.db.set_value(self.doctype, self.name, "error_log", message)
+					frappe.db.set_value(self.doctype, self.name, "status", "Failed")
+
+	def delete_notifications(self):
+		self.validate_doc_status()
+		if not self.clear_notifications:
+			clear_notifications()
+			self.db_set("clear_notifications", 1)
+		self.enqueue_task(task="Initialize Summary Table")
 
 	def populate_doctypes_to_be_ignored_table(self):
 		doctypes_to_be_ignored_list = get_doctypes_to_be_ignored()
 		for doctype in doctypes_to_be_ignored_list:
 			self.append("doctypes_to_be_ignored", {"doctype_name": doctype})
 
-	def delete_bins(self):
-		frappe.db.sql(
-			"""delete from `tabBin` where warehouse in
-				(select name from tabWarehouse where company=%s)""",
-			self.company,
-		)
+	def validate_running_task_for_doc(self, job_names: list = None):
+		# at most only one task should be runnning
+		running_tasks = []
+		for x in job_names:
+			if is_job_enqueued(x):
+				running_tasks.append(get_job(x).get_id())
 
-	def delete_lead_addresses(self):
-		"""Delete addresses to which leads are linked"""
-		leads = frappe.get_all("Lead", filters={"company": self.company})
-		leads = ["'%s'" % row.get("name") for row in leads]
-		addresses = []
-		if leads:
-			addresses = frappe.db.sql_list(
-				"""select parent from `tabDynamic Link` where link_name
-				in ({leads})""".format(
-					leads=",".join(leads)
+		if running_tasks:
+			frappe.throw(
+				_("{0} is already running for {1}").format(
+					comma_and([get_link_to_form("RQ Job", x) for x in running_tasks]), self.name
 				)
 			)
 
-			if addresses:
-				addresses = ["%s" % frappe.db.escape(addr) for addr in addresses]
-
-				frappe.db.sql(
-					"""delete from `tabAddress` where name in ({addresses}) and
-					name not in (select distinct dl1.parent from `tabDynamic Link` dl1
-					inner join `tabDynamic Link` dl2 on dl1.parent=dl2.parent
-					and dl1.link_doctype<>dl2.link_doctype)""".format(
-						addresses=",".join(addresses)
-					)
+	def validate_doc_status(self):
+		if self.status != "Running":
+			frappe.throw(
+				_("{0} is not running. Cannot trigger events for this Document").format(
+					get_link_to_form("Transaction Deletion Record", self.name)
 				)
+			)
 
-				frappe.db.sql(
-					"""delete from `tabDynamic Link` where link_doctype='Lead'
-					and parenttype='Address' and link_name in ({leads})""".format(
+	@frappe.whitelist()
+	def start_deletion_tasks(self):
+		# This method is the entry point for the chain of events that follow
+		self.db_set("status", "Running")
+		self.enqueue_task(task="Delete Bins")
+
+	def delete_bins(self):
+		self.validate_doc_status()
+		if not self.delete_bin_data:
+			frappe.db.sql(
+				"""delete from `tabBin` where warehouse in
+					(select name from tabWarehouse where company=%s)""",
+				self.company,
+			)
+			self.db_set("delete_bin_data", 1)
+		self.enqueue_task(task="Delete Leads and Addresses")
+
+	def delete_lead_addresses(self):
+		"""Delete addresses to which leads are linked"""
+		self.validate_doc_status()
+		if not self.delete_leads_and_addresses:
+			leads = frappe.get_all("Lead", filters={"company": self.company})
+			leads = ["'%s'" % row.get("name") for row in leads]
+			addresses = []
+			if leads:
+				addresses = frappe.db.sql_list(
+					"""select parent from `tabDynamic Link` where link_name
+					in ({leads})""".format(
 						leads=",".join(leads)
 					)
 				)
 
-			frappe.db.sql(
-				"""update `tabCustomer` set lead_name=NULL where lead_name in ({leads})""".format(
-					leads=",".join(leads)
+				if addresses:
+					addresses = ["%s" % frappe.db.escape(addr) for addr in addresses]
+
+					frappe.db.sql(
+						"""delete from `tabAddress` where name in ({addresses}) and
+						name not in (select distinct dl1.parent from `tabDynamic Link` dl1
+						inner join `tabDynamic Link` dl2 on dl1.parent=dl2.parent
+						and dl1.link_doctype<>dl2.link_doctype)""".format(
+							addresses=",".join(addresses)
+						)
+					)
+
+					frappe.db.sql(
+						"""delete from `tabDynamic Link` where link_doctype='Lead'
+						and parenttype='Address' and link_name in ({leads})""".format(
+							leads=",".join(leads)
+						)
+					)
+
+				frappe.db.sql(
+					"""update `tabCustomer` set lead_name=NULL where lead_name in ({leads})""".format(
+						leads=",".join(leads)
+					)
 				)
-			)
+			self.db_set("delete_leads_and_addresses", 1)
+		self.enqueue_task(task="Reset Company Values")
 
 	def reset_company_values(self):
-		company_obj = frappe.get_doc("Company", self.company)
-		company_obj.total_monthly_sales = 0
-		company_obj.sales_monthly_history = None
-		company_obj.save()
+		self.validate_doc_status()
+		if not self.reset_company_default_values:
+			company_obj = frappe.get_doc("Company", self.company)
+			company_obj.total_monthly_sales = 0
+			company_obj.sales_monthly_history = None
+			company_obj.save()
+			self.db_set("reset_company_default_values", 1)
+		self.enqueue_task(task="Clear Notifications")
+
+	def initialize_doctypes_to_be_deleted_table(self):
+		self.validate_doc_status()
+		if not self.initialize_doctypes_table:
+			doctypes_to_be_ignored_list = self.get_doctypes_to_be_ignored_list()
+			docfields = self.get_doctypes_with_company_field(doctypes_to_be_ignored_list)
+			tables = self.get_all_child_doctypes()
+			for docfield in docfields:
+				if docfield["parent"] != self.doctype:
+					no_of_docs = self.get_number_of_docs_linked_with_specified_company(
+						docfield["parent"], docfield["fieldname"]
+					)
+					if no_of_docs > 0:
+						# Initialize
+						self.populate_doctypes_table(tables, docfield["parent"], docfield["fieldname"], 0)
+			self.db_set("initialize_doctypes_table", 1)
+		self.enqueue_task(task="Delete Transactions")
 
 	def delete_company_transactions(self):
-		doctypes_to_be_ignored_list = self.get_doctypes_to_be_ignored_list()
-		docfields = self.get_doctypes_with_company_field(doctypes_to_be_ignored_list)
+		self.validate_doc_status()
+		if not self.delete_transactions:
+			doctypes_to_be_ignored_list = self.get_doctypes_to_be_ignored_list()
+			docfields = self.get_doctypes_with_company_field(doctypes_to_be_ignored_list)
 
-		tables = self.get_all_child_doctypes()
-		for docfield in docfields:
-			if docfield["parent"] != self.doctype:
-				no_of_docs = self.get_number_of_docs_linked_with_specified_company(
-					docfield["parent"], docfield["fieldname"]
-				)
-
-				if no_of_docs > 0:
-					self.delete_version_log(docfield["parent"], docfield["fieldname"])
-
-					reference_docs = frappe.get_all(
-						docfield["parent"], filters={docfield["fieldname"]: self.company}
+			tables = self.get_all_child_doctypes()
+			for docfield in self.doctypes:
+				if docfield.doctype_name != self.doctype and not docfield.done:
+					no_of_docs = self.get_number_of_docs_linked_with_specified_company(
+						docfield.doctype_name, docfield.docfield_name
 					)
-					reference_doc_names = [r.name for r in reference_docs]
+					if no_of_docs > 0:
+						reference_docs = frappe.get_all(
+							docfield.doctype_name, filters={docfield.docfield_name: self.company}, limit=self.batch_size
+						)
+						reference_doc_names = [r.name for r in reference_docs]
 
-					self.delete_communications(docfield["parent"], reference_doc_names)
-					self.delete_comments(docfield["parent"], reference_doc_names)
-					self.unlink_attachments(docfield["parent"], reference_doc_names)
+						self.delete_version_log(docfield.doctype_name, reference_doc_names)
+						self.delete_communications(docfield.doctype_name, reference_doc_names)
+						self.delete_comments(docfield.doctype_name, reference_doc_names)
+						self.unlink_attachments(docfield.doctype_name, reference_doc_names)
+						self.delete_child_tables(docfield.doctype_name, reference_doc_names)
+						self.delete_docs_linked_with_specified_company(docfield.doctype_name, reference_doc_names)
+						processed = int(docfield.no_of_docs) + len(reference_doc_names)
+						frappe.db.set_value(docfield.doctype, docfield.name, "no_of_docs", processed)
+					else:
+						# reset naming series
+						naming_series = frappe.db.get_value("DocType", docfield.doctype_name, "autoname")
+						if naming_series:
+							if "#" in naming_series:
+								self.update_naming_series(naming_series, docfield.doctype_name)
+						frappe.db.set_value(docfield.doctype, docfield.name, "done", 1)
 
-					self.populate_doctypes_table(tables, docfield["parent"], no_of_docs)
-
-					self.delete_child_tables(docfield["parent"], docfield["fieldname"])
-					self.delete_docs_linked_with_specified_company(docfield["parent"], docfield["fieldname"])
-
-					naming_series = frappe.db.get_value("DocType", docfield["parent"], "autoname")
-					if naming_series:
-						if "#" in naming_series:
-							self.update_naming_series(naming_series, docfield["parent"])
+			pending_doctypes = frappe.db.get_all(
+				"Transaction Deletion Record Details",
+				filters={"parent": self.name, "done": 0},
+				pluck="doctype_name",
+			)
+			if pending_doctypes:
+				# as method is enqueued after commit, calling itself will not make validate_doc_status to throw
+				# recursively call this task to delete all transactions
+				self.enqueue_task(task="Delete Transactions")
+			else:
+				self.db_set("status", "Completed")
+				self.db_set("delete_transactions", 1)
+				self.db_set("error_log", None)
 
 	def get_doctypes_to_be_ignored_list(self):
 		singles = frappe.get_all("DocType", filters={"issingle": 1}, pluck="name")
@@ -174,25 +360,24 @@
 	def get_number_of_docs_linked_with_specified_company(self, doctype, company_fieldname):
 		return frappe.db.count(doctype, {company_fieldname: self.company})
 
-	def populate_doctypes_table(self, tables, doctype, no_of_docs):
+	def populate_doctypes_table(self, tables, doctype, fieldname, no_of_docs):
+		self.flags.ignore_validate_update_after_submit = True
 		if doctype not in tables:
-			self.append("doctypes", {"doctype_name": doctype, "no_of_docs": no_of_docs})
+			self.append(
+				"doctypes", {"doctype_name": doctype, "docfield_name": fieldname, "no_of_docs": no_of_docs}
+			)
+		self.save(ignore_permissions=True)
 
-	def delete_child_tables(self, doctype, company_fieldname):
-		parent_docs_to_be_deleted = frappe.get_all(
-			doctype, {company_fieldname: self.company}, pluck="name"
-		)
-
+	def delete_child_tables(self, doctype, reference_doc_names):
 		child_tables = frappe.get_all(
 			"DocField", filters={"fieldtype": "Table", "parent": doctype}, pluck="options"
 		)
 
-		for batch in create_batch(parent_docs_to_be_deleted, self.batch_size):
-			for table in child_tables:
-				frappe.db.delete(table, {"parent": ["in", batch]})
+		for table in child_tables:
+			frappe.db.delete(table, {"parent": ["in", reference_doc_names]})
 
-	def delete_docs_linked_with_specified_company(self, doctype, company_fieldname):
-		frappe.db.delete(doctype, {company_fieldname: self.company})
+	def delete_docs_linked_with_specified_company(self, doctype, reference_doc_names):
+		frappe.db.delete(doctype, {"name": ("in", reference_doc_names)})
 
 	def update_naming_series(self, naming_series, doctype_name):
 		if "." in naming_series:
@@ -213,17 +398,11 @@
 
 		frappe.db.sql("""update `tabSeries` set current = %s where name=%s""", (last, prefix))
 
-	def delete_version_log(self, doctype, company_fieldname):
-		dt = qb.DocType(doctype)
-		names = qb.from_(dt).select(dt.name).where(dt[company_fieldname] == self.company).run(as_list=1)
-		names = [x[0] for x in names]
-
-		if names:
-			versions = qb.DocType("Version")
-			for batch in create_batch(names, self.batch_size):
-				qb.from_(versions).delete().where(
-					(versions.ref_doctype == doctype) & (versions.docname.isin(batch))
-				).run()
+	def delete_version_log(self, doctype, docnames):
+		versions = qb.DocType("Version")
+		qb.from_(versions).delete().where(
+			(versions.ref_doctype == doctype) & (versions.docname.isin(docnames))
+		).run()
 
 	def delete_communications(self, doctype, reference_doc_names):
 		communications = frappe.get_all(
@@ -295,3 +474,34 @@
 	doctypes_to_be_ignored.extend(frappe.get_hooks("company_data_to_be_ignored") or [])
 
 	return doctypes_to_be_ignored
+
+
+@frappe.whitelist()
+def is_deletion_doc_running(company: str | None = None, err_msg: str | None = None):
+	if company:
+		if running_deletion_jobs := frappe.db.get_all(
+			"Transaction Deletion Record",
+			filters={"docstatus": 1, "company": company, "status": "Running"},
+		):
+			if not err_msg:
+				err_msg = ""
+			frappe.throw(
+				title=_("Deletion in Progress!"),
+				msg=_("Transaction Deletion Document: {0} is running for this Company. {1}").format(
+					get_link_to_form("Transaction Deletion Record", running_deletion_jobs[0].name), err_msg
+				),
+			)
+
+
+def check_for_running_deletion_job(doc, method=None):
+	# Check if DocType has 'company' field
+	df = qb.DocType("DocField")
+	if (
+		not_allowed := qb.from_(df)
+		.select(df.parent)
+		.where((df.fieldname == "company") & (df.parent == doc.doctype))
+		.run()
+	):
+		is_deletion_doc_running(
+			doc.company, _("Cannot make any transactions until the deletion job is completed")
+		)
diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record_list.js b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record_list.js
index 08a35df..285cb6d 100644
--- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record_list.js
+++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record_list.js
@@ -2,11 +2,15 @@
 // License: GNU General Public License v3. See license.txt
 
 frappe.listview_settings["Transaction Deletion Record"] = {
+	add_fields: ["status"],
 	get_indicator: function (doc) {
-		if (doc.docstatus == 0) {
-			return [__("Draft"), "red"];
-		} else {
-			return [__("Completed"), "green"];
-		}
+		let colors = {
+			Queued: "orange",
+			Completed: "green",
+			Running: "blue",
+			Failed: "red",
+		};
+		let status = doc.status;
+		return [__(status), colors[status], "status,=," + status];
 	},
 };
diff --git a/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.json b/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.json
index be0be94..89db636 100644
--- a/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.json
+++ b/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.json
@@ -5,8 +5,7 @@
  "editable_grid": 1,
  "engine": "InnoDB",
  "field_order": [
-  "doctype_name",
-  "no_of_docs"
+  "doctype_name"
  ],
  "fields": [
   {
@@ -16,18 +15,12 @@
    "label": "DocType",
    "options": "DocType",
    "reqd": 1
-  },
-  {
-   "fieldname": "no_of_docs",
-   "fieldtype": "Data",
-   "in_list_view": 1,
-   "label": "Number of Docs"
   }
  ],
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2021-05-08 23:10:46.166744",
+ "modified": "2024-02-04 10:56:27.413691",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "Transaction Deletion Record Item",
@@ -35,5 +28,6 @@
  "permissions": [],
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.py b/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.py
index f154cdb..9066607 100644
--- a/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.py
+++ b/erpnext/setup/doctype/transaction_deletion_record_item/transaction_deletion_record_item.py
@@ -16,7 +16,6 @@
 		from frappe.types import DF
 
 		doctype_name: DF.Link
-		no_of_docs: DF.Data | None
 		parent: DF.Data
 		parentfield: DF.Data
 		parenttype: DF.Data
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json
index d07a825..87c3333 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.json
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.json
@@ -680,7 +680,7 @@
   },
   {
    "fieldname": "other_charges_calculation",
-   "fieldtype": "Long Text",
+   "fieldtype": "Text Editor",
    "label": "Taxes and Charges Calculation",
    "no_copy": 1,
    "oldfieldtype": "HTML",
@@ -1397,7 +1397,7 @@
  "idx": 146,
  "is_submittable": 1,
  "links": [],
- "modified": "2024-03-05 11:58:47.784349",
+ "modified": "2024-03-20 16:05:02.854990",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Delivery Note",
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index 2f52f21..e17a0a2 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -76,7 +76,7 @@
 		ignore_pricing_rule: DF.Check
 		in_words: DF.Data | None
 		incoterm: DF.Link | None
-		installation_status: DF.Literal
+		installation_status: DF.Literal[None]
 		instructions: DF.Text | None
 		inter_company_reference: DF.Link | None
 		is_internal_customer: DF.Check
@@ -90,7 +90,7 @@
 		named_place: DF.Data | None
 		naming_series: DF.Literal["MAT-DN-.YYYY.-", "MAT-DN-RET-.YYYY.-"]
 		net_total: DF.Currency
-		other_charges_calculation: DF.LongText | None
+		other_charges_calculation: DF.TextEditor | None
 		packed_items: DF.Table[PackedItem]
 		per_billed: DF.Percent
 		per_installed: DF.Percent
diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
index 247672f..b8164b2 100644
--- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
+++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json
@@ -796,7 +796,8 @@
    "label": "Purchase Order",
    "options": "Purchase Order",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "search_index": 1
   },
   {
    "fieldname": "column_break_82",
@@ -912,7 +913,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2024-02-04 14:10:31.750340",
+ "modified": "2024-03-21 18:15:07.603672",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Delivery Note Item",
diff --git a/erpnext/stock/doctype/item_price/item_price.py b/erpnext/stock/doctype/item_price/item_price.py
index de2add6..25a28b4 100644
--- a/erpnext/stock/doctype/item_price/item_price.py
+++ b/erpnext/stock/doctype/item_price/item_price.py
@@ -7,7 +7,6 @@
 from frappe.model.document import Document
 from frappe.query_builder import Criterion
 from frappe.query_builder.functions import Cast_
-from frappe.utils import getdate
 
 
 class ItemPriceDuplicateItem(frappe.ValidationError):
@@ -46,7 +45,7 @@
 
 	def validate(self):
 		self.validate_item()
-		self.validate_dates()
+		self.validate_from_to_dates("valid_from", "valid_upto")
 		self.update_price_list_details()
 		self.update_item_details()
 		self.check_duplicates()
@@ -56,11 +55,6 @@
 		if not frappe.db.exists("Item", self.item_code):
 			frappe.throw(_("Item {0} not found.").format(self.item_code))
 
-	def validate_dates(self):
-		if self.valid_from and self.valid_upto:
-			if getdate(self.valid_from) > getdate(self.valid_upto):
-				frappe.throw(_("Valid From Date must be lesser than Valid Up To Date."))
-
 	def update_price_list_details(self):
 		if self.price_list:
 			price_list_details = frappe.db.get_value(
diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py
index 627520c..4eab7e8 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.py
+++ b/erpnext/stock/doctype/pick_list/pick_list.py
@@ -778,7 +778,7 @@
 
 	if picked_item_details:
 		for location in list(locations):
-			if location["qty"] < 1:
+			if location["qty"] < 0:
 				locations.remove(location)
 
 		total_qty_available = sum(location.get("qty") for location in locations)
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
index a181022..b926e98 100755
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
@@ -651,7 +651,7 @@
   },
   {
    "fieldname": "other_charges_calculation",
-   "fieldtype": "Long Text",
+   "fieldtype": "Text Editor",
    "label": "Taxes and Charges Calculation",
    "no_copy": 1,
    "oldfieldtype": "HTML",
@@ -1252,7 +1252,7 @@
  "idx": 261,
  "is_submittable": 1,
  "links": [],
- "modified": "2023-12-18 17:26:41.279663",
+ "modified": "2024-03-20 16:05:31.713453",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Purchase Receipt",
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 2eec58f..034dd0a 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -87,7 +87,7 @@
 		named_place: DF.Data | None
 		naming_series: DF.Literal["MAT-PRE-.YYYY.-", "MAT-PR-RET-.YYYY.-"]
 		net_total: DF.Currency
-		other_charges_calculation: DF.LongText | None
+		other_charges_calculation: DF.TextEditor | None
 		per_billed: DF.Percent
 		per_returned: DF.Percent
 		plc_conversion_rate: DF.Float
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 12049f1..c6f8d62 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -825,7 +825,6 @@
 		):
 			if args.price_list and args.rate:
 				insert_item_price(args)
-			return out
 
 		out.price_list_rate = (
 			flt(price_list_rate) * flt(args.plc_conversion_rate) / flt(args.conversion_rate)