Merge branch 'develop' into anand_codeowners
diff --git a/erpnext/accounts/doctype/dunning/dunning.py b/erpnext/accounts/doctype/dunning/dunning.py
index 331adb4..b4df0a5 100644
--- a/erpnext/accounts/doctype/dunning/dunning.py
+++ b/erpnext/accounts/doctype/dunning/dunning.py
@@ -40,7 +40,7 @@
 
 	def on_cancel(self):
 		if self.dunning_amount:
-			self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry")
+			self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry")
 			make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
 
 	def make_gl_entries(self):
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
index 154fdc0..675a328 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
@@ -234,7 +234,7 @@
 	def allocate_entries(self, args):
 		self.validate_entries()
 
-		invoice_exchange_map = self.get_invoice_exchange_map(args.get("invoices"))
+		invoice_exchange_map = self.get_invoice_exchange_map(args.get("invoices"), args.get("payments"))
 		default_exchange_gain_loss_account = frappe.get_cached_value(
 			"Company", self.company, "exchange_gain_loss_account"
 		)
@@ -253,6 +253,9 @@
 					pay["amount"] = 0
 
 				inv["exchange_rate"] = invoice_exchange_map.get(inv.get("invoice_number"))
+				if pay.get("reference_type") in ["Sales Invoice", "Purchase Invoice"]:
+					pay["exchange_rate"] = invoice_exchange_map.get(pay.get("reference_name"))
+
 				res.difference_amount = self.get_difference_amount(pay, inv, res["allocated_amount"])
 				res.difference_account = default_exchange_gain_loss_account
 				res.exchange_rate = inv.get("exchange_rate")
@@ -407,13 +410,21 @@
 		if not self.get("payments"):
 			frappe.throw(_("No records found in the Payments table"))
 
-	def get_invoice_exchange_map(self, invoices):
+	def get_invoice_exchange_map(self, invoices, payments):
 		sales_invoices = [
 			d.get("invoice_number") for d in invoices if d.get("invoice_type") == "Sales Invoice"
 		]
+
+		sales_invoices.extend(
+			[d.get("reference_name") for d in payments if d.get("reference_type") == "Sales Invoice"]
+		)
 		purchase_invoices = [
 			d.get("invoice_number") for d in invoices if d.get("invoice_type") == "Purchase Invoice"
 		]
+		purchase_invoices.extend(
+			[d.get("reference_name") for d in payments if d.get("reference_type") == "Purchase Invoice"]
+		)
+
 		invoice_exchange_map = frappe._dict()
 
 		if sales_invoices:
diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py
index 00e3934..f9dda05 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py
@@ -473,6 +473,11 @@
 		invoices = [x.as_dict() for x in pr.get("invoices")]
 		payments = [x.as_dict() for x in pr.get("payments")]
 		pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
+
+		# Cr Note and Invoice are of the same currency. There shouldn't any difference amount.
+		for row in pr.allocation:
+			self.assertEqual(flt(row.get("difference_amount")), 0.0)
+
 		pr.reconcile()
 
 		pr.get_unreconciled_entries()
@@ -506,6 +511,11 @@
 		payments = [x.as_dict() for x in pr.get("payments")]
 		pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
 		pr.allocation[0].allocated_amount = allocated_amount
+
+		# Cr Note and Invoice are of the same currency. There shouldn't any difference amount.
+		for row in pr.allocation:
+			self.assertEqual(flt(row.get("difference_amount")), 0.0)
+
 		pr.reconcile()
 
 		# assert outstanding
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py
index fc837c7..52eb29b 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/payment_request.py
@@ -45,21 +45,20 @@
 			frappe.throw(_("To create a Payment Request reference document is required"))
 
 	def validate_payment_request_amount(self):
-		existing_payment_request_amount = get_existing_payment_request_amount(
-			self.reference_doctype, self.reference_name
+		existing_payment_request_amount = flt(
+			get_existing_payment_request_amount(self.reference_doctype, self.reference_name)
 		)
 
-		if existing_payment_request_amount:
-			ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
-			if not hasattr(ref_doc, "order_type") or getattr(ref_doc, "order_type") != "Shopping Cart":
-				ref_amount = get_amount(ref_doc, self.payment_account)
+		ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
+		if not hasattr(ref_doc, "order_type") or getattr(ref_doc, "order_type") != "Shopping Cart":
+			ref_amount = get_amount(ref_doc, self.payment_account)
 
-				if existing_payment_request_amount + flt(self.grand_total) > ref_amount:
-					frappe.throw(
-						_("Total Payment Request amount cannot be greater than {0} amount").format(
-							self.reference_doctype
-						)
+			if existing_payment_request_amount + flt(self.grand_total) > ref_amount:
+				frappe.throw(
+					_("Total Payment Request amount cannot be greater than {0} amount").format(
+						self.reference_doctype
 					)
+				)
 
 	def validate_currency(self):
 		ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index a03de9e..2608c03 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -1512,9 +1512,12 @@
 		ref_doc = frappe.get_doc(voucher_type, voucher_no)
 
 		# Didn't use db_set for optimisation purpose
-		ref_doc.outstanding_amount = outstanding["outstanding_in_account_currency"]
+		ref_doc.outstanding_amount = outstanding["outstanding_in_account_currency"] or 0.0
 		frappe.db.set_value(
-			voucher_type, voucher_no, "outstanding_amount", outstanding["outstanding_in_account_currency"]
+			voucher_type,
+			voucher_no,
+			"outstanding_amount",
+			outstanding["outstanding_in_account_currency"] or 0.0,
 		)
 
 		ref_doc.set_status(update=True)
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index 54f0d94..4f7d9ad 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -712,6 +712,8 @@
 						asset.purchase_date = self.posting_date
 						asset.supplier = self.supplier
 					elif self.docstatus == 2:
+						if asset.docstatus == 2:
+							continue
 						if asset.docstatus == 0:
 							asset.set(field, None)
 							asset.supplier = None
diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py
index 9fcb769..fc6793a 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.py
@@ -252,6 +252,7 @@
 			child.parent = par.name and par.docstatus = 1
 			and par.is_return = 1 and par.return_against = %s
 		group by item_code
+		for update
 	""".format(
 			column, doc.doctype, doc.doctype
 		),
diff --git a/erpnext/crm/doctype/lead_source/lead_source.json b/erpnext/crm/doctype/lead_source/lead_source.json
index 723c6d9..c3cedcc 100644
--- a/erpnext/crm/doctype/lead_source/lead_source.json
+++ b/erpnext/crm/doctype/lead_source/lead_source.json
@@ -26,10 +26,11 @@
   }
  ],
  "links": [],
- "modified": "2021-02-08 12:51:48.971517",
+ "modified": "2023-02-10 00:51:44.973957",
  "modified_by": "Administrator",
  "module": "CRM",
  "name": "Lead Source",
+ "naming_rule": "By fieldname",
  "owner": "Administrator",
  "permissions": [
   {
@@ -58,5 +59,7 @@
  ],
  "quick_entry": 1,
  "sort_field": "modified",
- "sort_order": "DESC"
+ "sort_order": "DESC",
+ "states": [],
+ "translated_doctype": 1
 }
\ No newline at end of file
diff --git a/erpnext/crm/doctype/sales_stage/sales_stage.json b/erpnext/crm/doctype/sales_stage/sales_stage.json
index 77aa559..caf8ff5 100644
--- a/erpnext/crm/doctype/sales_stage/sales_stage.json
+++ b/erpnext/crm/doctype/sales_stage/sales_stage.json
@@ -18,10 +18,11 @@
   }
  ],
  "links": [],
- "modified": "2020-05-20 12:22:01.866472",
+ "modified": "2023-02-10 01:40:23.713390",
  "modified_by": "Administrator",
  "module": "CRM",
  "name": "Sales Stage",
+ "naming_rule": "By fieldname",
  "owner": "Administrator",
  "permissions": [
   {
@@ -40,5 +41,7 @@
  "quick_entry": 1,
  "sort_field": "modified",
  "sort_order": "DESC",
- "track_changes": 1
+ "states": [],
+ "track_changes": 1,
+ "translated_doctype": 1
 }
\ No newline at end of file
diff --git a/erpnext/loan_management/report/loan_interest_report/loan_interest_report.js b/erpnext/loan_management/report/loan_interest_report/loan_interest_report.js
index a227b6d..458c79a 100644
--- a/erpnext/loan_management/report/loan_interest_report/loan_interest_report.js
+++ b/erpnext/loan_management/report/loan_interest_report/loan_interest_report.js
@@ -11,6 +11,40 @@
 			"options": "Company",
 			"default": frappe.defaults.get_user_default("Company"),
 			"reqd": 1
-		}
+		},
+		{
+			"fieldname":"applicant_type",
+			"label": __("Applicant Type"),
+			"fieldtype": "Select",
+			"options": ["Customer", "Employee"],
+			"reqd": 1,
+			"default": "Customer",
+			on_change: function() {
+				frappe.query_report.set_filter_value('applicant', "");
+			}
+		},
+		{
+			"fieldname": "applicant",
+			"label": __("Applicant"),
+			"fieldtype": "Dynamic Link",
+			"get_options": function() {
+				var applicant_type = frappe.query_report.get_filter_value('applicant_type');
+				var applicant = frappe.query_report.get_filter_value('applicant');
+				if(applicant && !applicant_type) {
+					frappe.throw(__("Please select Applicant Type first"));
+				}
+				return applicant_type;
+			}
+		},
+		{
+			"fieldname":"from_date",
+			"label": __("From Date"),
+			"fieldtype": "Date",
+		},
+		{
+			"fieldname":"to_date",
+			"label": __("From Date"),
+			"fieldtype": "Date",
+		},
 	]
 };
diff --git a/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py b/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py
index 9186ce6..58a7880 100644
--- a/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py
+++ b/erpnext/loan_management/report/loan_interest_report/loan_interest_report.py
@@ -13,12 +13,12 @@
 
 
 def execute(filters=None):
-	columns = get_columns(filters)
+	columns = get_columns()
 	data = get_active_loan_details(filters)
 	return columns, data
 
 
-def get_columns(filters):
+def get_columns():
 	columns = [
 		{"label": _("Loan"), "fieldname": "loan", "fieldtype": "Link", "options": "Loan", "width": 160},
 		{"label": _("Status"), "fieldname": "status", "fieldtype": "Data", "width": 160},
@@ -71,6 +71,13 @@
 			"width": 120,
 		},
 		{
+			"label": _("Accrued Principal"),
+			"fieldname": "accrued_principal",
+			"fieldtype": "Currency",
+			"options": "currency",
+			"width": 120,
+		},
+		{
 			"label": _("Total Repayment"),
 			"fieldname": "total_repayment",
 			"fieldtype": "Currency",
@@ -137,11 +144,16 @@
 
 
 def get_active_loan_details(filters):
-
-	filter_obj = {"status": ("!=", "Closed")}
+	filter_obj = {
+		"status": ("!=", "Closed"),
+		"docstatus": 1,
+	}
 	if filters.get("company"):
 		filter_obj.update({"company": filters.get("company")})
 
+	if filters.get("applicant"):
+		filter_obj.update({"applicant": filters.get("applicant")})
+
 	loan_details = frappe.get_all(
 		"Loan",
 		fields=[
@@ -167,8 +179,8 @@
 
 	sanctioned_amount_map = get_sanctioned_amount_map()
 	penal_interest_rate_map = get_penal_interest_rate_map()
-	payments = get_payments(loan_list)
-	accrual_map = get_interest_accruals(loan_list)
+	payments = get_payments(loan_list, filters)
+	accrual_map = get_interest_accruals(loan_list, filters)
 	currency = erpnext.get_company_currency(filters.get("company"))
 
 	for loan in loan_details:
@@ -183,6 +195,7 @@
 				- flt(loan.written_off_amount),
 				"total_repayment": flt(payments.get(loan.loan)),
 				"accrued_interest": flt(accrual_map.get(loan.loan, {}).get("accrued_interest")),
+				"accrued_principal": flt(accrual_map.get(loan.loan, {}).get("accrued_principal")),
 				"interest_outstanding": flt(accrual_map.get(loan.loan, {}).get("interest_outstanding")),
 				"penalty": flt(accrual_map.get(loan.loan, {}).get("penalty")),
 				"penalty_interest": penal_interest_rate_map.get(loan.loan_type),
@@ -212,20 +225,35 @@
 	)
 
 
-def get_payments(loans):
+def get_payments(loans, filters):
+	query_filters = {"against_loan": ("in", loans)}
+
+	if filters.get("from_date"):
+		query_filters.update({"posting_date": (">=", filters.get("from_date"))})
+
+	if filters.get("to_date"):
+		query_filters.update({"posting_date": ("<=", filters.get("to_date"))})
+
 	return frappe._dict(
 		frappe.get_all(
 			"Loan Repayment",
 			fields=["against_loan", "sum(amount_paid)"],
-			filters={"against_loan": ("in", loans)},
+			filters=query_filters,
 			group_by="against_loan",
 			as_list=1,
 		)
 	)
 
 
-def get_interest_accruals(loans):
+def get_interest_accruals(loans, filters):
 	accrual_map = {}
+	query_filters = {"loan": ("in", loans)}
+
+	if filters.get("from_date"):
+		query_filters.update({"posting_date": (">=", filters.get("from_date"))})
+
+	if filters.get("to_date"):
+		query_filters.update({"posting_date": ("<=", filters.get("to_date"))})
 
 	interest_accruals = frappe.get_all(
 		"Loan Interest Accrual",
@@ -236,8 +264,9 @@
 			"penalty_amount",
 			"paid_interest_amount",
 			"accrual_type",
+			"payable_principal_amount",
 		],
-		filters={"loan": ("in", loans)},
+		filters=query_filters,
 		order_by="posting_date desc",
 	)
 
@@ -246,6 +275,7 @@
 			entry.loan,
 			{
 				"accrued_interest": 0.0,
+				"accrued_principal": 0.0,
 				"undue_interest": 0.0,
 				"interest_outstanding": 0.0,
 				"last_accrual_date": "",
@@ -270,6 +300,7 @@
 			accrual_map[entry.loan]["undue_interest"] += entry.interest_amount - entry.paid_interest_amount
 
 		accrual_map[entry.loan]["accrued_interest"] += entry.interest_amount
+		accrual_map[entry.loan]["accrued_principal"] += entry.payable_principal_amount
 
 		if last_accrual_date and getdate(entry.posting_date) == last_accrual_date:
 			accrual_map[entry.loan]["penalty"] = entry.penalty_amount
diff --git a/erpnext/loan_management/workspace/loans/loans.json b/erpnext/loan_management/workspace/loans/loans.json
new file mode 100644
index 0000000..c65be4e
--- /dev/null
+++ b/erpnext/loan_management/workspace/loans/loans.json
@@ -0,0 +1,315 @@
+{
+ "charts": [],
+ "content": "[{\"id\":\"_38WStznya\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"id\":\"t7o_K__1jB\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Loan Application\",\"col\":3}},{\"id\":\"IRiNDC6w1p\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Loan\",\"col\":3}},{\"id\":\"xbbo0FYbq0\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"id\":\"7ZL4Bro-Vi\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"yhyioTViZ3\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports &amp; Masters</b></span>\",\"col\":12}},{\"id\":\"oYFn4b1kSw\",\"type\":\"card\",\"data\":{\"card_name\":\"Loan\",\"col\":4}},{\"id\":\"vZepJF5tl9\",\"type\":\"card\",\"data\":{\"card_name\":\"Loan Processes\",\"col\":4}},{\"id\":\"k-393Mjhqe\",\"type\":\"card\",\"data\":{\"card_name\":\"Disbursement and Repayment\",\"col\":4}},{\"id\":\"6crJ0DBiBJ\",\"type\":\"card\",\"data\":{\"card_name\":\"Loan Security\",\"col\":4}},{\"id\":\"Um5YwxVLRJ\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}}]",
+ "creation": "2020-03-12 16:35:55.299820",
+ "docstatus": 0,
+ "doctype": "Workspace",
+ "for_user": "",
+ "hide_custom": 0,
+ "icon": "loan",
+ "idx": 0,
+ "is_hidden": 0,
+ "label": "Loans",
+ "links": [
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Loan",
+   "link_count": 0,
+   "onboard": 0,
+   "type": "Card Break"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Loan Type",
+   "link_count": 0,
+   "link_to": "Loan Type",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Loan Application",
+   "link_count": 0,
+   "link_to": "Loan Application",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Loan",
+   "link_count": 0,
+   "link_to": "Loan",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Loan Processes",
+   "link_count": 0,
+   "onboard": 0,
+   "type": "Card Break"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Process Loan Security Shortfall",
+   "link_count": 0,
+   "link_to": "Process Loan Security Shortfall",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Process Loan Interest Accrual",
+   "link_count": 0,
+   "link_to": "Process Loan Interest Accrual",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Disbursement and Repayment",
+   "link_count": 0,
+   "onboard": 0,
+   "type": "Card Break"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Loan Disbursement",
+   "link_count": 0,
+   "link_to": "Loan Disbursement",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Loan Repayment",
+   "link_count": 0,
+   "link_to": "Loan Repayment",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Loan Write Off",
+   "link_count": 0,
+   "link_to": "Loan Write Off",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Loan Interest Accrual",
+   "link_count": 0,
+   "link_to": "Loan Interest Accrual",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Loan Security",
+   "link_count": 0,
+   "onboard": 0,
+   "type": "Card Break"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Loan Security Type",
+   "link_count": 0,
+   "link_to": "Loan Security Type",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Loan Security Price",
+   "link_count": 0,
+   "link_to": "Loan Security Price",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Loan Security",
+   "link_count": 0,
+   "link_to": "Loan Security",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Loan Security Pledge",
+   "link_count": 0,
+   "link_to": "Loan Security Pledge",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Loan Security Unpledge",
+   "link_count": 0,
+   "link_to": "Loan Security Unpledge",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Loan Security Shortfall",
+   "link_count": 0,
+   "link_to": "Loan Security Shortfall",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Reports",
+   "link_count": 6,
+   "onboard": 0,
+   "type": "Card Break"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Loan Repayment and Closure",
+   "link_count": 0,
+   "link_to": "Loan Repayment and Closure",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "dependencies": "",
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Loan Security Status",
+   "link_count": 0,
+   "link_to": "Loan Security Status",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Loan Interest Report",
+   "link_count": 0,
+   "link_to": "Loan Interest Report",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Loan Security Exposure",
+   "link_count": 0,
+   "link_to": "Loan Security Exposure",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Applicant-Wise Loan Security Exposure",
+   "link_count": 0,
+   "link_to": "Applicant-Wise Loan Security Exposure",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
+   "hidden": 0,
+   "is_query_report": 1,
+   "label": "Loan Security Status",
+   "link_count": 0,
+   "link_to": "Loan Security Status",
+   "link_type": "Report",
+   "onboard": 0,
+   "type": "Link"
+  }
+ ],
+ "modified": "2023-01-31 19:47:13.114415",
+ "modified_by": "Administrator",
+ "module": "Loan Management",
+ "name": "Loans",
+ "owner": "Administrator",
+ "parent_page": "",
+ "public": 1,
+ "quick_lists": [],
+ "restrict_to_domain": "",
+ "roles": [],
+ "sequence_id": 16.0,
+ "shortcuts": [
+  {
+   "color": "Green",
+   "format": "{} Open",
+   "label": "Loan Application",
+   "link_to": "Loan Application",
+   "stats_filter": "{ \"status\": \"Open\" }",
+   "type": "DocType"
+  },
+  {
+   "label": "Loan",
+   "link_to": "Loan",
+   "type": "DocType"
+  },
+  {
+   "doc_view": "",
+   "label": "Dashboard",
+   "link_to": "Loan Dashboard",
+   "type": "Dashboard"
+  }
+ ],
+ "title": "Loans"
+}
\ No newline at end of file
diff --git a/erpnext/manufacturing/doctype/bom/bom.json b/erpnext/manufacturing/doctype/bom/bom.json
index c2b331f..db699b9 100644
--- a/erpnext/manufacturing/doctype/bom/bom.json
+++ b/erpnext/manufacturing/doctype/bom/bom.json
@@ -289,7 +289,7 @@
   {
    "fieldname": "scrap_items",
    "fieldtype": "Table",
-   "label": "Items",
+   "label": "Scrap Items",
    "options": "BOM Scrap Item"
   },
   {
@@ -605,7 +605,7 @@
  "image_field": "image",
  "is_submittable": 1,
  "links": [],
- "modified": "2023-01-10 07:47:08.652616",
+ "modified": "2023-02-13 17:31:37.504565",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "BOM",
diff --git a/erpnext/patches/v11_0/update_sales_partner_type.py b/erpnext/patches/v11_0/update_sales_partner_type.py
index 2d37fd6..72fd424 100644
--- a/erpnext/patches/v11_0/update_sales_partner_type.py
+++ b/erpnext/patches/v11_0/update_sales_partner_type.py
@@ -1,16 +1,17 @@
 import frappe
-from frappe import _
 
 
 def execute():
-	from erpnext.setup.setup_wizard.operations.install_fixtures import default_sales_partner_type
+	from erpnext.setup.setup_wizard.operations.install_fixtures import read_lines
 
 	frappe.reload_doc("selling", "doctype", "sales_partner_type")
 
 	frappe.local.lang = frappe.db.get_default("lang") or "en"
 
+	default_sales_partner_type = read_lines("sales_partner_type.txt")
+
 	for s in default_sales_partner_type:
-		insert_sales_partner_type(_(s))
+		insert_sales_partner_type(s)
 
 	# get partner type in existing forms (customized)
 	# and create a document if not created
diff --git a/erpnext/projects/doctype/timesheet/test_timesheet.py b/erpnext/projects/doctype/timesheet/test_timesheet.py
index e098c3e..828a55e 100644
--- a/erpnext/projects/doctype/timesheet/test_timesheet.py
+++ b/erpnext/projects/doctype/timesheet/test_timesheet.py
@@ -161,6 +161,37 @@
 		to_time = timesheet.time_logs[0].to_time
 		self.assertEqual(to_time, add_to_date(from_time, hours=2, as_datetime=True))
 
+	def test_per_billed_hours(self):
+		"""If amounts are 0, per_billed should be calculated based on hours."""
+		ts = frappe.new_doc("Timesheet")
+		ts.total_billable_amount = 0
+		ts.total_billed_amount = 0
+		ts.total_billable_hours = 2
+
+		ts.total_billed_hours = 0.5
+		ts.calculate_percentage_billed()
+		self.assertEqual(ts.per_billed, 25)
+
+		ts.total_billed_hours = 2
+		ts.calculate_percentage_billed()
+		self.assertEqual(ts.per_billed, 100)
+
+	def test_per_billed_amount(self):
+		"""If amounts are > 0, per_billed should be calculated based on amounts, regardless of hours."""
+		ts = frappe.new_doc("Timesheet")
+		ts.total_billable_hours = 2
+		ts.total_billed_hours = 1
+		ts.total_billable_amount = 200
+		ts.total_billed_amount = 50
+		ts.calculate_percentage_billed()
+		self.assertEqual(ts.per_billed, 25)
+
+		ts.total_billed_hours = 3
+		ts.total_billable_amount = 200
+		ts.total_billed_amount = 200
+		ts.calculate_percentage_billed()
+		self.assertEqual(ts.per_billed, 100)
+
 
 def make_timesheet(
 	employee,
diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py
index f3bd09a..d482a46 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.py
+++ b/erpnext/projects/doctype/timesheet/timesheet.py
@@ -64,6 +64,8 @@
 		self.per_billed = 0
 		if self.total_billed_amount > 0 and self.total_billable_amount > 0:
 			self.per_billed = (self.total_billed_amount * 100) / self.total_billable_amount
+		elif self.total_billed_hours > 0 and self.total_billable_hours > 0:
+			self.per_billed = (self.total_billed_hours * 100) / self.total_billable_hours
 
 	def update_billing_hours(self, args):
 		if args.is_billable:
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index 2ce0c7e..a87c3ec 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -126,7 +126,16 @@
 				frappe.model.round_floats_in(item);
 				item.net_rate = item.rate;
 				item.qty = item.qty === undefined ? (me.frm.doc.is_return ? -1 : 1) : item.qty;
-				item.net_amount = item.amount = flt(item.rate * item.qty, precision("amount", item));
+
+				if (!(me.frm.doc.is_return || me.frm.doc.is_debit_note)) {
+					item.net_amount = item.amount = flt(item.rate * item.qty, precision("amount", item));
+				}
+				else {
+					let qty = item.qty || 1;
+					qty = me.frm.doc.is_return ? -1 * qty : qty;
+					item.net_amount = item.amount = flt(item.rate * qty, precision("amount", item));
+				}
+
 				item.item_tax_amount = 0.0;
 				item.total_weight = flt(item.weight_per_unit * item.stock_qty);
 
diff --git a/erpnext/selling/doctype/industry_type/industry_type.json b/erpnext/selling/doctype/industry_type/industry_type.json
index 6c49f0f..3c8ab8e 100644
--- a/erpnext/selling/doctype/industry_type/industry_type.json
+++ b/erpnext/selling/doctype/industry_type/industry_type.json
@@ -1,123 +1,68 @@
 {
- "allow_copy": 0, 
- "allow_import": 1, 
- "allow_rename": 1, 
- "autoname": "field:industry", 
- "beta": 0, 
- "creation": "2012-03-27 14:36:09", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "Setup", 
- "editable_grid": 0, 
+ "actions": [],
+ "allow_import": 1,
+ "allow_rename": 1,
+ "autoname": "field:industry",
+ "creation": "2012-03-27 14:36:09",
+ "doctype": "DocType",
+ "document_type": "Setup",
+ "engine": "InnoDB",
+ "field_order": [
+  "industry"
+ ],
  "fields": [
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "fieldname": "industry", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 0, 
-   "label": "Industry", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "industry", 
-   "oldfieldtype": "Data", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
+   "fieldname": "industry",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Industry",
+   "oldfieldname": "industry",
+   "oldfieldtype": "Data",
+   "reqd": 1,
+   "unique": 1
   }
- ], 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "icon": "fa fa-flag", 
- "idx": 1, 
- "image_view": 0, 
- "in_create": 0, 
-
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 0, 
- "max_attachments": 0, 
- "modified": "2020-09-18 17:26:09.703215", 
- "modified_by": "Administrator", 
- "module": "Selling", 
- "name": "Industry Type", 
- "owner": "Administrator", 
+ ],
+ "icon": "fa fa-flag",
+ "idx": 1,
+ "links": [],
+ "modified": "2023-02-10 03:14:40.735763",
+ "modified_by": "Administrator",
+ "module": "Selling",
+ "name": "Industry Type",
+ "naming_rule": "By fieldname",
+ "owner": "Administrator",
  "permissions": [
   {
-   "amend": 0, 
-   "apply_user_permissions": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 0, 
-   "email": 1, 
-   "export": 0, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "Sales Manager", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
+   "create": 1,
+   "email": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Sales Manager",
+   "share": 1,
    "write": 1
-  }, 
+  },
   {
-   "amend": 0, 
-   "apply_user_permissions": 0, 
-   "cancel": 0, 
-   "create": 0, 
-   "delete": 0, 
-   "email": 1, 
-   "export": 0, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "Sales User", 
-   "set_user_permissions": 0, 
-   "share": 0, 
-   "submit": 0, 
-   "write": 0
-  }, 
+   "email": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Sales User"
+  },
   {
-   "amend": 0, 
-   "apply_user_permissions": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 0, 
-   "email": 1, 
-   "export": 0, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "Sales Master Manager", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
+   "create": 1,
+   "email": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Sales Master Manager",
+   "share": 1,
    "write": 1
   }
- ], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "track_seen": 0
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": [],
+ "translated_doctype": 1
 }
\ No newline at end of file
diff --git a/erpnext/selling/doctype/quotation/quotation.js b/erpnext/selling/doctype/quotation/quotation.js
index 6b42e4d..b348bd3 100644
--- a/erpnext/selling/doctype/quotation/quotation.js
+++ b/erpnext/selling/doctype/quotation/quotation.js
@@ -85,11 +85,15 @@
 		}
 
 		if (doc.docstatus == 1 && !["Lost", "Ordered"].includes(doc.status)) {
-			this.frm.add_custom_button(
-				__("Sales Order"),
-				this.frm.cscript["Make Sales Order"],
-				__("Create")
-			);
+			if (frappe.boot.sysdefaults.allow_sales_order_creation_for_expired_quotation
+				|| (!doc.valid_till)
+				|| frappe.datetime.get_diff(doc.valid_till, frappe.datetime.get_today()) >= 0) {
+					this.frm.add_custom_button(
+						__("Sales Order"),
+						this.frm.cscript["Make Sales Order"],
+						__("Create")
+					);
+				}
 
 			if(doc.status!=="Ordered") {
 				this.frm.add_custom_button(__('Set as Lost'), () => {
diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py
index 6836d56..063813b 100644
--- a/erpnext/selling/doctype/quotation/quotation.py
+++ b/erpnext/selling/doctype/quotation/quotation.py
@@ -195,6 +195,17 @@
 
 @frappe.whitelist()
 def make_sales_order(source_name: str, target_doc=None):
+	if not frappe.db.get_singles_value(
+		"Selling Settings", "allow_sales_order_creation_for_expired_quotation"
+	):
+		quotation = frappe.db.get_value(
+			"Quotation", source_name, ["transaction_date", "valid_till"], as_dict=1
+		)
+		if quotation.valid_till and (
+			quotation.valid_till < quotation.transaction_date or quotation.valid_till < getdate(nowdate())
+		):
+			frappe.throw(_("Validity period of this quotation has ended."))
+
 	return _make_sales_order(source_name, target_doc)
 
 
diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py
index 5aaba4f..cdf5f5d 100644
--- a/erpnext/selling/doctype/quotation/test_quotation.py
+++ b/erpnext/selling/doctype/quotation/test_quotation.py
@@ -144,11 +144,21 @@
 	def test_so_from_expired_quotation(self):
 		from erpnext.selling.doctype.quotation.quotation import make_sales_order
 
+		frappe.db.set_single_value(
+			"Selling Settings", "allow_sales_order_creation_for_expired_quotation", 0
+		)
+
 		quotation = frappe.copy_doc(test_records[0])
 		quotation.valid_till = add_days(nowdate(), -1)
 		quotation.insert()
 		quotation.submit()
 
+		self.assertRaises(frappe.ValidationError, make_sales_order, quotation.name)
+
+		frappe.db.set_single_value(
+			"Selling Settings", "allow_sales_order_creation_for_expired_quotation", 1
+		)
+
 		make_sales_order(quotation.name)
 
 	def test_shopping_cart_without_website_item(self):
diff --git a/erpnext/selling/doctype/sales_partner_type/sales_partner_type.json b/erpnext/selling/doctype/sales_partner_type/sales_partner_type.json
index e7dd0d8..a9b500a 100644
--- a/erpnext/selling/doctype/sales_partner_type/sales_partner_type.json
+++ b/erpnext/selling/doctype/sales_partner_type/sales_partner_type.json
@@ -1,94 +1,47 @@
 {
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "autoname": "field:sales_partner_type", 
- "beta": 0, 
- "creation": "2018-06-11 13:15:57.404716", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
+ "actions": [],
+ "autoname": "field:sales_partner_type",
+ "creation": "2018-06-11 13:15:57.404716",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "sales_partner_type"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "sales_partner_type", 
-   "fieldtype": "Data", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Sales Partner Type", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
+   "fieldname": "sales_partner_type",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Sales Partner Type",
+   "reqd": 1,
+   "unique": 1
   }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 0, 
- "max_attachments": 0, 
- "modified": "2018-06-11 13:45:13.554307", 
- "modified_by": "Administrator", 
- "module": "Selling", 
- "name": "Sales Partner Type", 
- "name_case": "", 
- "owner": "Administrator", 
+ ],
+ "links": [],
+ "modified": "2023-02-10 01:00:20.110800",
+ "modified_by": "Administrator",
+ "module": "Selling",
+ "name": "Sales Partner Type",
+ "naming_rule": "By fieldname",
+ "owner": "Administrator",
  "permissions": [
   {
-   "amend": 0, 
-   "cancel": 0, 
-   "create": 1, 
-   "delete": 1, 
-   "email": 1, 
-   "export": 1, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "System Manager", 
-   "set_user_permissions": 0, 
-   "share": 1, 
-   "submit": 0, 
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
    "write": 1
   }
- ], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 0, 
- "track_seen": 0
+ ],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": [],
+ "translated_doctype": 1
 }
\ No newline at end of file
diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.json b/erpnext/selling/doctype/selling_settings/selling_settings.json
index 2abb169..6ea66a0 100644
--- a/erpnext/selling/doctype/selling_settings/selling_settings.json
+++ b/erpnext/selling/doctype/selling_settings/selling_settings.json
@@ -27,6 +27,7 @@
   "column_break_5",
   "allow_multiple_items",
   "allow_against_multiple_purchase_orders",
+  "allow_sales_order_creation_for_expired_quotation",
   "hide_tax_id",
   "enable_discount_accounting"
  ],
@@ -172,6 +173,12 @@
    "fieldname": "enable_discount_accounting",
    "fieldtype": "Check",
    "label": "Enable Discount Accounting for Selling"
+  },
+  {
+   "default": "0",
+   "fieldname": "allow_sales_order_creation_for_expired_quotation",
+   "fieldtype": "Check",
+   "label": "Allow Sales Order Creation For Expired Quotation"
   }
  ],
  "icon": "fa fa-cog",
@@ -179,7 +186,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2022-05-31 19:39:48.398738",
+ "modified": "2023-02-04 12:37:53.380857",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Selling Settings",
diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js
index 0a356b9..89ce61ab 100644
--- a/erpnext/selling/page/point_of_sale/pos_payment.js
+++ b/erpnext/selling/page/point_of_sale/pos_payment.js
@@ -322,6 +322,11 @@
 		this.focus_on_default_mop();
 	}
 
+	after_render() {
+		const frm = this.events.get_frm();
+		frm.script_manager.trigger("after_payment_render", frm.doc.doctype, frm.doc.docname);
+	}
+
 	edit_cart() {
 		this.events.toggle_other_sections(false);
 		this.toggle_component(false);
@@ -332,6 +337,7 @@
 		this.toggle_component(true);
 
 		this.render_payment_section();
+		this.after_render();
 	}
 
 	toggle_remarks_control() {
diff --git a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.js b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.js
index 991ac71..990d736 100644
--- a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.js
+++ b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.js
@@ -103,6 +103,11 @@
 				return options
 			}
 		},
+		{
+			"fieldname":"only_immediate_upcoming_term",
+			"label": __("Show only the Immediate Upcoming Term"),
+			"fieldtype": "Check",
+		},
 	]
 	return filters;
 }
diff --git a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py
index 8bf5686..3682c5f 100644
--- a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py
+++ b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py
@@ -4,6 +4,7 @@
 import frappe
 from frappe import _, qb, query_builder
 from frappe.query_builder import Criterion, functions
+from frappe.utils.dateutils import getdate
 
 
 def get_columns():
@@ -208,6 +209,7 @@
 		)
 		.where(
 			(so.docstatus == 1)
+			& (so.status.isin(["To Deliver and Bill", "To Bill"]))
 			& (so.payment_terms_template != "NULL")
 			& (so.company == conditions.company)
 			& (so.transaction_date[conditions.start_date : conditions.end_date])
@@ -291,6 +293,18 @@
 	return sales_orders
 
 
+def filter_for_immediate_upcoming_term(filters, sales_orders):
+	if filters.only_immediate_upcoming_term and sales_orders:
+		immediate_term_found = set()
+		filtered_data = []
+		for order in sales_orders:
+			if order.name not in immediate_term_found and order.due_date > getdate():
+				filtered_data.append(order)
+				immediate_term_found.add(order.name)
+		return filtered_data
+	return sales_orders
+
+
 def execute(filters=None):
 	columns = get_columns()
 	sales_orders, so_invoices = get_so_with_invoices(filters)
@@ -298,6 +312,8 @@
 
 	sales_orders = filter_on_calculated_status(filters, sales_orders)
 
+	sales_orders = filter_for_immediate_upcoming_term(filters, sales_orders)
+
 	prepare_chart(sales_orders)
 
 	data = sales_orders
diff --git a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py
index 63d339a..2969123 100644
--- a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py
+++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py
@@ -175,7 +175,9 @@
 				# update existing entry
 				so_row = sales_order_map[so_name]
 				so_row["required_date"] = max(getdate(so_row["delivery_date"]), getdate(row["delivery_date"]))
-				so_row["delay"] = min(so_row["delay"], row["delay"])
+				so_row["delay"] = (
+					min(so_row["delay"], row["delay"]) if row["delay"] and so_row["delay"] else so_row["delay"]
+				)
 
 				# sum numeric columns
 				fields = [
diff --git a/erpnext/setup/doctype/designation/designation.json b/erpnext/setup/doctype/designation/designation.json
index 2cbbb04..a5b2ac9 100644
--- a/erpnext/setup/doctype/designation/designation.json
+++ b/erpnext/setup/doctype/designation/designation.json
@@ -31,7 +31,7 @@
  "icon": "fa fa-bookmark",
  "idx": 1,
  "links": [],
- "modified": "2022-06-28 17:10:26.853753",
+ "modified": "2023-02-10 01:53:41.319386",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "Designation",
@@ -58,5 +58,6 @@
  "show_name_in_global_search": 1,
  "sort_field": "modified",
  "sort_order": "ASC",
- "states": []
+ "states": [],
+ "translated_doctype": 1
 }
\ No newline at end of file
diff --git a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.js b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.js
index 3680906..c3605bf 100644
--- a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.js
+++ b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.js
@@ -1,13 +1,6 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-
-
-//--------- ONLOAD -------------
-cur_frm.cscript.onload = function(doc, cdt, cdn) {
-
-}
-
-cur_frm.cscript.refresh = function(doc, cdt, cdn) {
-
-}
+// frappe.ui.form.on("Terms and Conditions", {
+// 	refresh(frm) {}
+// });
diff --git a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json
index f14b243..f884864 100644
--- a/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json
+++ b/erpnext/setup/doctype/terms_and_conditions/terms_and_conditions.json
@@ -33,7 +33,6 @@
    "default": "0",
    "fieldname": "disabled",
    "fieldtype": "Check",
-   "in_list_view": 1,
    "label": "Disabled"
   },
   {
@@ -60,12 +59,14 @@
    "default": "1",
    "fieldname": "selling",
    "fieldtype": "Check",
+   "in_list_view": 1,
    "label": "Selling"
   },
   {
    "default": "1",
    "fieldname": "buying",
    "fieldtype": "Check",
+   "in_list_view": 1,
    "label": "Buying"
   },
   {
@@ -76,10 +77,11 @@
  "icon": "icon-legal",
  "idx": 1,
  "links": [],
- "modified": "2022-06-16 15:07:38.094844",
+ "modified": "2023-02-01 14:33:39.246532",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "Terms and Conditions",
+ "naming_rule": "By fieldname",
  "owner": "Administrator",
  "permissions": [
   {
@@ -133,5 +135,6 @@
  "quick_entry": 1,
  "show_name_in_global_search": 1,
  "sort_field": "modified",
- "sort_order": "ASC"
+ "sort_order": "ASC",
+ "states": []
 }
\ No newline at end of file
diff --git a/erpnext/setup/setup_wizard/data/designation.txt b/erpnext/setup/setup_wizard/data/designation.txt
new file mode 100644
index 0000000..4c6d7bd
--- /dev/null
+++ b/erpnext/setup/setup_wizard/data/designation.txt
@@ -0,0 +1,31 @@
+Accountant
+Administrative Assistant
+Administrative Officer
+Analyst
+Associate
+Business Analyst
+Business Development Manager
+Consultant
+Chief Executive Officer
+Chief Financial Officer
+Chief Operating Officer
+Chief Technology Officer
+Customer Service Representative
+Designer
+Engineer
+Executive Assistant
+Finance Manager
+HR Manager
+Head of Marketing and Sales
+Manager
+Managing Director
+Marketing Manager
+Marketing Specialist
+President
+Product Manager
+Project Manager
+Researcher
+Sales Representative
+Secretary
+Software Developer
+Vice President
diff --git a/erpnext/setup/setup_wizard/data/industry_type.py b/erpnext/setup/setup_wizard/data/industry_type.py
deleted file mode 100644
index 0bc3f32..0000000
--- a/erpnext/setup/setup_wizard/data/industry_type.py
+++ /dev/null
@@ -1,57 +0,0 @@
-from frappe import _
-
-
-def get_industry_types():
-	return [
-		_("Accounting"),
-		_("Advertising"),
-		_("Aerospace"),
-		_("Agriculture"),
-		_("Airline"),
-		_("Apparel & Accessories"),
-		_("Automotive"),
-		_("Banking"),
-		_("Biotechnology"),
-		_("Broadcasting"),
-		_("Brokerage"),
-		_("Chemical"),
-		_("Computer"),
-		_("Consulting"),
-		_("Consumer Products"),
-		_("Cosmetics"),
-		_("Defense"),
-		_("Department Stores"),
-		_("Education"),
-		_("Electronics"),
-		_("Energy"),
-		_("Entertainment & Leisure"),
-		_("Executive Search"),
-		_("Financial Services"),
-		_("Food, Beverage & Tobacco"),
-		_("Grocery"),
-		_("Health Care"),
-		_("Internet Publishing"),
-		_("Investment Banking"),
-		_("Legal"),
-		_("Manufacturing"),
-		_("Motion Picture & Video"),
-		_("Music"),
-		_("Newspaper Publishers"),
-		_("Online Auctions"),
-		_("Pension Funds"),
-		_("Pharmaceuticals"),
-		_("Private Equity"),
-		_("Publishing"),
-		_("Real Estate"),
-		_("Retail & Wholesale"),
-		_("Securities & Commodity Exchanges"),
-		_("Service"),
-		_("Soap & Detergent"),
-		_("Software"),
-		_("Sports"),
-		_("Technology"),
-		_("Telecommunications"),
-		_("Television"),
-		_("Transportation"),
-		_("Venture Capital"),
-	]
diff --git a/erpnext/setup/setup_wizard/data/industry_type.txt b/erpnext/setup/setup_wizard/data/industry_type.txt
new file mode 100644
index 0000000..eadc689
--- /dev/null
+++ b/erpnext/setup/setup_wizard/data/industry_type.txt
@@ -0,0 +1,51 @@
+Accounting
+Advertising
+Aerospace
+Agriculture
+Airline
+Apparel & Accessories
+Automotive
+Banking
+Biotechnology
+Broadcasting
+Brokerage
+Chemical
+Computer
+Consulting
+Consumer Products
+Cosmetics
+Defense
+Department Stores
+Education
+Electronics
+Energy
+Entertainment & Leisure
+Executive Search
+Financial Services
+Food, Beverage & Tobacco
+Grocery
+Health Care
+Internet Publishing
+Investment Banking
+Legal
+Manufacturing
+Motion Picture & Video
+Music
+Newspaper Publishers
+Online Auctions
+Pension Funds
+Pharmaceuticals
+Private Equity
+Publishing
+Real Estate
+Retail & Wholesale
+Securities & Commodity Exchanges
+Service
+Soap & Detergent
+Software
+Sports
+Technology
+Telecommunications
+Television
+Transportation
+Venture Capital
diff --git a/erpnext/setup/setup_wizard/data/lead_source.txt b/erpnext/setup/setup_wizard/data/lead_source.txt
new file mode 100644
index 0000000..00ca180
--- /dev/null
+++ b/erpnext/setup/setup_wizard/data/lead_source.txt
@@ -0,0 +1,10 @@
+Existing Customer
+Reference
+Advertisement
+Cold Calling
+Exhibition
+Supplier Reference
+Mass Mailing
+Customer's Vendor
+Campaign
+Walk In
diff --git a/erpnext/setup/setup_wizard/data/sales_partner_type.txt b/erpnext/setup/setup_wizard/data/sales_partner_type.txt
new file mode 100644
index 0000000..68e9b9a
--- /dev/null
+++ b/erpnext/setup/setup_wizard/data/sales_partner_type.txt
@@ -0,0 +1,7 @@
+Channel Partner
+Distributor
+Dealer
+Agent
+Retailer
+Implementation Partner
+Reseller
diff --git a/erpnext/setup/setup_wizard/data/sales_stage.txt b/erpnext/setup/setup_wizard/data/sales_stage.txt
new file mode 100644
index 0000000..2808ce7
--- /dev/null
+++ b/erpnext/setup/setup_wizard/data/sales_stage.txt
@@ -0,0 +1,8 @@
+Prospecting
+Qualification
+Needs Analysis
+Value Proposition
+Identifying Decision Makers
+Perception Analysis
+Proposal/Price Quote
+Negotiation/Review
diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py
index 4d9b871..6bc1771 100644
--- a/erpnext/setup/setup_wizard/operations/install_fixtures.py
+++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py
@@ -4,6 +4,7 @@
 
 import json
 import os
+from pathlib import Path
 
 import frappe
 from frappe import _
@@ -16,28 +17,10 @@
 from erpnext.accounts.doctype.account.account import RootNotEditable
 from erpnext.regional.address_template.setup import set_up_address_templates
 
-default_lead_sources = [
-	"Existing Customer",
-	"Reference",
-	"Advertisement",
-	"Cold Calling",
-	"Exhibition",
-	"Supplier Reference",
-	"Mass Mailing",
-	"Customer's Vendor",
-	"Campaign",
-	"Walk In",
-]
 
-default_sales_partner_type = [
-	"Channel Partner",
-	"Distributor",
-	"Dealer",
-	"Agent",
-	"Retailer",
-	"Implementation Partner",
-	"Reseller",
-]
+def read_lines(filename: str) -> list[str]:
+	"""Return a list of lines from a file in the data directory."""
+	return (Path(__file__).parent.parent / "data" / filename).read_text().splitlines()
 
 
 def install(country=None):
@@ -85,7 +68,11 @@
 		# Stock Entry Type
 		{"doctype": "Stock Entry Type", "name": "Material Issue", "purpose": "Material Issue"},
 		{"doctype": "Stock Entry Type", "name": "Material Receipt", "purpose": "Material Receipt"},
-		{"doctype": "Stock Entry Type", "name": "Material Transfer", "purpose": "Material Transfer"},
+		{
+			"doctype": "Stock Entry Type",
+			"name": "Material Transfer",
+			"purpose": "Material Transfer",
+		},
 		{"doctype": "Stock Entry Type", "name": "Manufacture", "purpose": "Manufacture"},
 		{"doctype": "Stock Entry Type", "name": "Repack", "purpose": "Repack"},
 		{
@@ -103,22 +90,6 @@
 			"name": "Material Consumption for Manufacture",
 			"purpose": "Material Consumption for Manufacture",
 		},
-		# Designation
-		{"doctype": "Designation", "designation_name": _("CEO")},
-		{"doctype": "Designation", "designation_name": _("Manager")},
-		{"doctype": "Designation", "designation_name": _("Analyst")},
-		{"doctype": "Designation", "designation_name": _("Engineer")},
-		{"doctype": "Designation", "designation_name": _("Accountant")},
-		{"doctype": "Designation", "designation_name": _("Secretary")},
-		{"doctype": "Designation", "designation_name": _("Associate")},
-		{"doctype": "Designation", "designation_name": _("Administrative Officer")},
-		{"doctype": "Designation", "designation_name": _("Business Development Manager")},
-		{"doctype": "Designation", "designation_name": _("HR Manager")},
-		{"doctype": "Designation", "designation_name": _("Project Manager")},
-		{"doctype": "Designation", "designation_name": _("Head of Marketing and Sales")},
-		{"doctype": "Designation", "designation_name": _("Software Developer")},
-		{"doctype": "Designation", "designation_name": _("Designer")},
-		{"doctype": "Designation", "designation_name": _("Researcher")},
 		# territory: with two default territories, one for home country and one named Rest of the World
 		{
 			"doctype": "Territory",
@@ -291,28 +262,18 @@
 		{"doctype": "Market Segment", "market_segment": _("Lower Income")},
 		{"doctype": "Market Segment", "market_segment": _("Middle Income")},
 		{"doctype": "Market Segment", "market_segment": _("Upper Income")},
-		# Sales Stages
-		{"doctype": "Sales Stage", "stage_name": _("Prospecting")},
-		{"doctype": "Sales Stage", "stage_name": _("Qualification")},
-		{"doctype": "Sales Stage", "stage_name": _("Needs Analysis")},
-		{"doctype": "Sales Stage", "stage_name": _("Value Proposition")},
-		{"doctype": "Sales Stage", "stage_name": _("Identifying Decision Makers")},
-		{"doctype": "Sales Stage", "stage_name": _("Perception Analysis")},
-		{"doctype": "Sales Stage", "stage_name": _("Proposal/Price Quote")},
-		{"doctype": "Sales Stage", "stage_name": _("Negotiation/Review")},
 		# Warehouse Type
 		{"doctype": "Warehouse Type", "name": "Transit"},
 	]
 
-	from erpnext.setup.setup_wizard.data.industry_type import get_industry_types
-
-	records += [{"doctype": "Industry Type", "industry": d} for d in get_industry_types()]
-	# records += [{"doctype":"Operation", "operation": d} for d in get_operations()]
-	records += [{"doctype": "Lead Source", "source_name": _(d)} for d in default_lead_sources]
-
-	records += [
-		{"doctype": "Sales Partner Type", "sales_partner_type": _(d)} for d in default_sales_partner_type
-	]
+	for doctype, title_field, filename in (
+		("Designation", "designation_name", "designation.txt"),
+		("Sales Stage", "stage_name", "sales_stage.txt"),
+		("Industry Type", "industry", "industry_type.txt"),
+		("Lead Source", "source_name", "lead_source.txt"),
+		("Sales Partner Type", "sales_partner_type", "sales_partner_type.txt"),
+	):
+		records += [{"doctype": doctype, title_field: title} for title in read_lines(filename)]
 
 	base_path = frappe.get_app_path("erpnext", "stock", "doctype")
 	response = frappe.read_file(
@@ -335,16 +296,11 @@
 	make_default_records()
 	make_records(records)
 	set_up_address_templates(default_country=country)
-	set_more_defaults()
-	update_global_search_doctypes()
-
-
-def set_more_defaults():
-	# Do more setup stuff that can be done here with no dependencies
 	update_selling_defaults()
 	update_buying_defaults()
 	add_uom_data()
 	update_item_variant_settings()
+	update_global_search_doctypes()
 
 
 def update_selling_defaults():
@@ -381,7 +337,7 @@
 	)
 	for d in uoms:
 		if not frappe.db.exists("UOM", _(d.get("uom_name"))):
-			uom_doc = frappe.get_doc(
+			frappe.get_doc(
 				{
 					"doctype": "UOM",
 					"uom_name": _(d.get("uom_name")),
@@ -402,9 +358,10 @@
 			frappe.get_doc({"doctype": "UOM Category", "category_name": _(d.get("category"))}).db_insert()
 
 		if not frappe.db.exists(
-			"UOM Conversion Factor", {"from_uom": _(d.get("from_uom")), "to_uom": _(d.get("to_uom"))}
+			"UOM Conversion Factor",
+			{"from_uom": _(d.get("from_uom")), "to_uom": _(d.get("to_uom"))},
 		):
-			uom_conversion = frappe.get_doc(
+			frappe.get_doc(
 				{
 					"doctype": "UOM Conversion Factor",
 					"category": _(d.get("category")),
@@ -412,7 +369,7 @@
 					"to_uom": _(d.get("to_uom")),
 					"value": d.get("value"),
 				}
-			).insert(ignore_permissions=True)
+			).db_insert()
 
 
 def add_market_segments():
@@ -468,7 +425,7 @@
 	make_records(records)
 
 
-def install_defaults(args=None):
+def install_defaults(args=None):  # nosemgrep
 	records = [
 		# Price Lists
 		{
@@ -493,7 +450,7 @@
 
 	# enable default currency
 	frappe.db.set_value("Currency", args.get("currency"), "enabled", 1)
-	frappe.db.set_value("Stock Settings", None, "email_footer_address", args.get("company_name"))
+	frappe.db.set_single_value("Stock Settings", "email_footer_address", args.get("company_name"))
 
 	set_global_defaults(args)
 	update_stock_settings()
@@ -540,7 +497,8 @@
 
 	company_name = args.get("company_name")
 	bank_account_group = frappe.db.get_value(
-		"Account", {"account_type": "Bank", "is_group": 1, "root_type": "Asset", "company": company_name}
+		"Account",
+		{"account_type": "Bank", "is_group": 1, "root_type": "Asset", "company": company_name},
 	)
 	if bank_account_group:
 		bank_account = frappe.get_doc(
diff --git a/erpnext/setup/setup_wizard/operations/taxes_setup.py b/erpnext/setup/setup_wizard/operations/taxes_setup.py
index 2f77dd6..49ba78c 100644
--- a/erpnext/setup/setup_wizard/operations/taxes_setup.py
+++ b/erpnext/setup/setup_wizard/operations/taxes_setup.py
@@ -158,6 +158,7 @@
 	# Ingone validations to make doctypes faster
 	doc.flags.ignore_links = True
 	doc.flags.ignore_validate = True
+	doc.flags.ignore_mandatory = True
 	doc.insert(ignore_permissions=True)
 	return doc
 
diff --git a/erpnext/startup/boot.py b/erpnext/startup/boot.py
index bb120ea..62936fc 100644
--- a/erpnext/startup/boot.py
+++ b/erpnext/startup/boot.py
@@ -25,6 +25,12 @@
 			frappe.db.get_single_value("CRM Settings", "default_valid_till")
 		)
 
+		bootinfo.sysdefaults.allow_sales_order_creation_for_expired_quotation = cint(
+			frappe.db.get_single_value(
+				"Selling Settings", "allow_sales_order_creation_for_expired_quotation"
+			)
+		)
+
 		# if no company, show a dialog box to create a new company
 		bootinfo.customer_count = frappe.db.sql("""SELECT count(*) FROM `tabCustomer`""")[0][0]
 
diff --git a/erpnext/stock/doctype/item_price/test_records.json b/erpnext/stock/doctype/item_price/test_records.json
index 0a3d7e8..afe5ad6 100644
--- a/erpnext/stock/doctype/item_price/test_records.json
+++ b/erpnext/stock/doctype/item_price/test_records.json
@@ -38,5 +38,19 @@
   "price_list_rate": 1000,
   "valid_from": "2017-04-10",
   "valid_upto": "2017-04-17"
+ },
+ {
+  "doctype": "Item Price",
+  "item_code": "_Test Item",
+  "price_list": "_Test Buying Price List",
+  "price_list_rate": 100,
+  "supplier": "_Test Supplier"
+ },
+ {
+  "doctype": "Item Price",
+  "item_code": "_Test Item",
+  "price_list": "_Test Selling Price List",
+  "price_list_rate": 200,
+  "customer": "_Test Customer"
  }
 ]
diff --git a/erpnext/stock/doctype/price_list/test_records.json b/erpnext/stock/doctype/price_list/test_records.json
index 7ca949c..e02a7ad 100644
--- a/erpnext/stock/doctype/price_list/test_records.json
+++ b/erpnext/stock/doctype/price_list/test_records.json
@@ -31,5 +31,21 @@
         "enabled": 1,
         "price_list_name": "_Test Price List Rest of the World",
         "selling": 1
+    },
+    {
+        "buying": 0,
+        "currency": "USD",
+        "doctype": "Price List",
+        "enabled": 1,
+        "price_list_name": "_Test Selling Price List",
+        "selling": 1
+    },
+    {
+        "buying": 1,
+        "currency": "USD",
+        "doctype": "Price List",
+        "enabled": 1,
+        "price_list_name": "_Test Buying Price List",
+        "selling": 0
     }
 ]
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 8c20ca0..7f69397 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -2494,7 +2494,7 @@
 
 	if not conversion_factor:
 		frappe.msgprint(
-			_("UOM coversion factor required for UOM: {0} in Item: {1}").format(uom, item_code)
+			_("UOM conversion factor required for UOM: {0} in Item: {1}").format(uom, item_code)
 		)
 		ret = {"uom": ""}
 	else:
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 5af1441..b53f429 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -88,8 +88,15 @@
 
 	update_party_blanket_order(args, out)
 
+	# Never try to find a customer price if customer is set in these Doctype
+	current_customer = args.customer
+	if args.get("doctype") in ["Purchase Order", "Purchase Receipt", "Purchase Invoice"]:
+		args.customer = None
+
 	out.update(get_price_list_rate(args, item))
 
+	args.customer = current_customer
+
 	if args.customer and cint(args.is_pos):
 		out.update(get_pos_profile_item_details(args.company, args, update_data=True))
 
diff --git a/erpnext/stock/stock_balance.py b/erpnext/stock/stock_balance.py
index 14cedd2..439ed7a 100644
--- a/erpnext/stock/stock_balance.py
+++ b/erpnext/stock/stock_balance.py
@@ -121,7 +121,7 @@
 					and parenttype='Sales Order'
 					and item_code != parent_item
 					and exists (select * from `tabSales Order` so
-					where name = dnpi_in.parent and docstatus = 1 and status != 'Closed')
+					where name = dnpi_in.parent and docstatus = 1 and status not in ('On Hold', 'Closed'))
 				) dnpi)
 			union
 				(select stock_qty as dnpi_qty, qty as so_item_qty,
@@ -131,7 +131,7 @@
 				and (so_item.delivered_by_supplier is null or so_item.delivered_by_supplier = 0)
 				and exists(select * from `tabSales Order` so
 					where so.name = so_item.parent and so.docstatus = 1
-					and so.status != 'Closed'))
+					and so.status not in ('On Hold', 'Closed')))
 			) tab
 		where
 			so_item_qty >= so_item_delivered_qty
diff --git a/erpnext/stock/tests/test_get_item_details.py b/erpnext/stock/tests/test_get_item_details.py
new file mode 100644
index 0000000..b53e29e
--- /dev/null
+++ b/erpnext/stock/tests/test_get_item_details.py
@@ -0,0 +1,40 @@
+import json
+
+import frappe
+from frappe.test_runner import make_test_records
+from frappe.tests.utils import FrappeTestCase
+
+from erpnext.stock.get_item_details import get_item_details
+
+test_ignore = ["BOM"]
+test_dependencies = ["Customer", "Supplier", "Item", "Price List", "Item Price"]
+
+
+class TestGetItemDetail(FrappeTestCase):
+	def setUp(self):
+		make_test_records("Price List")
+		super().setUp()
+
+	def test_get_item_detail_purchase_order(self):
+
+		args = frappe._dict(
+			{
+				"item_code": "_Test Item",
+				"company": "_Test Company",
+				"customer": "_Test Customer",
+				"conversion_rate": 1.0,
+				"price_list_currency": "USD",
+				"plc_conversion_rate": 1.0,
+				"doctype": "Purchase Order",
+				"name": None,
+				"supplier": "_Test Supplier",
+				"transaction_date": None,
+				"conversion_rate": 1.0,
+				"price_list": "_Test Buying Price List",
+				"is_subcontracted": 0,
+				"ignore_pricing_rule": 1,
+				"qty": 1,
+			}
+		)
+		details = get_item_details(args)
+		self.assertEqual(details.get("price_list_rate"), 100)
diff --git a/erpnext/www/shop-by-category/index.py b/erpnext/www/shop-by-category/index.py
index 8a92418..219747c 100644
--- a/erpnext/www/shop-by-category/index.py
+++ b/erpnext/www/shop-by-category/index.py
@@ -51,21 +51,31 @@
 	return tab_values
 
 
-def get_category_records(categories):
+def get_category_records(categories: list):
 	categorical_data = {}
-	for category in categories:
-		if category == "item_group":
+
+	for c in categories:
+		if c == "item_group":
 			categorical_data["item_group"] = frappe.db.get_all(
 				"Item Group",
 				filters={"parent_item_group": "All Item Groups", "show_in_website": 1},
 				fields=["name", "parent_item_group", "is_group", "image", "route"],
 			)
-		else:
-			doctype = frappe.unscrub(category)
-			fields = ["name"]
-			if frappe.get_meta(doctype, cached=True).get_field("image"):
+
+			continue
+
+		doctype = frappe.unscrub(c)
+		fields = ["name"]
+
+		try:
+			meta = frappe.get_meta(doctype, cached=True)
+			if meta.get_field("image"):
 				fields += ["image"]
 
-			categorical_data[category] = frappe.db.get_all(doctype, fields=fields)
+			data = frappe.db.get_all(doctype, fields=fields)
+			categorical_data[c] = data
+		except BaseException:
+			frappe.throw(_("DocType {} not found").format(doctype))
+			continue
 
 	return categorical_data