Merge pull request #36375 from ramonus/job-card-fix

fix: Job Card validation fixed when displaying total completed quantity
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js
index 2fa1d53..2f53f7b 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js
@@ -15,6 +15,17 @@
 			};
 		});
 
+		frm.set_query("offsetting_account", "dimension_defaults", function(doc, cdt, cdn) {
+			let d = locals[cdt][cdn];
+			return {
+				filters: {
+					company: d.company,
+					root_type: ["in", ["Asset", "Liability"]],
+					is_group: 0
+				}
+			}
+		});
+
 		if (!frm.is_new()) {
 			frm.add_custom_button(__('Show {0}', [frm.doc.document_type]), function () {
 				frappe.set_route("List", frm.doc.document_type);
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
index 15c84d4..cfe5e6e 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
@@ -39,6 +39,8 @@
 		if not self.is_new():
 			self.validate_document_type_change()
 
+		self.validate_dimension_defaults()
+
 	def validate_document_type_change(self):
 		doctype_before_save = frappe.db.get_value("Accounting Dimension", self.name, "document_type")
 		if doctype_before_save != self.document_type:
@@ -46,6 +48,14 @@
 			message += _("Please create a new Accounting Dimension if required.")
 			frappe.throw(message)
 
+	def validate_dimension_defaults(self):
+		companies = []
+		for default in self.get("dimension_defaults"):
+			if default.company not in companies:
+				companies.append(default.company)
+			else:
+				frappe.throw(_("Company {0} is added more than once").format(frappe.bold(default.company)))
+
 	def after_insert(self):
 		if frappe.flags.in_test:
 			make_dimension_in_accounting_doctypes(doc=self)
diff --git a/erpnext/accounts/doctype/accounting_dimension_detail/accounting_dimension_detail.json b/erpnext/accounts/doctype/accounting_dimension_detail/accounting_dimension_detail.json
index e9e1f43..7b6120a 100644
--- a/erpnext/accounts/doctype/accounting_dimension_detail/accounting_dimension_detail.json
+++ b/erpnext/accounts/doctype/accounting_dimension_detail/accounting_dimension_detail.json
@@ -8,7 +8,10 @@
   "reference_document",
   "default_dimension",
   "mandatory_for_bs",
-  "mandatory_for_pl"
+  "mandatory_for_pl",
+  "column_break_lqns",
+  "automatically_post_balancing_accounting_entry",
+  "offsetting_account"
  ],
  "fields": [
   {
@@ -50,6 +53,23 @@
    "fieldtype": "Check",
    "in_list_view": 1,
    "label": "Mandatory For Profit and Loss Account"
+  },
+  {
+   "default": "0",
+   "fieldname": "automatically_post_balancing_accounting_entry",
+   "fieldtype": "Check",
+   "label": "Automatically post balancing accounting entry"
+  },
+  {
+   "fieldname": "offsetting_account",
+   "fieldtype": "Link",
+   "label": "Offsetting Account",
+   "mandatory_depends_on": "eval: doc.automatically_post_balancing_accounting_entry",
+   "options": "Account"
+  },
+  {
+   "fieldname": "column_break_lqns",
+   "fieldtype": "Column Break"
   }
  ],
  "istable": 1,
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index 8c96480..486e01e 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -1736,6 +1736,61 @@
 		rate = flt(sle.stock_value_difference) / flt(sle.actual_qty)
 		self.assertAlmostEqual(returned_inv.items[0].rate, rate)
 
+	def test_offsetting_entries_for_accounting_dimensions(self):
+		from erpnext.accounts.doctype.account.test_account import create_account
+		from erpnext.accounts.report.trial_balance.test_trial_balance import (
+			clear_dimension_defaults,
+			create_accounting_dimension,
+			disable_dimension,
+		)
+
+		create_account(
+			account_name="Offsetting",
+			company="_Test Company",
+			parent_account="Temporary Accounts - _TC",
+		)
+
+		create_accounting_dimension(company="_Test Company", offsetting_account="Offsetting - _TC")
+
+		branch1 = frappe.new_doc("Branch")
+		branch1.branch = "Location 1"
+		branch1.insert(ignore_if_duplicate=True)
+		branch2 = frappe.new_doc("Branch")
+		branch2.branch = "Location 2"
+		branch2.insert(ignore_if_duplicate=True)
+
+		pi = make_purchase_invoice(
+			company="_Test Company",
+			customer="_Test Supplier",
+			do_not_save=True,
+			do_not_submit=True,
+			rate=1000,
+			price_list_rate=1000,
+			qty=1,
+		)
+		pi.branch = branch1.branch
+		pi.items[0].branch = branch2.branch
+		pi.save()
+		pi.submit()
+
+		expected_gle = [
+			["_Test Account Cost for Goods Sold - _TC", 1000, 0.0, nowdate(), branch2.branch],
+			["Creditors - _TC", 0.0, 1000, nowdate(), branch1.branch],
+			["Offsetting - _TC", 1000, 0.0, nowdate(), branch1.branch],
+			["Offsetting - _TC", 0.0, 1000, nowdate(), branch2.branch],
+		]
+
+		check_gl_entries(
+			self,
+			pi.name,
+			expected_gle,
+			nowdate(),
+			voucher_type="Purchase Invoice",
+			additional_columns=["branch"],
+		)
+		clear_dimension_defaults("Branch")
+		disable_dimension()
+
 
 def set_advance_flag(company, flag, default_account):
 	frappe.db.set_value(
@@ -1748,9 +1803,16 @@
 	)
 
 
-def check_gl_entries(doc, voucher_no, expected_gle, posting_date, voucher_type="Purchase Invoice"):
+def check_gl_entries(
+	doc,
+	voucher_no,
+	expected_gle,
+	posting_date,
+	voucher_type="Purchase Invoice",
+	additional_columns=None,
+):
 	gl = frappe.qb.DocType("GL Entry")
-	q = (
+	query = (
 		frappe.qb.from_(gl)
 		.select(gl.account, gl.debit, gl.credit, gl.posting_date)
 		.where(
@@ -1761,7 +1823,12 @@
 		)
 		.orderby(gl.posting_date, gl.account, gl.creation)
 	)
-	gl_entries = q.run(as_dict=True)
+
+	if additional_columns:
+		for col in additional_columns:
+			query = query.select(gl[col])
+
+	gl_entries = query.run(as_dict=True)
 
 	for i, gle in enumerate(gl_entries):
 		doc.assertEqual(expected_gle[i][0], gle.account)
@@ -1769,6 +1836,12 @@
 		doc.assertEqual(expected_gle[i][2], gle.credit)
 		doc.assertEqual(getdate(expected_gle[i][3]), gle.posting_date)
 
+		if additional_columns:
+			j = 4
+			for col in additional_columns:
+				doc.assertEqual(expected_gle[i][j], gle[col])
+				j += 1
+
 
 def create_tax_witholding_category(category_name, company, account):
 	from erpnext.accounts.utils import get_fiscal_year
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index b942a0c..3803836 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -28,6 +28,7 @@
 ):
 	if gl_map:
 		if not cancel:
+			make_acc_dimensions_offsetting_entry(gl_map)
 			validate_accounting_period(gl_map)
 			validate_disabled_accounts(gl_map)
 			gl_map = process_gl_map(gl_map, merge_entries)
@@ -51,6 +52,63 @@
 			make_reverse_gl_entries(gl_map, adv_adj=adv_adj, update_outstanding=update_outstanding)
 
 
+def make_acc_dimensions_offsetting_entry(gl_map):
+	accounting_dimensions_to_offset = get_accounting_dimensions_for_offsetting_entry(
+		gl_map, gl_map[0].company
+	)
+	no_of_dimensions = len(accounting_dimensions_to_offset)
+	if no_of_dimensions == 0:
+		return
+
+	offsetting_entries = []
+
+	for gle in gl_map:
+		for dimension in accounting_dimensions_to_offset:
+			offsetting_entry = gle.copy()
+			debit = flt(gle.credit) / no_of_dimensions if gle.credit != 0 else 0
+			credit = flt(gle.debit) / no_of_dimensions if gle.debit != 0 else 0
+			offsetting_entry.update(
+				{
+					"account": dimension.offsetting_account,
+					"debit": debit,
+					"credit": credit,
+					"debit_in_account_currency": debit,
+					"credit_in_account_currency": credit,
+					"remarks": _("Offsetting for Accounting Dimension") + " - {0}".format(dimension.name),
+					"against_voucher": None,
+				}
+			)
+			offsetting_entry["against_voucher_type"] = None
+			offsetting_entries.append(offsetting_entry)
+
+	gl_map += offsetting_entries
+
+
+def get_accounting_dimensions_for_offsetting_entry(gl_map, company):
+	acc_dimension = frappe.qb.DocType("Accounting Dimension")
+	dimension_detail = frappe.qb.DocType("Accounting Dimension Detail")
+
+	acc_dimensions = (
+		frappe.qb.from_(acc_dimension)
+		.inner_join(dimension_detail)
+		.on(acc_dimension.name == dimension_detail.parent)
+		.select(acc_dimension.fieldname, acc_dimension.name, dimension_detail.offsetting_account)
+		.where(
+			(acc_dimension.disabled == 0)
+			& (dimension_detail.company == company)
+			& (dimension_detail.automatically_post_balancing_accounting_entry == 1)
+		)
+	).run(as_dict=True)
+
+	accounting_dimensions_to_offset = []
+	for acc_dimension in acc_dimensions:
+		values = set([entry.get(acc_dimension.fieldname) for entry in gl_map])
+		if len(values) > 1:
+			accounting_dimensions_to_offset.append(acc_dimension)
+
+	return accounting_dimensions_to_offset
+
+
 def validate_disabled_accounts(gl_map):
 	accounts = [d.account for d in gl_map if d.account]
 
diff --git a/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py b/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py
index c84b843..28d0c20 100644
--- a/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py
+++ b/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py
@@ -2,6 +2,7 @@
 
 import frappe
 from frappe import qb
+from frappe.tests.utils import FrappeTestCase, change_settings
 from frappe.utils import nowdate
 
 from erpnext.accounts.doctype.account.test_account import create_account
@@ -10,16 +11,15 @@
 from erpnext.accounts.report.deferred_revenue_and_expense.deferred_revenue_and_expense import (
 	Deferred_Revenue_and_Expense_Report,
 )
+from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
 from erpnext.accounts.utils import get_fiscal_year
 from erpnext.buying.doctype.supplier.test_supplier import create_supplier
 from erpnext.stock.doctype.item.test_item import create_item
 
 
-class TestDeferredRevenueAndExpense(unittest.TestCase):
+class TestDeferredRevenueAndExpense(FrappeTestCase, AccountsTestMixin):
 	@classmethod
 	def setUpClass(self):
-		clear_accounts_and_items()
-		create_company()
 		self.maxDiff = None
 
 	def clear_old_entries(self):
@@ -51,55 +51,58 @@
 		if deferred_invoices:
 			qb.from_(pinv).delete().where(pinv.name.isin(deferred_invoices)).run()
 
-	def test_deferred_revenue(self):
-		self.clear_old_entries()
+	def setup_deferred_accounts_and_items(self):
+		# created deferred expense accounts, if not found
+		self.deferred_revenue_account = create_account(
+			account_name="Deferred Revenue",
+			parent_account="Current Liabilities - " + self.company_abbr,
+			company=self.company,
+		)
 
 		# created deferred expense accounts, if not found
-		deferred_revenue_account = create_account(
-			account_name="Deferred Revenue",
-			parent_account="Current Liabilities - _CD",
-			company="_Test Company DR",
+		self.deferred_expense_account = create_account(
+			account_name="Deferred Expense",
+			parent_account="Current Assets - " + self.company_abbr,
+			company=self.company,
 		)
 
-		acc_settings = frappe.get_doc("Accounts Settings", "Accounts Settings")
-		acc_settings.book_deferred_entries_based_on = "Months"
-		acc_settings.save()
+	def setUp(self):
+		self.create_company()
+		self.create_customer("_Test Customer")
+		self.create_supplier("_Test Furniture Supplier")
+		self.setup_deferred_accounts_and_items()
+		self.clear_old_entries()
 
-		customer = frappe.new_doc("Customer")
-		customer.customer_name = "_Test Customer DR"
-		customer.type = "Individual"
-		customer.insert()
+	def tearDown(self):
+		frappe.db.rollback()
 
-		item = create_item(
-			"_Test Internet Subscription",
-			is_stock_item=0,
-			warehouse="All Warehouses - _CD",
-			company="_Test Company DR",
-		)
+	@change_settings("Accounts Settings", {"book_deferred_entries_based_on": "Months"})
+	def test_deferred_revenue(self):
+		self.create_item("_Test Internet Subscription", 0, self.warehouse, self.company)
+		item = frappe.get_doc("Item", self.item)
 		item.enable_deferred_revenue = 1
-		item.deferred_revenue_account = deferred_revenue_account
+		item.deferred_revenue_account = self.deferred_revenue_account
 		item.no_of_months = 3
 		item.save()
 
 		si = create_sales_invoice(
-			item=item.name,
-			company="_Test Company DR",
-			customer="_Test Customer DR",
-			debit_to="Debtors - _CD",
+			item=self.item,
+			company=self.company,
+			customer=self.customer,
+			debit_to=self.debit_to,
 			posting_date="2021-05-01",
-			parent_cost_center="Main - _CD",
-			cost_center="Main - _CD",
+			parent_cost_center=self.cost_center,
+			cost_center=self.cost_center,
 			do_not_save=True,
 			rate=300,
 			price_list_rate=300,
 		)
 
-		si.items[0].income_account = "Sales - _CD"
+		si.items[0].income_account = self.income_account
 		si.items[0].enable_deferred_revenue = 1
 		si.items[0].service_start_date = "2021-05-01"
 		si.items[0].service_end_date = "2021-08-01"
-		si.items[0].deferred_revenue_account = deferred_revenue_account
-		si.items[0].income_account = "Sales - _CD"
+		si.items[0].deferred_revenue_account = self.deferred_revenue_account
 		si.save()
 		si.submit()
 
@@ -110,7 +113,7 @@
 				start_date="2021-05-01",
 				end_date="2021-08-01",
 				type="Income",
-				company="_Test Company DR",
+				company=self.company,
 			)
 		)
 		pda.insert()
@@ -120,7 +123,7 @@
 		fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date="2021-05-01"))
 		self.filters = frappe._dict(
 			{
-				"company": frappe.defaults.get_user_default("Company"),
+				"company": self.company,
 				"filter_based_on": "Date Range",
 				"period_start_date": "2021-05-01",
 				"period_end_date": "2021-08-01",
@@ -142,57 +145,36 @@
 		]
 		self.assertEqual(report.period_total, expected)
 
+	@change_settings("Accounts Settings", {"book_deferred_entries_based_on": "Months"})
 	def test_deferred_expense(self):
-		self.clear_old_entries()
-
-		# created deferred expense accounts, if not found
-		deferred_expense_account = create_account(
-			account_name="Deferred Expense",
-			parent_account="Current Assets - _CD",
-			company="_Test Company DR",
-		)
-
-		acc_settings = frappe.get_doc("Accounts Settings", "Accounts Settings")
-		acc_settings.book_deferred_entries_based_on = "Months"
-		acc_settings.save()
-
-		supplier = create_supplier(
-			supplier_name="_Test Furniture Supplier", supplier_group="Local", supplier_type="Company"
-		)
-		supplier.save()
-
-		item = create_item(
-			"_Test Office Desk",
-			is_stock_item=0,
-			warehouse="All Warehouses - _CD",
-			company="_Test Company DR",
-		)
+		self.create_item("_Test Office Desk", 0, self.warehouse, self.company)
+		item = frappe.get_doc("Item", self.item)
 		item.enable_deferred_expense = 1
-		item.deferred_expense_account = deferred_expense_account
+		item.deferred_expense_account = self.deferred_expense_account
 		item.no_of_months_exp = 3
 		item.save()
 
 		pi = make_purchase_invoice(
-			item=item.name,
-			company="_Test Company DR",
-			supplier="_Test Furniture Supplier",
+			item=self.item,
+			company=self.company,
+			supplier=self.supplier,
 			is_return=False,
 			update_stock=False,
 			posting_date=frappe.utils.datetime.date(2021, 5, 1),
-			parent_cost_center="Main - _CD",
-			cost_center="Main - _CD",
+			parent_cost_center=self.cost_center,
+			cost_center=self.cost_center,
 			do_not_save=True,
 			rate=300,
 			price_list_rate=300,
-			warehouse="All Warehouses - _CD",
+			warehouse=self.warehouse,
 			qty=1,
 		)
 		pi.set_posting_time = True
 		pi.items[0].enable_deferred_expense = 1
 		pi.items[0].service_start_date = "2021-05-01"
 		pi.items[0].service_end_date = "2021-08-01"
-		pi.items[0].deferred_expense_account = deferred_expense_account
-		pi.items[0].expense_account = "Office Maintenance Expenses - _CD"
+		pi.items[0].deferred_expense_account = self.deferred_expense_account
+		pi.items[0].expense_account = self.expense_account
 		pi.save()
 		pi.submit()
 
@@ -203,7 +185,7 @@
 				start_date="2021-05-01",
 				end_date="2021-08-01",
 				type="Expense",
-				company="_Test Company DR",
+				company=self.company,
 			)
 		)
 		pda.insert()
@@ -213,7 +195,7 @@
 		fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date="2021-05-01"))
 		self.filters = frappe._dict(
 			{
-				"company": frappe.defaults.get_user_default("Company"),
+				"company": self.company,
 				"filter_based_on": "Date Range",
 				"period_start_date": "2021-05-01",
 				"period_end_date": "2021-08-01",
@@ -235,52 +217,31 @@
 		]
 		self.assertEqual(report.period_total, expected)
 
+	@change_settings("Accounts Settings", {"book_deferred_entries_based_on": "Months"})
 	def test_zero_months(self):
-		self.clear_old_entries()
-		# created deferred expense accounts, if not found
-		deferred_revenue_account = create_account(
-			account_name="Deferred Revenue",
-			parent_account="Current Liabilities - _CD",
-			company="_Test Company DR",
-		)
-
-		acc_settings = frappe.get_doc("Accounts Settings", "Accounts Settings")
-		acc_settings.book_deferred_entries_based_on = "Months"
-		acc_settings.save()
-
-		customer = frappe.new_doc("Customer")
-		customer.customer_name = "_Test Customer DR"
-		customer.type = "Individual"
-		customer.insert()
-
-		item = create_item(
-			"_Test Internet Subscription",
-			is_stock_item=0,
-			warehouse="All Warehouses - _CD",
-			company="_Test Company DR",
-		)
+		self.create_item("_Test Internet Subscription", 0, self.warehouse, self.company)
+		item = frappe.get_doc("Item", self.item)
 		item.enable_deferred_revenue = 1
-		item.deferred_revenue_account = deferred_revenue_account
+		item.deferred_revenue_account = self.deferred_revenue_account
 		item.no_of_months = 0
 		item.save()
 
 		si = create_sales_invoice(
 			item=item.name,
-			company="_Test Company DR",
-			customer="_Test Customer DR",
-			debit_to="Debtors - _CD",
+			company=self.company,
+			customer=self.customer,
+			debit_to=self.debit_to,
 			posting_date="2021-05-01",
-			parent_cost_center="Main - _CD",
-			cost_center="Main - _CD",
+			parent_cost_center=self.cost_center,
+			cost_center=self.cost_center,
 			do_not_save=True,
 			rate=300,
 			price_list_rate=300,
 		)
 
 		si.items[0].enable_deferred_revenue = 1
-		si.items[0].income_account = "Sales - _CD"
-		si.items[0].deferred_revenue_account = deferred_revenue_account
-		si.items[0].income_account = "Sales - _CD"
+		si.items[0].income_account = self.income_account
+		si.items[0].deferred_revenue_account = self.deferred_revenue_account
 		si.save()
 		si.submit()
 
@@ -291,7 +252,7 @@
 				start_date="2021-05-01",
 				end_date="2021-08-01",
 				type="Income",
-				company="_Test Company DR",
+				company=self.company,
 			)
 		)
 		pda.insert()
@@ -301,7 +262,7 @@
 		fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date="2021-05-01"))
 		self.filters = frappe._dict(
 			{
-				"company": frappe.defaults.get_user_default("Company"),
+				"company": self.company,
 				"filter_based_on": "Date Range",
 				"period_start_date": "2021-05-01",
 				"period_end_date": "2021-08-01",
@@ -322,30 +283,3 @@
 			{"key": "aug_2021", "total": 0, "actual": 0},
 		]
 		self.assertEqual(report.period_total, expected)
-
-
-def create_company():
-	company = frappe.db.exists("Company", "_Test Company DR")
-	if not company:
-		company = frappe.new_doc("Company")
-		company.company_name = "_Test Company DR"
-		company.default_currency = "INR"
-		company.chart_of_accounts = "Standard"
-		company.insert()
-
-
-def clear_accounts_and_items():
-	item = qb.DocType("Item")
-	account = qb.DocType("Account")
-	customer = qb.DocType("Customer")
-	supplier = qb.DocType("Supplier")
-
-	qb.from_(account).delete().where(
-		(account.account_name == "Deferred Revenue")
-		| (account.account_name == "Deferred Expense") & (account.company == "_Test Company DR")
-	).run()
-	qb.from_(item).delete().where(
-		(item.item_code == "_Test Internet Subscription") | (item.item_code == "_Test Office Rent")
-	).run()
-	qb.from_(customer).delete().where(customer.customer_name == "_Test Customer DR").run()
-	qb.from_(supplier).delete().where(supplier.supplier_name == "_Test Furniture Supplier").run()
diff --git a/erpnext/accounts/report/trial_balance/test_trial_balance.py b/erpnext/accounts/report/trial_balance/test_trial_balance.py
new file mode 100644
index 0000000..4682ac4
--- /dev/null
+++ b/erpnext/accounts/report/trial_balance/test_trial_balance.py
@@ -0,0 +1,118 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
+# MIT License. See license.txt
+
+import frappe
+from frappe.tests.utils import FrappeTestCase
+from frappe.utils import today
+
+from erpnext.accounts.report.trial_balance.trial_balance import execute
+
+
+class TestTrialBalance(FrappeTestCase):
+	def setUp(self):
+		from erpnext.accounts.doctype.account.test_account import create_account
+		from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
+		from erpnext.accounts.utils import get_fiscal_year
+
+		self.company = create_company()
+		create_cost_center(
+			cost_center_name="Test Cost Center",
+			company="Trial Balance Company",
+			parent_cost_center="Trial Balance Company - TBC",
+		)
+		create_account(
+			account_name="Offsetting",
+			company="Trial Balance Company",
+			parent_account="Temporary Accounts - TBC",
+		)
+		self.fiscal_year = get_fiscal_year(today(), company="Trial Balance Company")[0]
+		create_accounting_dimension()
+
+	def test_offsetting_entries_for_accounting_dimensions(self):
+		"""
+		Checks if Trial Balance Report is balanced when filtered using a particular Accounting Dimension
+		"""
+		from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
+
+		frappe.db.sql("delete from `tabSales Invoice` where company='Trial Balance Company'")
+		frappe.db.sql("delete from `tabGL Entry` where company='Trial Balance Company'")
+
+		branch1 = frappe.new_doc("Branch")
+		branch1.branch = "Location 1"
+		branch1.insert(ignore_if_duplicate=True)
+		branch2 = frappe.new_doc("Branch")
+		branch2.branch = "Location 2"
+		branch2.insert(ignore_if_duplicate=True)
+
+		si = create_sales_invoice(
+			company=self.company,
+			debit_to="Debtors - TBC",
+			cost_center="Test Cost Center - TBC",
+			income_account="Sales - TBC",
+			do_not_submit=1,
+		)
+		si.branch = "Location 1"
+		si.items[0].branch = "Location 2"
+		si.save()
+		si.submit()
+
+		filters = frappe._dict(
+			{"company": self.company, "fiscal_year": self.fiscal_year, "branch": ["Location 1"]}
+		)
+		total_row = execute(filters)[1][-1]
+		self.assertEqual(total_row["debit"], total_row["credit"])
+
+	def tearDown(self):
+		clear_dimension_defaults("Branch")
+		disable_dimension()
+
+
+def create_company(**args):
+	args = frappe._dict(args)
+	company = frappe.get_doc(
+		{
+			"doctype": "Company",
+			"company_name": args.company_name or "Trial Balance Company",
+			"country": args.country or "India",
+			"default_currency": args.currency or "INR",
+		}
+	)
+	company.insert(ignore_if_duplicate=True)
+	return company.name
+
+
+def create_accounting_dimension(**args):
+	args = frappe._dict(args)
+	document_type = args.document_type or "Branch"
+	if frappe.db.exists("Accounting Dimension", document_type):
+		accounting_dimension = frappe.get_doc("Accounting Dimension", document_type)
+		accounting_dimension.disabled = 0
+	else:
+		accounting_dimension = frappe.new_doc("Accounting Dimension")
+		accounting_dimension.document_type = document_type
+		accounting_dimension.insert()
+
+	accounting_dimension.set("dimension_defaults", [])
+	accounting_dimension.append(
+		"dimension_defaults",
+		{
+			"company": args.company or "Trial Balance Company",
+			"automatically_post_balancing_accounting_entry": 1,
+			"offsetting_account": args.offsetting_account or "Offsetting - TBC",
+		},
+	)
+	accounting_dimension.save()
+
+
+def disable_dimension(**args):
+	args = frappe._dict(args)
+	document_type = args.document_type or "Branch"
+	dimension = frappe.get_doc("Accounting Dimension", document_type)
+	dimension.disabled = 1
+	dimension.save()
+
+
+def clear_dimension_defaults(dimension_name):
+	accounting_dimension = frappe.get_doc("Accounting Dimension", dimension_name)
+	accounting_dimension.dimension_defaults = []
+	accounting_dimension.save()
diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py
index 5a9e950..376571f 100644
--- a/erpnext/accounts/report/trial_balance/trial_balance.py
+++ b/erpnext/accounts/report/trial_balance/trial_balance.py
@@ -259,7 +259,7 @@
 		lft, rgt = frappe.db.get_value("Cost Center", filters.cost_center, ["lft", "rgt"])
 		cost_center = frappe.qb.DocType("Cost Center")
 		opening_balance = opening_balance.where(
-			closing_balance.cost_center.in_(
+			closing_balance.cost_center.isin(
 				frappe.qb.from_(cost_center)
 				.select("name")
 				.where((cost_center.lft >= lft) & (cost_center.rgt <= rgt))
diff --git a/erpnext/accounts/test/accounts_mixin.py b/erpnext/accounts/test/accounts_mixin.py
new file mode 100644
index 0000000..c82164e
--- /dev/null
+++ b/erpnext/accounts/test/accounts_mixin.py
@@ -0,0 +1,80 @@
+import frappe
+
+from erpnext.stock.doctype.item.test_item import create_item
+
+
+class AccountsTestMixin:
+	def create_customer(self, customer_name, currency=None):
+		if not frappe.db.exists("Customer", customer_name):
+			customer = frappe.new_doc("Customer")
+			customer.customer_name = customer_name
+			customer.type = "Individual"
+
+			if currency:
+				customer.default_currency = currency
+			customer.save()
+			self.customer = customer.name
+		else:
+			self.customer = customer_name
+
+	def create_supplier(self, supplier_name, currency=None):
+		if not frappe.db.exists("Supplier", supplier_name):
+			supplier = frappe.new_doc("Supplier")
+			supplier.supplier_name = supplier_name
+			supplier.supplier_type = "Individual"
+			supplier.supplier_group = "Local"
+
+			if currency:
+				supplier.default_currency = currency
+			supplier.save()
+			self.supplier = supplier.name
+		else:
+			self.supplier = supplier_name
+
+	def create_item(self, item_name, is_stock=0, warehouse=None, company=None):
+		item = create_item(item_name, is_stock_item=is_stock, warehouse=warehouse, company=company)
+		self.item = item.name
+
+	def create_company(self, company_name="_Test Company", abbr="_TC"):
+		self.company_abbr = abbr
+		if frappe.db.exists("Company", company_name):
+			company = frappe.get_doc("Company", company_name)
+		else:
+			company = frappe.get_doc(
+				{
+					"doctype": "Company",
+					"company_name": company_name,
+					"country": "India",
+					"default_currency": "INR",
+					"create_chart_of_accounts_based_on": "Standard Template",
+					"chart_of_accounts": "Standard",
+				}
+			)
+			company = company.save()
+
+		self.company = company.name
+		self.cost_center = company.cost_center
+		self.warehouse = "Stores - " + abbr
+		self.finished_warehouse = "Finished Goods - " + abbr
+		self.income_account = "Sales - " + abbr
+		self.expense_account = "Cost of Goods Sold - " + abbr
+		self.debit_to = "Debtors - " + abbr
+		self.debit_usd = "Debtors USD - " + abbr
+		self.cash = "Cash - " + abbr
+		self.creditors = "Creditors - " + abbr
+
+		# create bank account
+		bank_account = "HDFC - " + abbr
+		if frappe.db.exists("Account", bank_account):
+			self.bank = bank_account
+		else:
+			bank_acc = frappe.get_doc(
+				{
+					"doctype": "Account",
+					"account_name": "HDFC",
+					"parent_account": "Bank Accounts - " + abbr,
+					"company": self.company,
+				}
+			)
+			bank_acc.save()
+			self.bank = bank_acc.name
diff --git a/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.py b/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.py
index 17b5aae..e986746 100644
--- a/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.py
+++ b/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.py
@@ -93,6 +93,7 @@
 		else:
 			frappe.enqueue(
 				method="erpnext.manufacturing.doctype.bom_update_log.bom_update_log.process_boms_cost_level_wise",
+				queue="long",
 				update_doc=self,
 				now=frappe.flags.in_test,
 				enqueue_after_commit=True,
diff --git a/erpnext/manufacturing/doctype/bom_update_log/bom_updation_utils.py b/erpnext/manufacturing/doctype/bom_update_log/bom_updation_utils.py
index b90cfd9..a2919b7 100644
--- a/erpnext/manufacturing/doctype/bom_update_log/bom_updation_utils.py
+++ b/erpnext/manufacturing/doctype/bom_update_log/bom_updation_utils.py
@@ -157,12 +157,19 @@
 def get_leaf_boms() -> List[str]:
 	"Get BOMs that have no dependencies."
 
-	return frappe.db.sql_list(
-		"""select name from `tabBOM` bom
-		where docstatus=1 and is_active=1
-			and not exists(select bom_no from `tabBOM Item`
-				where parent=bom.name and bom_no !='')"""
-	)
+	bom = frappe.qb.DocType("BOM")
+	bom_item = frappe.qb.DocType("BOM Item")
+
+	boms = (
+		frappe.qb.from_(bom)
+		.left_join(bom_item)
+		.on((bom.name == bom_item.parent) & (bom_item.bom_no != ""))
+		.select(bom.name)
+		.where((bom.docstatus == 1) & (bom.is_active == 1) & (bom_item.bom_no.isnull()))
+		.distinct()
+	).run(pluck=True)
+
+	return boms
 
 
 def _generate_dependence_map() -> defaultdict:
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js
index 4898691..46c554c 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.js
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js
@@ -9,19 +9,25 @@
 			item.temporary_name = item.name;
 		});
 	},
+
 	setup(frm) {
+		frm.trigger("setup_queries");
+
 		frm.custom_make_buttons = {
 			'Work Order': 'Work Order / Subcontract PO',
 			'Material Request': 'Material Request',
 		};
+	},
 
-		frm.fields_dict['po_items'].grid.get_field('warehouse').get_query = function(doc) {
+	setup_queries(frm) {
+		frm.set_query("sales_order", "sales_orders", () => {
 			return {
+				query: "erpnext.manufacturing.doctype.production_plan.production_plan.sales_order_query",
 				filters: {
-					company: doc.company
+					company: frm.doc.company,
 				}
 			}
-		}
+		});
 
 		frm.set_query('for_warehouse', function(doc) {
 			return {
@@ -42,32 +48,40 @@
 			};
 		});
 
-		frm.fields_dict['po_items'].grid.get_field('item_code').get_query = function(doc) {
+		frm.set_query("item_code", "po_items", (doc, cdt, cdn) => {
 			return {
 				query: "erpnext.controllers.queries.item_query",
 				filters:{
 					'is_stock_item': 1,
 				}
 			}
-		}
+		});
 
-		frm.fields_dict['po_items'].grid.get_field('bom_no').get_query = function(doc, cdt, cdn) {
+		frm.set_query("bom_no", "po_items", (doc, cdt, cdn) => {
 			var d = locals[cdt][cdn];
 			if (d.item_code) {
 				return {
 					query: "erpnext.controllers.queries.bom",
-					filters:{'item': cstr(d.item_code), 'docstatus': 1}
+					filters:{'item': d.item_code, 'docstatus': 1}
 				}
 			} else frappe.msgprint(__("Please enter Item first"));
-		}
+		});
 
-		frm.fields_dict['mr_items'].grid.get_field('warehouse').get_query = function(doc) {
+		frm.set_query("warehouse", "mr_items", (doc) => {
 			return {
 				filters: {
 					company: doc.company
 				}
 			}
-		}
+		});
+
+		frm.set_query("warehouse", "po_items", (doc) => {
+			return {
+				filters: {
+					company: doc.company
+				}
+			}
+		});
 	},
 
 	refresh(frm) {
@@ -436,7 +450,7 @@
 				}
 			});
 		}
-	}
+	},
 });
 
 frappe.ui.form.on("Material Request Plan Item", {
@@ -467,31 +481,36 @@
 
 frappe.ui.form.on("Production Plan Sales Order", {
 	sales_order(frm, cdt, cdn) {
-		const { sales_order } = locals[cdt][cdn];
+		let row = locals[cdt][cdn];
+		const sales_order = row.sales_order;
 		if (!sales_order) {
 			return;
 		}
-		frappe.call({
-			method: "erpnext.manufacturing.doctype.production_plan.production_plan.get_so_details",
-			args: { sales_order },
-			callback(r) {
-				const {transaction_date, customer, grand_total} = r.message;
-				frappe.model.set_value(cdt, cdn, 'sales_order_date', transaction_date);
-				frappe.model.set_value(cdt, cdn, 'customer', customer);
-				frappe.model.set_value(cdt, cdn, 'grand_total', grand_total);
-			}
-		});
+
+		if (row.sales_order) {
+			frm.call({
+				method: "validate_sales_orders",
+				doc: frm.doc,
+				args: {
+					sales_order: row.sales_order,
+				},
+				callback(r) {
+					frappe.call({
+						method: "erpnext.manufacturing.doctype.production_plan.production_plan.get_so_details",
+						args: { sales_order },
+						callback(r) {
+							const {transaction_date, customer, grand_total} = r.message;
+							frappe.model.set_value(cdt, cdn, 'sales_order_date', transaction_date);
+							frappe.model.set_value(cdt, cdn, 'customer', customer);
+							frappe.model.set_value(cdt, cdn, 'grand_total', grand_total);
+						}
+					});
+				}
+			});
+		}
 	}
 });
 
-cur_frm.fields_dict['sales_orders'].grid.get_field("sales_order").get_query = function() {
-	return{
-		filters: [
-			['Sales Order','docstatus', '=' ,1]
-		]
-	}
-};
-
 frappe.tour['Production Plan'] = [
 	{
 		fieldname: "get_items_from",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json
index 232f1cb..0d0fd5e 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.json
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json
@@ -228,10 +228,10 @@
   },
   {
    "default": "0",
-   "description": "To know more about projected quantity, <a href=\"https://erpnext.com/docs/user/manual/en/stock/projected-quantity\" style=\"text-decoration: underline;\" target=\"_blank\">click here</a>.",
+   "description": "If enabled, the system won't create material requests for the available items.",
    "fieldname": "ignore_existing_ordered_qty",
    "fieldtype": "Check",
-   "label": "Ignore Existing Projected Quantity"
+   "label": "Ignore Available Stock"
   },
   {
    "fieldname": "column_break_25",
@@ -339,7 +339,7 @@
    "depends_on": "eval:doc.get_items_from == 'Sales Order'",
    "fieldname": "combine_items",
    "fieldtype": "Check",
-   "label": "Consolidate Items"
+   "label": "Consolidate Sales Order Items"
   },
   {
    "fieldname": "section_break_25",
@@ -399,7 +399,7 @@
   },
   {
    "default": "0",
-   "description": "System consider the projected quantity to check available or will be available sub-assembly items ",
+   "description": "If this checkbox is enabled, then the system won\u2019t run the MRP for the available sub-assembly items.",
    "fieldname": "skip_available_sub_assembly_item",
    "fieldtype": "Check",
    "label": "Skip Available Sub Assembly Items"
@@ -422,7 +422,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2023-05-22 23:36:31.770517",
+ "modified": "2023-07-28 13:37:43.926686",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Production Plan",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index d8cc8f6..261aa76 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -39,6 +39,36 @@
 		self.set_status()
 		self._rename_temporary_references()
 		validate_uom_is_integer(self, "stock_uom", "planned_qty")
+		self.validate_sales_orders()
+
+	@frappe.whitelist()
+	def validate_sales_orders(self, sales_order=None):
+		sales_orders = []
+
+		if sales_order:
+			sales_orders.append(sales_order)
+		else:
+			sales_orders = [row.sales_order for row in self.sales_orders if row.sales_order]
+
+		data = sales_order_query(filters={"company": self.company, "sales_orders": sales_orders})
+
+		title = _("Production Plan Already Submitted")
+		if not data:
+			msg = _("No items are available in the sales order {0} for production").format(sales_orders[0])
+			if len(sales_orders) > 1:
+				sales_orders = ", ".join(sales_orders)
+				msg = _("No items are available in sales orders {0} for production").format(sales_orders)
+
+			frappe.throw(msg, title=title)
+
+		data = [d[0] for d in data]
+
+		for sales_order in sales_orders:
+			if sales_order not in data:
+				frappe.throw(
+					_("No items are available in the sales order {0} for production").format(sales_order),
+					title=title,
+				)
 
 	def set_pending_qty_in_row_without_reference(self):
 		"Set Pending Qty in independent rows (not from SO or MR)."
@@ -205,6 +235,7 @@
 				).as_("pending_qty"),
 				so_item.description,
 				so_item.name,
+				so_item.bom_no,
 			)
 			.distinct()
 			.where(
@@ -342,7 +373,7 @@
 					"item_code": data.item_code,
 					"description": data.description or item_details.description,
 					"stock_uom": item_details and item_details.stock_uom or "",
-					"bom_no": item_details and item_details.bom_no or "",
+					"bom_no": data.bom_no or item_details and item_details.bom_no or "",
 					"planned_qty": data.pending_qty,
 					"pending_qty": data.pending_qty,
 					"planned_start_date": now_datetime(),
@@ -401,11 +432,50 @@
 
 	def on_submit(self):
 		self.update_bin_qty()
+		self.update_sales_order()
 
 	def on_cancel(self):
 		self.db_set("status", "Cancelled")
 		self.delete_draft_work_order()
 		self.update_bin_qty()
+		self.update_sales_order()
+
+	def update_sales_order(self):
+		sales_orders = [row.sales_order for row in self.po_items if row.sales_order]
+		if sales_orders:
+			so_wise_planned_qty = self.get_so_wise_planned_qty(sales_orders)
+
+			for row in self.po_items:
+				if not row.sales_order and not row.sales_order_item:
+					continue
+
+				key = (row.sales_order, row.sales_order_item)
+				frappe.db.set_value(
+					"Sales Order Item",
+					row.sales_order_item,
+					"production_plan_qty",
+					flt(so_wise_planned_qty.get(key)),
+				)
+
+	@staticmethod
+	def get_so_wise_planned_qty(sales_orders):
+		so_wise_planned_qty = frappe._dict()
+		data = frappe.get_all(
+			"Production Plan Item",
+			fields=["sales_order", "sales_order_item", "SUM(planned_qty) as qty"],
+			filters={
+				"sales_order": ("in", sales_orders),
+				"docstatus": 1,
+				"sales_order_item": ("is", "set"),
+			},
+			group_by="sales_order, sales_order_item",
+		)
+
+		for row in data:
+			key = (row.sales_order, row.sales_order_item)
+			so_wise_planned_qty[key] = row.qty
+
+		return so_wise_planned_qty
 
 	def update_bin_qty(self):
 		for d in self.mr_items:
@@ -719,6 +789,9 @@
 		sub_assembly_items_store = []  # temporary store to process all subassembly items
 
 		for row in self.po_items:
+			if self.skip_available_sub_assembly_item and not row.warehouse:
+				frappe.throw(_("Row #{0}: Please select the FG Warehouse in Assembly Items").format(row.idx))
+
 			if not row.item_code:
 				frappe.throw(_("Row #{0}: Please select Item Code in Assembly Items").format(row.idx))
 
@@ -1142,7 +1215,7 @@
 			& (so.docstatus == 1)
 			& (so.status.notin(["Stopped", "Closed"]))
 			& (so.company == self.company)
-			& (so_item.qty > so_item.work_order_qty)
+			& (so_item.qty > so_item.production_plan_qty)
 		)
 	)
 
@@ -1566,7 +1639,6 @@
 def get_raw_materials_of_sub_assembly_items(
 	item_details, company, bom_no, include_non_stock_items, sub_assembly_items, planned_qty=1
 ):
-
 	bei = frappe.qb.DocType("BOM Item")
 	bom = frappe.qb.DocType("BOM")
 	item = frappe.qb.DocType("Item")
@@ -1609,7 +1681,10 @@
 
 	for item in items:
 		key = (item.item_code, item.bom_no)
-		if item.bom_no and key in sub_assembly_items:
+		if item.bom_no and key not in sub_assembly_items:
+			continue
+
+		if item.bom_no:
 			planned_qty = flt(sub_assembly_items[key])
 			get_raw_materials_of_sub_assembly_items(
 				item_details,
@@ -1626,3 +1701,42 @@
 			item_details.setdefault(item.get("item_code"), item)
 
 	return item_details
+
+
+@frappe.whitelist()
+def sales_order_query(
+	doctype=None, txt=None, searchfield=None, start=None, page_len=None, filters=None
+):
+	frappe.has_permission("Production Plan", throw=True)
+
+	if not filters:
+		filters = {}
+
+	so_table = frappe.qb.DocType("Sales Order")
+	table = frappe.qb.DocType("Sales Order Item")
+
+	query = (
+		frappe.qb.from_(so_table)
+		.join(table)
+		.on(table.parent == so_table.name)
+		.select(table.parent)
+		.distinct()
+		.where((table.qty > table.production_plan_qty) & (table.docstatus == 1))
+	)
+
+	if filters.get("company"):
+		query = query.where(so_table.company == filters.get("company"))
+
+	if filters.get("sales_orders"):
+		query = query.where(so_table.name.isin(filters.get("sales_orders")))
+
+	if txt:
+		query = query.where(table.item_code.like(f"{txt}%"))
+
+	if page_len:
+		query = query.limit(page_len)
+
+	if start:
+		query = query.offset(start)
+
+	return query.run()
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index f60dbfc..2871a29 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -225,6 +225,102 @@
 
 		self.assertEqual(sales_orders, [])
 
+	def test_donot_allow_to_make_multiple_pp_against_same_so(self):
+		item = "Test SO Production Item 1"
+		create_item(item)
+
+		raw_material = "Test SO RM Production Item 1"
+		create_item(raw_material)
+
+		if not frappe.db.get_value("BOM", {"item": item}):
+			make_bom(item=item, raw_materials=[raw_material])
+
+		so = make_sales_order(item_code=item, qty=4)
+		pln = frappe.new_doc("Production Plan")
+		pln.company = so.company
+		pln.get_items_from = "Sales Order"
+
+		pln.append(
+			"sales_orders",
+			{
+				"sales_order": so.name,
+				"sales_order_date": so.transaction_date,
+				"customer": so.customer,
+				"grand_total": so.grand_total,
+			},
+		)
+
+		pln.get_so_items()
+		pln.submit()
+
+		pln = frappe.new_doc("Production Plan")
+		pln.company = so.company
+		pln.get_items_from = "Sales Order"
+
+		pln.append(
+			"sales_orders",
+			{
+				"sales_order": so.name,
+				"sales_order_date": so.transaction_date,
+				"customer": so.customer,
+				"grand_total": so.grand_total,
+			},
+		)
+
+		pln.get_so_items()
+		self.assertRaises(frappe.ValidationError, pln.save)
+
+	def test_so_based_bill_of_material(self):
+		item = "Test SO Production Item 1"
+		create_item(item)
+
+		raw_material = "Test SO RM Production Item 1"
+		create_item(raw_material)
+
+		bom1 = make_bom(item=item, raw_materials=[raw_material])
+
+		so = make_sales_order(item_code=item, qty=4)
+
+		# Create new BOM and assign to new sales order
+		bom2 = make_bom(item=item, raw_materials=[raw_material])
+		so2 = make_sales_order(item_code=item, qty=4)
+
+		pln1 = frappe.new_doc("Production Plan")
+		pln1.company = so.company
+		pln1.get_items_from = "Sales Order"
+
+		pln1.append(
+			"sales_orders",
+			{
+				"sales_order": so.name,
+				"sales_order_date": so.transaction_date,
+				"customer": so.customer,
+				"grand_total": so.grand_total,
+			},
+		)
+
+		pln1.get_so_items()
+
+		self.assertEqual(pln1.po_items[0].bom_no, bom1.name)
+
+		pln2 = frappe.new_doc("Production Plan")
+		pln2.company = so2.company
+		pln2.get_items_from = "Sales Order"
+
+		pln2.append(
+			"sales_orders",
+			{
+				"sales_order": so2.name,
+				"sales_order_date": so2.transaction_date,
+				"customer": so2.customer,
+				"grand_total": so2.grand_total,
+			},
+		)
+
+		pln2.get_so_items()
+
+		self.assertEqual(pln2.po_items[0].bom_no, bom2.name)
+
 	def test_production_plan_combine_items(self):
 		"Test combining FG items in Production Plan."
 		item = "Test Production Item 1"
diff --git a/erpnext/quality_management/doctype/quality_goal_objective/quality_goal_objective.json b/erpnext/quality_management/doctype/quality_goal_objective/quality_goal_objective.json
index e3dbd66..010888d 100644
--- a/erpnext/quality_management/doctype/quality_goal_objective/quality_goal_objective.json
+++ b/erpnext/quality_management/doctype/quality_goal_objective/quality_goal_objective.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "autoname": "format:{####}",
  "creation": "2019-05-26 15:03:43.996455",
  "doctype": "DocType",
@@ -12,7 +13,6 @@
  ],
  "fields": [
   {
-   "fetch_from": "goal.objective",
    "fieldname": "objective",
    "fieldtype": "Text",
    "in_list_view": 1,
@@ -38,14 +38,17 @@
   }
  ],
  "istable": 1,
- "modified": "2019-05-26 16:12:54.832058",
+ "links": [],
+ "modified": "2023-07-28 18:10:23.351246",
  "modified_by": "Administrator",
  "module": "Quality Management",
  "name": "Quality Goal Objective",
+ "naming_rule": "Expression",
  "owner": "Administrator",
  "permissions": [],
  "quick_entry": 1,
  "sort_field": "modified",
  "sort_order": "DESC",
+ "states": [],
  "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
index 5c7e10a..07565c3 100644
--- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json
+++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
@@ -84,6 +84,7 @@
   "actual_qty",
   "ordered_qty",
   "planned_qty",
+  "production_plan_qty",
   "column_break_69",
   "work_order_qty",
   "delivered_qty",
@@ -882,12 +883,19 @@
    "print_hide": 1,
    "read_only": 1,
    "report_hide": 1
+  },
+  {
+   "fieldname": "production_plan_qty",
+   "fieldtype": "Float",
+   "label": "Production Plan Qty",
+   "no_copy": 1,
+   "read_only": 1
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-04-04 10:44:05.707488",
+ "modified": "2023-07-28 14:56:42.031636",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Sales Order Item",
diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js
index 8b37e40..720d142 100644
--- a/erpnext/selling/page/point_of_sale/pos_controller.js
+++ b/erpnext/selling/page/point_of_sale/pos_controller.js
@@ -605,7 +605,6 @@
 				i => i.item_code === item_code
 					&& (!has_batch_no || (has_batch_no && i.batch_no === batch_no))
 					&& (i.uom === uom)
-					&& (i.rate == rate)
 			);
 		}
 
diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py
index f2c2e27..d60e9b5 100644
--- a/erpnext/stock/report/stock_balance/stock_balance.py
+++ b/erpnext/stock/report/stock_balance/stock_balance.py
@@ -446,10 +446,9 @@
 				{
 					"label": _("Valuation Rate"),
 					"fieldname": "val_rate",
-					"fieldtype": "Currency",
+					"fieldtype": "Float",
 					"width": 90,
 					"convertible": "rate",
-					"options": "currency",
 				},
 				{
 					"label": _("Reserved Stock"),
diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py
index 77bc4e0..ed28ed3 100644
--- a/erpnext/stock/report/stock_ledger/stock_ledger.py
+++ b/erpnext/stock/report/stock_ledger/stock_ledger.py
@@ -196,7 +196,7 @@
 			{
 				"label": _("Avg Rate (Balance Stock)"),
 				"fieldname": "valuation_rate",
-				"fieldtype": "Currency",
+				"fieldtype": "Float",
 				"width": 180,
 				"options": "Company:company:default_currency",
 				"convertible": "rate",
@@ -204,7 +204,7 @@
 			{
 				"label": _("Valuation Rate"),
 				"fieldname": "in_out_rate",
-				"fieldtype": "Currency",
+				"fieldtype": "Float",
 				"width": 140,
 				"options": "Company:company:default_currency",
 				"convertible": "rate",