Merge pull request #33684 from rohitwaghchaure/fixed-work-order-summary

feat: [minor] date type based on filter in Work Order Summary report
diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js
index 28e79b5..c083189 100644
--- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js
+++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js
@@ -21,13 +21,22 @@
 		frm.trigger('bank_account');
 	},
 
+	filter_by_reference_date: function (frm) {
+		if (frm.doc.filter_by_reference_date) {
+			frm.set_value("bank_statement_from_date", "");
+			frm.set_value("bank_statement_to_date", "");
+		} else {
+			frm.set_value("from_reference_date", "");
+			frm.set_value("to_reference_date", "");
+		}
+	},
+
 	refresh: function (frm) {
 		frappe.require("bank-reconciliation-tool.bundle.js", () =>
 			frm.trigger("make_reconciliation_tool")
 		);
-		frm.upload_statement_button = frm.page.set_secondary_action(
-			__("Upload Bank Statement"),
-			() =>
+
+		frm.add_custom_button(__("Upload Bank Statement"), () =>
 				frappe.call({
 					method:
 						"erpnext.accounts.doctype.bank_statement_import.bank_statement_import.upload_bank_statement",
@@ -49,6 +58,20 @@
 					},
 				})
 		);
+
+		frm.add_custom_button(__('Auto Reconcile'), function() {
+			frappe.call({
+				method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.auto_reconcile_vouchers",
+				args: {
+					bank_account: frm.doc.bank_account,
+					from_date: frm.doc.bank_statement_from_date,
+					to_date: frm.doc.bank_statement_to_date,
+					filter_by_reference_date: frm.doc.filter_by_reference_date,
+					from_reference_date: frm.doc.from_reference_date,
+					to_reference_date: frm.doc.to_reference_date,
+				},
+			})
+		});
 	},
 
 	after_save: function (frm) {
@@ -160,6 +183,9 @@
 					).$wrapper,
 					bank_statement_from_date: frm.doc.bank_statement_from_date,
 					bank_statement_to_date: frm.doc.bank_statement_to_date,
+					filter_by_reference_date: frm.doc.filter_by_reference_date,
+					from_reference_date: frm.doc.from_reference_date,
+					to_reference_date: frm.doc.to_reference_date,
 					bank_statement_closing_balance:
 						frm.doc.bank_statement_closing_balance,
 					cards_manager: frm.cards_manager,
diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json
index f666101..80993d6 100644
--- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json
+++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.json
@@ -10,6 +10,9 @@
   "column_break_1",
   "bank_statement_from_date",
   "bank_statement_to_date",
+  "from_reference_date",
+  "to_reference_date",
+  "filter_by_reference_date",
   "column_break_2",
   "account_opening_balance",
   "bank_statement_closing_balance",
@@ -36,13 +39,13 @@
    "fieldtype": "Column Break"
   },
   {
-   "depends_on": "eval: doc.bank_account",
+   "depends_on": "eval: doc.bank_account && !doc.filter_by_reference_date",
    "fieldname": "bank_statement_from_date",
    "fieldtype": "Date",
    "label": "From Date"
   },
   {
-   "depends_on": "eval: doc.bank_statement_from_date",
+   "depends_on": "eval: doc.bank_account && !doc.filter_by_reference_date",
    "fieldname": "bank_statement_to_date",
    "fieldtype": "Date",
    "label": "To Date"
@@ -81,14 +84,33 @@
   },
   {
    "fieldname": "no_bank_transactions",
-   "fieldtype": "HTML"
+   "fieldtype": "HTML",
+   "options": "<div class=\"text-muted text-center\">No Matching Bank Transactions Found</div>"
+  },
+  {
+   "depends_on": "eval:doc.filter_by_reference_date",
+   "fieldname": "from_reference_date",
+   "fieldtype": "Date",
+   "label": "From Reference Date"
+  },
+  {
+   "depends_on": "eval:doc.filter_by_reference_date",
+   "fieldname": "to_reference_date",
+   "fieldtype": "Date",
+   "label": "To Reference Date"
+  },
+  {
+   "default": "0",
+   "fieldname": "filter_by_reference_date",
+   "fieldtype": "Check",
+   "label": "Filter by Reference Date"
   }
  ],
  "hide_toolbar": 1,
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2021-04-21 11:13:49.831769",
+ "modified": "2023-01-13 13:00:02.022919",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Bank Reconciliation Tool",
@@ -107,5 +129,6 @@
  ],
  "quick_entry": 1,
  "sort_field": "modified",
- "sort_order": "DESC"
-}
+ "sort_order": "DESC",
+ "states": []
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
index f5f04ae..4ba6146 100644
--- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
+++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.py
@@ -8,7 +8,7 @@
 from frappe import _
 from frappe.model.document import Document
 from frappe.query_builder.custom import ConstantColumn
-from frappe.utils import flt
+from frappe.utils import cint, flt
 
 from erpnext.accounts.doctype.bank_transaction.bank_transaction import get_paid_amount
 from erpnext.accounts.report.bank_reconciliation_statement.bank_reconciliation_statement import (
@@ -50,6 +50,7 @@
 			"party",
 		],
 		filters=filters,
+		order_by="date",
 	)
 	return transactions
 
@@ -266,6 +267,80 @@
 
 
 @frappe.whitelist()
+def auto_reconcile_vouchers(
+	bank_account,
+	from_date=None,
+	to_date=None,
+	filter_by_reference_date=None,
+	from_reference_date=None,
+	to_reference_date=None,
+):
+	frappe.flags.auto_reconcile_vouchers = True
+	document_types = ["payment_entry", "journal_entry"]
+	bank_transactions = get_bank_transactions(bank_account)
+	matched_transaction = []
+	for transaction in bank_transactions:
+		linked_payments = get_linked_payments(
+			transaction.name,
+			document_types,
+			from_date,
+			to_date,
+			filter_by_reference_date,
+			from_reference_date,
+			to_reference_date,
+		)
+		vouchers = []
+		for r in linked_payments:
+			vouchers.append(
+				{
+					"payment_doctype": r[1],
+					"payment_name": r[2],
+					"amount": r[4],
+				}
+			)
+		transaction = frappe.get_doc("Bank Transaction", transaction.name)
+		account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
+		matched_trans = 0
+		for voucher in vouchers:
+			gl_entry = frappe.db.get_value(
+				"GL Entry",
+				dict(
+					account=account, voucher_type=voucher["payment_doctype"], voucher_no=voucher["payment_name"]
+				),
+				["credit", "debit"],
+				as_dict=1,
+			)
+			gl_amount, transaction_amount = (
+				(gl_entry.credit, transaction.deposit)
+				if gl_entry.credit > 0
+				else (gl_entry.debit, transaction.withdrawal)
+			)
+			allocated_amount = gl_amount if gl_amount >= transaction_amount else transaction_amount
+			transaction.append(
+				"payment_entries",
+				{
+					"payment_document": voucher["payment_doctype"],
+					"payment_entry": voucher["payment_name"],
+					"allocated_amount": allocated_amount,
+				},
+			)
+			matched_transaction.append(str(transaction.name))
+		transaction.save()
+		transaction.update_allocations()
+	matched_transaction_len = len(set(matched_transaction))
+	if matched_transaction_len == 0:
+		frappe.msgprint(_("No matching references found for auto reconciliation"))
+	elif matched_transaction_len == 1:
+		frappe.msgprint(_("{0} transaction is reconcilied").format(matched_transaction_len))
+	else:
+		frappe.msgprint(_("{0} transactions are reconcilied").format(matched_transaction_len))
+
+	frappe.flags.auto_reconcile_vouchers = False
+
+	return frappe.get_doc("Bank Transaction", transaction.name)
+
+
+@frappe.whitelist()
 def reconcile_vouchers(bank_transaction_name, vouchers):
 	# updated clear date of all the vouchers based on the bank transaction
 	vouchers = json.loads(vouchers)
@@ -327,20 +402,58 @@
 
 
 @frappe.whitelist()
-def get_linked_payments(bank_transaction_name, document_types=None):
+def get_linked_payments(
+	bank_transaction_name,
+	document_types=None,
+	from_date=None,
+	to_date=None,
+	filter_by_reference_date=None,
+	from_reference_date=None,
+	to_reference_date=None,
+):
 	# get all matching payments for a bank transaction
 	transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
 	bank_account = frappe.db.get_values(
 		"Bank Account", transaction.bank_account, ["account", "company"], as_dict=True
 	)[0]
 	(account, company) = (bank_account.account, bank_account.company)
-	matching = check_matching(account, company, transaction, document_types)
+	matching = check_matching(
+		account,
+		company,
+		transaction,
+		document_types,
+		from_date,
+		to_date,
+		filter_by_reference_date,
+		from_reference_date,
+		to_reference_date,
+	)
 	return matching
 
 
-def check_matching(bank_account, company, transaction, document_types):
+def check_matching(
+	bank_account,
+	company,
+	transaction,
+	document_types,
+	from_date,
+	to_date,
+	filter_by_reference_date,
+	from_reference_date,
+	to_reference_date,
+):
 	# combine all types of vouchers
-	subquery = get_queries(bank_account, company, transaction, document_types)
+	subquery = get_queries(
+		bank_account,
+		company,
+		transaction,
+		document_types,
+		from_date,
+		to_date,
+		filter_by_reference_date,
+		from_reference_date,
+		to_reference_date,
+	)
 	filters = {
 		"amount": transaction.unallocated_amount,
 		"payment_type": "Receive" if transaction.deposit > 0 else "Pay",
@@ -361,11 +474,20 @@
 				filters,
 			)
 		)
-
 	return sorted(matching_vouchers, key=lambda x: x[0], reverse=True) if matching_vouchers else []
 
 
-def get_queries(bank_account, company, transaction, document_types):
+def get_queries(
+	bank_account,
+	company,
+	transaction,
+	document_types,
+	from_date,
+	to_date,
+	filter_by_reference_date,
+	from_reference_date,
+	to_reference_date,
+):
 	# get queries to get matching vouchers
 	amount_condition = "=" if "exact_match" in document_types else "<="
 	account_from_to = "paid_to" if transaction.deposit > 0 else "paid_from"
@@ -381,6 +503,11 @@
 				document_types,
 				amount_condition,
 				account_from_to,
+				from_date,
+				to_date,
+				filter_by_reference_date,
+				from_reference_date,
+				to_reference_date,
 			)
 			or []
 		)
@@ -389,15 +516,42 @@
 
 
 def get_matching_queries(
-	bank_account, company, transaction, document_types, amount_condition, account_from_to
+	bank_account,
+	company,
+	transaction,
+	document_types,
+	amount_condition,
+	account_from_to,
+	from_date,
+	to_date,
+	filter_by_reference_date,
+	from_reference_date,
+	to_reference_date,
 ):
 	queries = []
 	if "payment_entry" in document_types:
-		pe_amount_matching = get_pe_matching_query(amount_condition, account_from_to, transaction)
+		pe_amount_matching = get_pe_matching_query(
+			amount_condition,
+			account_from_to,
+			transaction,
+			from_date,
+			to_date,
+			filter_by_reference_date,
+			from_reference_date,
+			to_reference_date,
+		)
 		queries.extend([pe_amount_matching])
 
 	if "journal_entry" in document_types:
-		je_amount_matching = get_je_matching_query(amount_condition, transaction)
+		je_amount_matching = get_je_matching_query(
+			amount_condition,
+			transaction,
+			from_date,
+			to_date,
+			filter_by_reference_date,
+			from_reference_date,
+			to_reference_date,
+		)
 		queries.extend([je_amount_matching])
 
 	if transaction.deposit > 0 and "sales_invoice" in document_types:
@@ -504,47 +658,81 @@
 	return vouchers
 
 
-def get_pe_matching_query(amount_condition, account_from_to, transaction):
+def get_pe_matching_query(
+	amount_condition,
+	account_from_to,
+	transaction,
+	from_date,
+	to_date,
+	filter_by_reference_date,
+	from_reference_date,
+	to_reference_date,
+):
 	# get matching payment entries query
 	if transaction.deposit > 0:
 		currency_field = "paid_to_account_currency as currency"
 	else:
 		currency_field = "paid_from_account_currency as currency"
+	filter_by_date = f"AND posting_date between '{from_date}' and '{to_date}'"
+	order_by = " posting_date"
+	filter_by_reference_no = ""
+	if cint(filter_by_reference_date):
+		filter_by_date = f"AND reference_date between '{from_reference_date}' and '{to_reference_date}'"
+		order_by = " reference_date"
+	if frappe.flags.auto_reconcile_vouchers == True:
+		filter_by_reference_no = f"AND reference_no = '{transaction.reference_number}'"
 	return f"""
-	SELECT
-		(CASE WHEN reference_no=%(reference_no)s THEN 1 ELSE 0 END
-		+ CASE WHEN (party_type = %(party_type)s AND party = %(party)s ) THEN 1 ELSE 0  END
-		+ 1 ) AS rank,
-		'Payment Entry' as doctype,
-		name,
-		paid_amount,
-		reference_no,
-		reference_date,
-		party,
-		party_type,
-		posting_date,
-		{currency_field}
-	FROM
-		`tabPayment Entry`
-	WHERE
-		paid_amount {amount_condition} %(amount)s
-		AND docstatus = 1
-		AND payment_type IN (%(payment_type)s, 'Internal Transfer')
-		AND ifnull(clearance_date, '') = ""
-		AND {account_from_to} = %(bank_account)s
+		SELECT
+			(CASE WHEN reference_no=%(reference_no)s THEN 1 ELSE 0 END
+			+ CASE WHEN (party_type = %(party_type)s AND party = %(party)s ) THEN 1 ELSE 0  END
+			+ 1 ) AS rank,
+			'Payment Entry' as doctype,
+			name,
+			paid_amount,
+			reference_no,
+			reference_date,
+			party,
+			party_type,
+			posting_date,
+			{currency_field}
+		FROM
+			`tabPayment Entry`
+		WHERE
+			paid_amount {amount_condition} %(amount)s
+			AND docstatus = 1
+			AND payment_type IN (%(payment_type)s, 'Internal Transfer')
+			AND ifnull(clearance_date, '') = ""
+			AND {account_from_to} = %(bank_account)s
+			{filter_by_date}
+			{filter_by_reference_no}
+		order by{order_by}
+
 	"""
 
 
-def get_je_matching_query(amount_condition, transaction):
+def get_je_matching_query(
+	amount_condition,
+	transaction,
+	from_date,
+	to_date,
+	filter_by_reference_date,
+	from_reference_date,
+	to_reference_date,
+):
 	# get matching journal entry query
-
 	# We have mapping at the bank level
 	# So one bank could have both types of bank accounts like asset and liability
 	# So cr_or_dr should be judged only on basis of withdrawal and deposit and not account type
 	cr_or_dr = "credit" if transaction.withdrawal > 0 else "debit"
-
+	filter_by_date = f"AND je.posting_date between '{from_date}' and '{to_date}'"
+	order_by = " je.posting_date"
+	filter_by_reference_no = ""
+	if cint(filter_by_reference_date):
+		filter_by_date = f"AND je.cheque_date between '{from_reference_date}' and '{to_reference_date}'"
+		order_by = " je.cheque_date"
+	if frappe.flags.auto_reconcile_vouchers == True:
+		filter_by_reference_no = f"AND je.cheque_no = '{transaction.reference_number}'"
 	return f"""
-
 		SELECT
 			(CASE WHEN je.cheque_no=%(reference_no)s THEN 1 ELSE 0 END
 			+ 1) AS rank ,
@@ -568,6 +756,9 @@
 			AND jea.account = %(bank_account)s
 			AND jea.{cr_or_dr}_in_account_currency {amount_condition} %(amount)s
 			AND je.docstatus = 1
+			{filter_by_date}
+			{filter_by_reference_no}
+			order by {order_by}
 	"""
 
 
diff --git a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
index a5d0413..f900e07 100644
--- a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
+++ b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
@@ -5,6 +5,7 @@
 import unittest
 
 import frappe
+from frappe import utils
 from frappe.tests.utils import FrappeTestCase
 
 from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import (
@@ -40,7 +41,12 @@
 			"Bank Transaction",
 			dict(description="Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic"),
 		)
-		linked_payments = get_linked_payments(bank_transaction.name, ["payment_entry", "exact_match"])
+		linked_payments = get_linked_payments(
+			bank_transaction.name,
+			["payment_entry", "exact_match"],
+			from_date=bank_transaction.date,
+			to_date=utils.today(),
+		)
 		self.assertTrue(linked_payments[0][6] == "Conrad Electronic")
 
 	# This test validates a simple reconciliation leading to the clearance of the bank transaction and the payment
@@ -81,7 +87,12 @@
 			"Bank Transaction",
 			dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07"),
 		)
-		linked_payments = get_linked_payments(bank_transaction.name, ["payment_entry", "exact_match"])
+		linked_payments = get_linked_payments(
+			bank_transaction.name,
+			["payment_entry", "exact_match"],
+			from_date=bank_transaction.date,
+			to_date=utils.today(),
+		)
 		self.assertTrue(linked_payments[0][3])
 
 	# Check error if already reconciled
diff --git a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json
index 7921fcc..c62b711 100644
--- a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json
+++ b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.json
@@ -6,6 +6,7 @@
  "engine": "InnoDB",
  "field_order": [
   "api_details_section",
+  "disabled",
   "service_provider",
   "api_endpoint",
   "url",
@@ -77,12 +78,18 @@
    "label": "Service Provider",
    "options": "frankfurter.app\nexchangerate.host\nCustom",
    "reqd": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "disabled",
+   "fieldtype": "Check",
+   "label": "Disabled"
   }
  ],
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2022-01-10 15:51:14.521174",
+ "modified": "2023-01-09 12:19:03.955906",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Currency Exchange Settings",
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index ff2690f..1cccbd9 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -247,7 +247,7 @@
 		self.set_target_exchange_rate(ref_doc)
 
 	def set_source_exchange_rate(self, ref_doc=None):
-		if self.paid_from and not self.source_exchange_rate:
+		if self.paid_from:
 			if self.paid_from_account_currency == self.company_currency:
 				self.source_exchange_rate = 1
 			else:
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
index ac033f7..13712ce 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
@@ -471,6 +471,7 @@
 
 	def build_qb_filter_conditions(self, get_invoices=False, get_return_invoices=False):
 		self.common_filter_conditions.clear()
+		self.accounting_dimension_filter_conditions.clear()
 		self.ple_posting_date_filter.clear()
 		ple = qb.DocType("Payment Ledger Entry")
 
diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html
index 0da44a4..3920d4c 100644
--- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html
+++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.html
@@ -49,7 +49,6 @@
 						<br>
 					{% endif %}
 
-					{{ _("Against") }}: {{ row.against }}
 					<br>{{ _("Remarks") }}: {{ row.remarks }}
 					{% if row.bill_no %}
 						<br>{{ _("Supplier Invoice No") }}: {{ row.bill_no }}
diff --git a/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.py b/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.py
index 9f6828f..209cad4 100644
--- a/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.py
+++ b/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.py
@@ -7,7 +7,6 @@
 from frappe import _, qb
 from frappe.model.document import Document
 from frappe.query_builder.custom import ConstantColumn
-from frappe.utils.background_jobs import is_job_queued
 
 from erpnext.accounts.utils import _delete_pl_entries, create_payment_ledger_entry
 
@@ -27,7 +26,7 @@
 	"""
 	if docname:
 		repost_doc = frappe.get_doc("Repost Payment Ledger", docname)
-		if repost_doc.docstatus == 1 and repost_doc.repost_status in ["Queued", "Failed"]:
+		if repost_doc.docstatus.is_submitted() and repost_doc.repost_status in ["Queued", "Failed"]:
 			try:
 				for entry in repost_doc.repost_vouchers:
 					doc = frappe.get_doc(entry.voucher_type, entry.voucher_no)
@@ -102,10 +101,9 @@
 
 	job_name = "payment_ledger_repost_" + docname
 
-	if not is_job_queued(job_name):
-		frappe.enqueue(
-			method="erpnext.accounts.doctype.repost_payment_ledger.repost_payment_ledger.start_payment_ledger_repost",
-			docname=docname,
-			is_async=True,
-			job_name=job_name,
-		)
+	frappe.enqueue(
+		method="erpnext.accounts.doctype.repost_payment_ledger.repost_payment_ledger.start_payment_ledger_repost",
+		docname=docname,
+		is_async=True,
+		job_name=job_name,
+	)
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.html b/erpnext/accounts/report/general_ledger/general_ledger.html
index c04f518..475be92 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.html
+++ b/erpnext/accounts/report/general_ledger/general_ledger.html
@@ -25,8 +25,8 @@
 	<thead>
 		<tr>
 			<th style="width: 12%">{%= __("Date") %}</th>
-			<th style="width: 15%">{%= __("Ref") %}</th>
-			<th style="width: 25%">{%= __("Party") %}</th>
+			<th style="width: 15%">{%= __("Reference") %}</th>
+			<th style="width: 25%">{%= __("Remarks") %}</th>
 			<th style="width: 15%">{%= __("Debit") %}</th>
 			<th style="width: 15%">{%= __("Credit") %}</th>
 			<th style="width: 18%">{%= __("Balance (Dr - Cr)") %}</th>
@@ -45,7 +45,6 @@
 						<br>
 					{% } %}
 
-					{{ __("Against") }}: {%= data[i].against %}
 					<br>{%= __("Remarks") %}: {%= data[i].remarks %}
 					{% if(data[i].bill_no) { %}
 						<br>{%= __("Supplier Invoice No") %}: {%= data[i].bill_no %}
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 445dcc5..a03de9e 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -439,8 +439,7 @@
 		# cancel advance entry
 		doc = frappe.get_doc(voucher_type, voucher_no)
 		frappe.flags.ignore_party_validation = True
-		gl_map = doc.build_gl_map()
-		create_payment_ledger_entry(gl_map, cancel=1, adv_adj=1)
+		_delete_pl_entries(voucher_type, voucher_no)
 
 		for entry in entries:
 			check_if_advance_entry_modified(entry)
@@ -452,11 +451,23 @@
 			else:
 				update_reference_in_payment_entry(entry, doc, do_not_save=True)
 
+		if doc.doctype == "Journal Entry":
+			try:
+				doc.validate_total_debit_and_credit()
+			except Exception as validation_exception:
+				raise frappe.ValidationError(_(f"Validation Error for {doc.name}")) from validation_exception
+
 		doc.save(ignore_permissions=True)
 		# re-submit advance entry
 		doc = frappe.get_doc(entry.voucher_type, entry.voucher_no)
 		gl_map = doc.build_gl_map()
-		create_payment_ledger_entry(gl_map, cancel=0, adv_adj=1)
+		create_payment_ledger_entry(gl_map, update_outstanding="No", cancel=0, adv_adj=1)
+
+		# Only update outstanding for newly linked vouchers
+		for entry in entries:
+			update_voucher_outstanding(
+				entry.against_voucher_type, entry.against_voucher, entry.account, entry.party_type, entry.party
+			)
 
 		frappe.flags.ignore_party_validation = False
 
diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
index 7d3b645..62a3483 100644
--- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
+++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
@@ -8,7 +8,6 @@
 # import erpnext
 from frappe import _
 from frappe.utils import cint, flt, get_link_to_form
-from six import string_types
 
 import erpnext
 from erpnext.assets.doctype.asset.depreciation import (
@@ -626,7 +625,7 @@
 
 @frappe.whitelist()
 def get_consumed_stock_item_details(args):
-	if isinstance(args, string_types):
+	if isinstance(args, str):
 		args = json.loads(args)
 
 	args = frappe._dict(args)
@@ -678,7 +677,7 @@
 
 @frappe.whitelist()
 def get_warehouse_details(args):
-	if isinstance(args, string_types):
+	if isinstance(args, str):
 		args = json.loads(args)
 
 	args = frappe._dict(args)
@@ -694,7 +693,7 @@
 
 @frappe.whitelist()
 def get_consumed_asset_details(args):
-	if isinstance(args, string_types):
+	if isinstance(args, str):
 		args = json.loads(args)
 
 	args = frappe._dict(args)
@@ -746,7 +745,7 @@
 
 @frappe.whitelist()
 def get_service_item_details(args):
-	if isinstance(args, string_types):
+	if isinstance(args, str):
 		args = json.loads(args)
 
 	args = frappe._dict(args)
diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
index bb50df0..faffd11 100644
--- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
+++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
@@ -86,6 +86,7 @@
 			"status",
 			"department",
 			"cost_center",
+			"calculate_depreciation",
 			"purchase_receipt",
 			"asset_category",
 			"purchase_date",
@@ -98,11 +99,7 @@
 		assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields)
 
 	for asset in assets_record:
-		asset_value = (
-			asset.gross_purchase_amount
-			- flt(asset.opening_accumulated_depreciation)
-			- flt(depreciation_amount_map.get(asset.name))
-		)
+		asset_value = get_asset_value(asset, filters.finance_book)
 		row = {
 			"asset_id": asset.asset_id,
 			"asset_name": asset.asset_name,
@@ -125,6 +122,21 @@
 	return data
 
 
+def get_asset_value(asset, finance_book=None):
+	if not asset.calculate_depreciation:
+		return flt(asset.gross_purchase_amount) - flt(asset.opening_accumulated_depreciation)
+
+	finance_book_filter = ["finance_book", "is", "not set"]
+	if finance_book:
+		finance_book_filter = ["finance_book", "=", finance_book]
+
+	return frappe.db.get_value(
+		doctype="Asset Finance Book",
+		filters=[["parent", "=", asset.asset_id], finance_book_filter],
+		fieldname="value_after_depreciation",
+	)
+
+
 def prepare_chart_data(data, filters):
 	labels_values_map = {}
 	date_field = frappe.scrub(filters.date_based_on)
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index 5a4168a..2415aec 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -219,20 +219,16 @@
 					else:
 						if not frappe.get_value("Item", item.fg_item, "is_sub_contracted_item"):
 							frappe.throw(
-								_(
-									"Row #{0}: Finished Good Item {1} must be a sub-contracted item for service item {2}"
-								).format(item.idx, item.fg_item, item.item_code)
+								_("Row #{0}: Finished Good Item {1} must be a sub-contracted item").format(
+									item.idx, item.fg_item
+								)
 							)
 						elif not frappe.get_value("Item", item.fg_item, "default_bom"):
 							frappe.throw(
 								_("Row #{0}: Default BOM not found for FG Item {1}").format(item.idx, item.fg_item)
 							)
 					if not item.fg_item_qty:
-						frappe.throw(
-							_("Row #{0}: Finished Good Item Qty is not specified for service item {0}").format(
-								item.idx, item.item_code
-							)
-						)
+						frappe.throw(_("Row #{0}: Finished Good Item Qty can not be zero").format(item.idx))
 		else:
 			for item in self.items:
 				item.set("fg_item", None)
diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index 291d756..572d9d3 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -743,9 +743,9 @@
 		pe = get_payment_entry("Purchase Order", po_doc.name)
 		pe.mode_of_payment = "Cash"
 		pe.paid_from = "Cash - _TC"
-		pe.source_exchange_rate = 80
-		pe.target_exchange_rate = 1
-		pe.paid_amount = po_doc.grand_total
+		pe.source_exchange_rate = 1
+		pe.target_exchange_rate = 80
+		pe.paid_amount = po_doc.base_grand_total
 		pe.save(ignore_permissions=True)
 		pe.submit()
 
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
index dbc3644..8e9ded9 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
@@ -216,6 +216,7 @@
 			recipients=data.email_id,
 			sender=sender,
 			attachments=attachments,
+			print_format=self.meta.default_print_format or "Standard",
 			send_email=True,
 			doctype=self.doctype,
 			name=self.name,
@@ -224,9 +225,7 @@
 		frappe.msgprint(_("Email Sent to Supplier {0}").format(data.supplier))
 
 	def get_attachments(self):
-		attachments = [d.name for d in get_attachments(self.doctype, self.name)]
-		attachments.append(frappe.attach_print(self.doctype, self.name, doc=self))
-		return attachments
+		return [d.name for d in get_attachments(self.doctype, self.name)]
 
 	def update_rfq_supplier_status(self, sup_name=None):
 		for supplier in self.suppliers:
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 788dc49..6fa44c9 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -394,7 +394,7 @@
 				self.get("inter_company_reference")
 				or self.get("inter_company_invoice_reference")
 				or self.get("inter_company_order_reference")
-			):
+			) and not self.get("is_return"):
 				msg = _("Internal Sale or Delivery Reference missing.")
 				msg += _("Please create purchase from internal sale or delivery document itself")
 				frappe.throw(msg, title=_("Internal Sales Reference Missing"))
diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py
index 15c82af..8bd0998 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.py
@@ -37,7 +37,7 @@
 		if (
 			ref_doc.company == doc.company
 			and ref_doc.get(party_type) == doc.get(party_type)
-			and ref_doc.docstatus == 1
+			and ref_doc.docstatus.is_submitted()
 		):
 			# validate posting date time
 			return_posting_datetime = "%s %s" % (doc.posting_date, doc.get("posting_time") or "00:00:00")
diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py
index 335d92f..a9561fe 100644
--- a/erpnext/controllers/subcontracting_controller.py
+++ b/erpnext/controllers/subcontracting_controller.py
@@ -74,24 +74,25 @@
 			)
 
 			if not is_stock_item:
-				msg = f"Item {item.item_name} must be a stock item."
-				frappe.throw(_(msg))
+				frappe.throw(_("Row {0}: Item {1} must be a stock item.").format(item.idx, item.item_name))
 
 			if not is_sub_contracted_item:
-				msg = f"Item {item.item_name} must be a subcontracted item."
-				frappe.throw(_(msg))
+				frappe.throw(
+					_("Row {0}: Item {1} must be a subcontracted item.").format(item.idx, item.item_name)
+				)
 
 			if item.bom:
 				bom = frappe.get_doc("BOM", item.bom)
 				if not bom.is_active:
-					msg = f"Please select an active BOM for Item {item.item_name}."
-					frappe.throw(_(msg))
+					frappe.throw(
+						_("Row {0}: Please select an active BOM for Item {1}.").format(item.idx, item.item_name)
+					)
 				if bom.item != item.item_code:
-					msg = f"Please select an valid BOM for Item {item.item_name}."
-					frappe.throw(_(msg))
+					frappe.throw(
+						_("Row {0}: Please select an valid BOM for Item {1}.").format(item.idx, item.item_name)
+					)
 			else:
-				msg = f"Please select a BOM for Item {item.item_name}."
-				frappe.throw(_(msg))
+				frappe.throw(_("Row {0}: Please select a BOM for Item {1}.").format(item.idx, item.item_name))
 
 	def __get_data_before_save(self):
 		item_dict = {}
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index c6a634b..8c403aa 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -6,6 +6,7 @@
 
 import frappe
 from frappe import _, scrub
+from frappe.model.document import Document
 from frappe.utils import cint, flt, round_based_on_smallest_currency_fraction
 
 import erpnext
@@ -20,7 +21,7 @@
 
 
 class calculate_taxes_and_totals(object):
-	def __init__(self, doc):
+	def __init__(self, doc: Document):
 		self.doc = doc
 		frappe.flags.round_off_applicable_accounts = []
 		get_round_off_applicable_accounts(self.doc.company, frappe.flags.round_off_applicable_accounts)
@@ -677,7 +678,7 @@
 			)
 
 	def calculate_total_advance(self):
-		if self.doc.docstatus < 2:
+		if not self.doc.docstatus.is_cancelled():
 			total_allocated_amount = sum(
 				flt(adv.allocated_amount, adv.precision("allocated_amount"))
 				for adv in self.doc.get("advances")
@@ -708,7 +709,7 @@
 					)
 				)
 
-			if self.doc.docstatus == 0:
+			if self.doc.docstatus.is_draft():
 				if self.doc.get("write_off_outstanding_amount_automatically"):
 					self.doc.write_off_amount = 0
 
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 7495ab8..cba1ecc 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -269,6 +269,7 @@
 erpnext.patches.v13_0.create_accounting_dimensions_for_asset_repair
 erpnext.patches.v15_0.delete_taxjar_doctypes
 erpnext.patches.v15_0.create_asset_depreciation_schedules_from_assets
+erpnext.patches.v14_0.update_reference_due_date_in_journal_entry
 
 [post_model_sync]
 execute:frappe.delete_doc_if_exists('Workspace', 'ERPNext Integrations Settings')
diff --git a/erpnext/patches/v14_0/update_reference_due_date_in_journal_entry.py b/erpnext/patches/v14_0/update_reference_due_date_in_journal_entry.py
new file mode 100644
index 0000000..7000312
--- /dev/null
+++ b/erpnext/patches/v14_0/update_reference_due_date_in_journal_entry.py
@@ -0,0 +1,12 @@
+import frappe
+
+
+def execute():
+	if frappe.db.get_value("Journal Entry Account", {"reference_due_date": ""}):
+		frappe.db.sql(
+			"""
+			UPDATE `tabJournal Entry Account`
+			SET reference_due_date = NULL
+			WHERE reference_due_date = ''
+		"""
+		)
diff --git a/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js b/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js
index 9ef8ce6..f7c19a1 100644
--- a/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js
+++ b/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js
@@ -5,7 +5,12 @@
 		Object.assign(this, opts);
 		this.dialog_manager = new erpnext.accounts.bank_reconciliation.DialogManager(
 			this.company,
-			this.bank_account
+			this.bank_account,
+			this.bank_statement_from_date,
+			this.bank_statement_to_date,
+			this.filter_by_reference_date,
+			this.from_reference_date,
+			this.to_reference_date
 		);
 		this.make_dt();
 	}
@@ -17,6 +22,8 @@
 				"erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_bank_transactions",
 			args: {
 				bank_account: this.bank_account,
+				from_date: this.bank_statement_from_date,
+				to_date: this.bank_statement_to_date
 			},
 			callback: function (response) {
 				me.format_data(response.message);
diff --git a/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js b/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js
index b5e6ab8..51664f8 100644
--- a/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js
+++ b/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js
@@ -5,8 +5,12 @@
 		this.bank_account = bank_account;
 		this.company = company;
 		this.make_dialog();
+		this.bank_statement_from_date = bank_statement_from_date;
+		this.bank_statement_to_date = bank_statement_to_date;
+		this.filter_by_reference_date = filter_by_reference_date;
+		this.from_reference_date = from_reference_date;
+		this.to_reference_date = to_reference_date;
 	}
-
 	show_dialog(bank_transaction_name, update_dt_cards) {
 		this.bank_transaction_name = bank_transaction_name;
 		this.update_dt_cards = update_dt_cards;
@@ -35,13 +39,13 @@
 				if (r.message) {
 					this.bank_transaction = r.message;
 					r.message.payment_entry = 1;
+					r.message.journal_entry = 1;
 					this.dialog.set_values(r.message);
 					this.dialog.show();
 				}
 			},
 		});
 	}
-
 	get_linked_vouchers(document_types) {
 		frappe.call({
 			method:
@@ -49,6 +53,11 @@
 			args: {
 				bank_transaction_name: this.bank_transaction_name,
 				document_types: document_types,
+				from_date: this.bank_statement_from_date,
+				to_date: this.bank_statement_to_date,
+				filter_by_reference_date: this.filter_by_reference_date,
+				from_reference_date:this.from_reference_date,
+				to_reference_date:this.to_reference_date
 			},
 
 			callback: (result) => {
@@ -66,6 +75,7 @@
 							row[1],
 							row[2],
 							reference_date,
+							row[8],
 							format_currency(row[3], row[9]),
 							row[6],
 							row[4],
@@ -102,6 +112,11 @@
 				width: 120,
 			},
 			{
+				name: "Posting Date",
+				editable: false,
+				width: 120,
+			},
+			{
 				name: __("Amount"),
 				editable: false,
 				width: 100,
@@ -578,4 +593,4 @@
 		}
 	}
 
-};
+};
\ No newline at end of file
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index f2f1ce1..5c1c6d1 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -1691,7 +1691,7 @@
 		var valid = true;
 
 		$.each(["company", "customer"], function(i, fieldname) {
-			if(frappe.meta.has_field(me.frm.doc.doctype, fieldname) && me.frm.doc.doctype != "Purchase Order") {
+			if(frappe.meta.has_field(me.frm.doc.doctype, fieldname) &&  !["Purchase Order","Purchase Invoice"].includes(me.frm.doc.doctype)) {
 				if (!me.frm.doc[fieldname]) {
 					frappe.msgprint(__("Please specify") + ": " +
 						frappe.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name) +
diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py
index 484b8c9..6836d56 100644
--- a/erpnext/selling/doctype/quotation/quotation.py
+++ b/erpnext/selling/doctype/quotation/quotation.py
@@ -194,14 +194,7 @@
 
 
 @frappe.whitelist()
-def make_sales_order(source_name, target_doc=None):
-	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."))
+def make_sales_order(source_name: str, target_doc=None):
 	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 b151dd5..5aaba4f 100644
--- a/erpnext/selling/doctype/quotation/test_quotation.py
+++ b/erpnext/selling/doctype/quotation/test_quotation.py
@@ -136,17 +136,20 @@
 			sales_order.payment_schedule[1].due_date, getdate(add_days(quotation.transaction_date, 30))
 		)
 
-	def test_valid_till(self):
-		from erpnext.selling.doctype.quotation.quotation import make_sales_order
-
+	def test_valid_till_before_transaction_date(self):
 		quotation = frappe.copy_doc(test_records[0])
 		quotation.valid_till = add_days(quotation.transaction_date, -1)
 		self.assertRaises(frappe.ValidationError, quotation.validate)
 
+	def test_so_from_expired_quotation(self):
+		from erpnext.selling.doctype.quotation.quotation import make_sales_order
+
+		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)
+
+		make_sales_order(quotation.name)
 
 	def test_shopping_cart_without_website_item(self):
 		if frappe.db.exists("Website Item", {"item_code": "_Test Item Home Desktop 100"}):
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index 7c0601e..accf5f2 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -208,7 +208,7 @@
 		for quotation in set(d.prevdoc_docname for d in self.get("items")):
 			if quotation:
 				doc = frappe.get_doc("Quotation", quotation)
-				if doc.docstatus == 2:
+				if doc.docstatus.is_cancelled():
 					frappe.throw(_("Quotation {0} is cancelled").format(quotation))
 
 				doc.set_status(update=True)
diff --git a/erpnext/setup/utils.py b/erpnext/setup/utils.py
index 54bd8c3..bab57fe 100644
--- a/erpnext/setup/utils.py
+++ b/erpnext/setup/utils.py
@@ -81,6 +81,11 @@
 	if entries:
 		return flt(entries[0].exchange_rate)
 
+	if frappe.get_cached_value(
+		"Currency Exchange Settings", "Currency Exchange Settings", "disabled"
+	):
+		return 0.00
+
 	try:
 		cache = frappe.cache()
 		key = "currency_exchange_rate_{0}:{1}:{2}".format(transaction_date, from_currency, to_currency)
diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py
index 7e426ae..53f6b7f 100644
--- a/erpnext/stock/doctype/item/test_item.py
+++ b/erpnext/stock/doctype/item/test_item.py
@@ -106,7 +106,6 @@
 			"conversion_factor": 1.0,
 			"reserved_qty": 1,
 			"actual_qty": 5,
-			"ordered_qty": 10,
 			"projected_qty": 14,
 		}
 
diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py
index 65a792f..9c6f4f4 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.py
+++ b/erpnext/stock/doctype/pick_list/pick_list.py
@@ -4,7 +4,7 @@
 import json
 from collections import OrderedDict, defaultdict
 from itertools import groupby
-from typing import Dict, List, Set
+from typing import Dict, List
 
 import frappe
 from frappe import _
@@ -41,7 +41,9 @@
 				)
 
 	def before_submit(self):
-		update_sales_orders = set()
+		self.validate_picked_items()
+
+	def validate_picked_items(self):
 		for item in self.locations:
 			if self.scan_mode and item.picked_qty < item.stock_qty:
 				frappe.throw(
@@ -50,17 +52,14 @@
 					).format(item.idx, item.stock_qty - item.picked_qty, item.stock_uom),
 					title=_("Pick List Incomplete"),
 				)
-			elif not self.scan_mode and item.picked_qty == 0:
+
+			if not self.scan_mode and item.picked_qty == 0:
 				# if the user has not entered any picked qty, set it to stock_qty, before submit
 				item.picked_qty = item.stock_qty
 
-			if item.sales_order_item:
-				# update the picked_qty in SO Item
-				self.update_sales_order_item(item, item.picked_qty, item.item_code)
-				update_sales_orders.add(item.sales_order)
-
 			if not frappe.get_cached_value("Item", item.item_code, "has_serial_no"):
 				continue
+
 			if not item.serial_no:
 				frappe.throw(
 					_("Row #{0}: {1} does not have any available serial numbers in {2}").format(
@@ -68,58 +67,96 @@
 					),
 					title=_("Serial Nos Required"),
 				)
-			if len(item.serial_no.split("\n")) == item.picked_qty:
-				continue
-			frappe.throw(
-				_(
-					"For item {0} at row {1}, count of serial numbers does not match with the picked quantity"
-				).format(frappe.bold(item.item_code), frappe.bold(item.idx)),
-				title=_("Quantity Mismatch"),
-			)
 
-		self.update_bundle_picked_qty()
-		self.update_sales_order_picking_status(update_sales_orders)
-
-	def before_cancel(self):
-		"""Deduct picked qty on cancelling pick list"""
-		updated_sales_orders = set()
-
-		for item in self.get("locations"):
-			if item.sales_order_item:
-				self.update_sales_order_item(item, -1 * item.picked_qty, item.item_code)
-				updated_sales_orders.add(item.sales_order)
-
-		self.update_bundle_picked_qty()
-		self.update_sales_order_picking_status(updated_sales_orders)
-
-	def update_sales_order_item(self, item, picked_qty, item_code):
-		item_table = "Sales Order Item" if not item.product_bundle_item else "Packed Item"
-		stock_qty_field = "stock_qty" if not item.product_bundle_item else "qty"
-
-		already_picked, actual_qty = frappe.db.get_value(
-			item_table,
-			item.sales_order_item,
-			["picked_qty", stock_qty_field],
-			for_update=True,
-		)
-
-		if self.docstatus == 1:
-			if (((already_picked + picked_qty) / actual_qty) * 100) > (
-				100 + flt(frappe.db.get_single_value("Stock Settings", "over_delivery_receipt_allowance"))
-			):
+			if len(item.serial_no.split("\n")) != item.picked_qty:
 				frappe.throw(
 					_(
-						"You are picking more than required quantity for {}. Check if there is any other pick list created for {}"
-					).format(item_code, item.sales_order)
+						"For item {0} at row {1}, count of serial numbers does not match with the picked quantity"
+					).format(frappe.bold(item.item_code), frappe.bold(item.idx)),
+					title=_("Quantity Mismatch"),
 				)
 
-		frappe.db.set_value(item_table, item.sales_order_item, "picked_qty", already_picked + picked_qty)
+	def on_submit(self):
+		self.update_bundle_picked_qty()
+		self.update_reference_qty()
+		self.update_sales_order_picking_status()
 
-	@staticmethod
-	def update_sales_order_picking_status(sales_orders: Set[str]) -> None:
+	def on_cancel(self):
+		self.update_bundle_picked_qty()
+		self.update_reference_qty()
+		self.update_sales_order_picking_status()
+
+	def update_reference_qty(self):
+		packed_items = []
+		so_items = []
+
+		for item in self.locations:
+			if item.product_bundle_item:
+				packed_items.append(item.sales_order_item)
+			elif item.sales_order_item:
+				so_items.append(item.sales_order_item)
+
+		if packed_items:
+			self.update_packed_items_qty(packed_items)
+
+		if so_items:
+			self.update_sales_order_item_qty(so_items)
+
+	def update_packed_items_qty(self, packed_items):
+		picked_items = get_picked_items_qty(packed_items)
+		self.validate_picked_qty(picked_items)
+
+		picked_qty = frappe._dict()
+		for d in picked_items:
+			picked_qty[d.sales_order_item] = d.picked_qty
+
+		for packed_item in packed_items:
+			frappe.db.set_value(
+				"Packed Item",
+				packed_item,
+				"picked_qty",
+				flt(picked_qty.get(packed_item)),
+				update_modified=False,
+			)
+
+	def update_sales_order_item_qty(self, so_items):
+		picked_items = get_picked_items_qty(so_items)
+		self.validate_picked_qty(picked_items)
+
+		picked_qty = frappe._dict()
+		for d in picked_items:
+			picked_qty[d.sales_order_item] = d.picked_qty
+
+		for so_item in so_items:
+			frappe.db.set_value(
+				"Sales Order Item",
+				so_item,
+				"picked_qty",
+				flt(picked_qty.get(so_item)),
+				update_modified=False,
+			)
+
+	def update_sales_order_picking_status(self) -> None:
+		sales_orders = []
+		for row in self.locations:
+			if row.sales_order and row.sales_order not in sales_orders:
+				sales_orders.append(row.sales_order)
+
 		for sales_order in sales_orders:
-			if sales_order:
-				frappe.get_doc("Sales Order", sales_order, for_update=True).update_picking_status()
+			frappe.get_doc("Sales Order", sales_order, for_update=True).update_picking_status()
+
+	def validate_picked_qty(self, data):
+		over_delivery_receipt_allowance = 100 + flt(
+			frappe.db.get_single_value("Stock Settings", "over_delivery_receipt_allowance")
+		)
+
+		for row in data:
+			if (row.picked_qty / row.stock_qty) * 100 > over_delivery_receipt_allowance:
+				frappe.throw(
+					_(
+						f"You are picking more than required quantity for the item {row.item_code}. Check if there is any other pick list created for the sales order {row.sales_order}."
+					)
+				)
 
 	@frappe.whitelist()
 	def set_item_locations(self, save=False):
@@ -230,7 +267,8 @@
 			frappe.throw(_("Qty of Finished Goods Item should be greater than 0."))
 
 	def before_print(self, settings=None):
-		self.group_similar_items()
+		if self.group_same_items:
+			self.group_similar_items()
 
 	def group_similar_items(self):
 		group_item_qty = defaultdict(float)
@@ -308,6 +346,31 @@
 		return int(flt(min(possible_bundles), precision or 6))
 
 
+def get_picked_items_qty(items) -> List[Dict]:
+	return frappe.db.sql(
+		f"""
+		SELECT
+			sales_order_item,
+			item_code,
+			sales_order,
+			SUM(stock_qty) AS stock_qty,
+			SUM(picked_qty) AS picked_qty
+		FROM
+			`tabPick List Item`
+		WHERE
+			sales_order_item IN (
+				{", ".join(frappe.db.escape(d) for d in items)}
+			)
+			AND docstatus = 1
+		GROUP BY
+			sales_order_item,
+			sales_order
+		FOR UPDATE
+	""",
+		as_dict=1,
+	)
+
+
 def validate_item_locations(pick_list):
 	if not pick_list.locations:
 		frappe.throw(_("Add items in the Item Locations table"))
diff --git a/erpnext/stock/doctype/pick_list/pick_list_dashboard.py b/erpnext/stock/doctype/pick_list/pick_list_dashboard.py
index 92e57be..7fbcbaf 100644
--- a/erpnext/stock/doctype/pick_list/pick_list_dashboard.py
+++ b/erpnext/stock/doctype/pick_list/pick_list_dashboard.py
@@ -1,7 +1,10 @@
 def get_data():
 	return {
 		"fieldname": "pick_list",
+		"internal_links": {
+			"Sales Order": ["locations", "sales_order"],
+		},
 		"transactions": [
-			{"items": ["Stock Entry", "Delivery Note"]},
+			{"items": ["Stock Entry", "Sales Order", "Delivery Note"]},
 		],
 	}
diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py
index f552299..43acdf0 100644
--- a/erpnext/stock/doctype/pick_list/test_pick_list.py
+++ b/erpnext/stock/doctype/pick_list/test_pick_list.py
@@ -445,10 +445,10 @@
 		pl.before_print()
 		self.assertEqual(len(pl.locations), 4)
 
-		# grouping should halve the number of items
+		# grouping should not happen if group_same_items is False
 		pl = frappe.get_doc(
 			doctype="Pick List",
-			group_same_items=True,
+			group_same_items=False,
 			locations=[
 				_dict(item_code="A", warehouse="X", qty=5, picked_qty=1),
 				_dict(item_code="B", warehouse="Y", qty=4, picked_qty=2),
@@ -457,6 +457,11 @@
 			],
 		)
 		pl.before_print()
+		self.assertEqual(len(pl.locations), 4)
+
+		# grouping should halve the number of items
+		pl.group_same_items = True
+		pl.before_print()
 		self.assertEqual(len(pl.locations), 2)
 
 		expected_items = [
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index f7fcb30..363dc0a 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -1181,7 +1181,7 @@
 
 @frappe.whitelist()
 def get_bin_details(item_code, warehouse, company=None, include_child_warehouses=False):
-	bin_details = {"projected_qty": 0, "actual_qty": 0, "reserved_qty": 0, "ordered_qty": 0}
+	bin_details = {"projected_qty": 0, "actual_qty": 0, "reserved_qty": 0}
 
 	if warehouse:
 		from frappe.query_builder.functions import Coalesce, Sum
@@ -1197,7 +1197,6 @@
 				Coalesce(Sum(bin.projected_qty), 0).as_("projected_qty"),
 				Coalesce(Sum(bin.actual_qty), 0).as_("actual_qty"),
 				Coalesce(Sum(bin.reserved_qty), 0).as_("reserved_qty"),
-				Coalesce(Sum(bin.ordered_qty), 0).as_("ordered_qty"),
 			)
 			.where((bin.item_code == item_code) & (bin.warehouse.isin(warehouses)))
 		).run(as_dict=True)[0]
diff --git a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py
index 99f820e..106e877 100644
--- a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py
+++ b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.py
@@ -41,7 +41,7 @@
 		key = (d.voucher_type, d.voucher_no)
 		gl_data = voucher_wise_gl_data.get(key) or {}
 		d.account_value = gl_data.get("account_value", 0)
-		d.difference_value = abs(d.stock_value - d.account_value)
+		d.difference_value = d.stock_value - d.account_value
 		if abs(d.difference_value) > 0.1:
 			data.append(d)
 
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 55a11a1..5d75bfd 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -1270,20 +1270,6 @@
 			(item_code, warehouse, voucher_no, voucher_type),
 		)
 
-	if not last_valuation_rate:
-		# Get valuation rate from last sle for the item against any warehouse
-		last_valuation_rate = frappe.db.sql(
-			"""select valuation_rate
-			from `tabStock Ledger Entry` force index (item_code)
-			where
-				item_code = %s
-				AND valuation_rate > 0
-				AND is_cancelled = 0
-				AND NOT(voucher_no = %s AND voucher_type = %s)
-			order by posting_date desc, posting_time desc, name desc limit 1""",
-			(item_code, voucher_no, voucher_type),
-		)
-
 	if last_valuation_rate:
 		return flt(last_valuation_rate[0][0])
 
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
index bce5360..e8faa48 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
@@ -57,11 +57,17 @@
 
 	def before_validate(self):
 		super(SubcontractingReceipt, self).before_validate()
+		self.validate_items_qty()
 		self.set_items_bom()
 		self.set_items_cost_center()
 		self.set_items_expense_account()
 
 	def validate(self):
+		if (
+			frappe.db.get_single_value("Buying Settings", "backflush_raw_materials_of_subcontract_based_on")
+			== "BOM"
+		):
+			self.supplied_items = []
 		super(SubcontractingReceipt, self).validate()
 		self.set_missing_values()
 		self.validate_posting_time()
@@ -157,7 +163,7 @@
 
 		total_qty = total_amount = 0
 		for item in self.items:
-			if item.name in rm_supp_cost:
+			if item.qty and item.name in rm_supp_cost:
 				item.rm_supp_cost = rm_supp_cost[item.name]
 				item.rm_cost_per_qty = item.rm_supp_cost / item.qty
 				rm_supp_cost.pop(item.name)
@@ -194,6 +200,13 @@
 					).format(item.idx)
 				)
 
+	def validate_items_qty(self):
+		for item in self.items:
+			if not (item.qty or item.rejected_qty):
+				frappe.throw(
+					_("Row {0}: Accepted Qty and Rejected Qty can't be zero at the same time.").format(item.idx)
+				)
+
 	def set_items_bom(self):
 		if self.is_return:
 			for item in self.items: