Merge pull request #40095 from GursheenK/unique-gl-account-for-plaid-bank-accounts

fix: unique GL account for plaid bank accounts
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
index 295cd98..5258214 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
@@ -11,6 +11,10 @@
 from frappe.model.document import Document
 from frappe.utils import cstr
 
+from erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger import (
+	get_allowed_types_from_settings,
+)
+
 
 class AccountingDimension(Document):
 	# begin: auto-generated types
@@ -106,6 +110,7 @@
 
 	doc_count = len(get_accounting_dimensions())
 	count = 0
+	repostable_doctypes = get_allowed_types_from_settings()
 
 	for doctype in doclist:
 
@@ -121,6 +126,7 @@
 			"options": doc.document_type,
 			"insert_after": insert_after_field,
 			"owner": "Administrator",
+			"allow_on_submit": 1 if doctype in repostable_doctypes else 0,
 		}
 
 		meta = frappe.get_meta(doctype, cached=False)
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js
index 07fb5e8..72469ea 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.js
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js
@@ -14,6 +14,25 @@
 	refresh: function(frm) {
 		erpnext.toggle_naming_series();
 
+		if (frm.doc.repost_required && frm.doc.docstatus===1) {
+			frm.set_intro(__("Accounting entries for this Journal Entry need to be reposted. Please click on 'Repost' button to update."));
+			frm.add_custom_button(__('Repost Accounting Entries'),
+				() => {
+					frm.call({
+						doc: frm.doc,
+						method: 'repost_accounting_entries',
+						freeze: true,
+						freeze_message: __('Reposting...'),
+						callback: (r) => {
+							if (!r.exc) {
+								frappe.msgprint(__('Accounting Entries are reposted.'));
+								frm.refresh();
+							}
+						}
+					});
+				}).removeClass('btn-default').addClass('btn-warning');
+		}
+
 		if(frm.doc.docstatus > 0) {
 			frm.add_custom_button(__('Ledger'), function() {
 				frappe.route_options = {
@@ -184,7 +203,6 @@
 	$.each(r, function(i, d) {
 		var row = frappe.model.add_child(doc, "Journal Entry Account", "accounts");
 		frappe.model.set_value(row.doctype, row.name, "account", d.account)
-		frappe.model.set_value(row.doctype, row.name, "balance", d.balance)
 	});
 	refresh_field("accounts");
 }
@@ -193,7 +211,6 @@
 	onload() {
 		this.load_defaults();
 		this.setup_queries();
-		this.setup_balance_formatter();
 		erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype);
 	}
 
@@ -292,19 +309,6 @@
 
 	}
 
-	setup_balance_formatter() {
-		const formatter = function(value, df, options, doc) {
-			var currency = frappe.meta.get_field_currency(df, doc);
-			var dr_or_cr = value ? ('<label>' + (value > 0.0 ? __("Dr") : __("Cr")) + '</label>') : "";
-			return "<div style='text-align: right'>"
-				+ ((value==null || value==="") ? "" : format_currency(Math.abs(value), currency))
-				+ " " + dr_or_cr
-				+ "</div>";
-		};
-		this.frm.fields_dict.accounts.grid.update_docfield_property('balance', 'formatter', formatter);
-		this.frm.fields_dict.accounts.grid.update_docfield_property('party_balance', 'formatter', formatter);
-	}
-
 	reference_name(doc, cdt, cdn) {
 		var d = frappe.get_doc(cdt, cdn);
 
@@ -400,23 +404,22 @@
 		if(!d.account && d.party_type && d.party) {
 			if(!frm.doc.company) frappe.throw(__("Please select Company"));
 			return frm.call({
-				method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_party_account_and_balance",
+				method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_party_account_and_currency",
 				child: d,
 				args: {
 					company: frm.doc.company,
 					party_type: d.party_type,
 					party: d.party,
-					cost_center: d.cost_center
 				}
 			});
 		}
 	},
 	cost_center: function(frm, dt, dn) {
-		erpnext.journal_entry.set_account_balance(frm, dt, dn);
+		erpnext.journal_entry.set_account_details(frm, dt, dn);
 	},
 
 	account: function(frm, dt, dn) {
-		erpnext.journal_entry.set_account_balance(frm, dt, dn);
+		erpnext.journal_entry.set_account_details(frm, dt, dn);
 	},
 
 	debit_in_account_currency: function(frm, cdt, cdn) {
@@ -600,14 +603,14 @@
 });
 
 $.extend(erpnext.journal_entry, {
-	set_account_balance: function(frm, dt, dn) {
+	set_account_details: function(frm, dt, dn) {
 		var d = locals[dt][dn];
 		if(d.account) {
 			if(!frm.doc.company) frappe.throw(__("Please select Company first"));
 			if(!frm.doc.posting_date) frappe.throw(__("Please select Posting Date first"));
 
 			return frappe.call({
-				method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_account_balance_and_party_type",
+				method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_account_details_and_party_type",
 				args: {
 					account: d.account,
 					date: frm.doc.posting_date,
@@ -615,7 +618,6 @@
 					debit: flt(d.debit_in_account_currency),
 					credit: flt(d.credit_in_account_currency),
 					exchange_rate: d.exchange_rate,
-					cost_center: d.cost_center
 				},
 				callback: function(r) {
 					if(r.message) {
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.json b/erpnext/accounts/doctype/journal_entry/journal_entry.json
index 906760e..a0fcaf9 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.json
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.json
@@ -64,7 +64,8 @@
   "stock_entry",
   "subscription_section",
   "auto_repeat",
-  "amended_from"
+  "amended_from",
+  "repost_required"
  ],
  "fields": [
   {
@@ -543,6 +544,15 @@
    "label": "Is System Generated",
    "no_copy": 1,
    "read_only": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "repost_required",
+   "fieldtype": "Check",
+   "hidden": 1,
+   "label": "Repost Required",
+   "print_hide": 1,
+   "read_only": 1
   }
  ],
  "icon": "fa fa-file-text",
@@ -558,6 +568,7 @@
   }
  ],
  "modified": "2023-11-23 12:11:04.128015",
+
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Journal Entry",
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index 18cf4ed..2a02cd7 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -13,6 +13,10 @@
 from erpnext.accounts.doctype.invoice_discounting.invoice_discounting import (
 	get_party_account_based_on_invoice_discounting,
 )
+from erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger import (
+	validate_docs_for_deferred_accounting,
+	validate_docs_for_voucher_types,
+)
 from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import (
 	get_party_tax_withholding_details,
 )
@@ -140,7 +144,6 @@
 		self.set_print_format_fields()
 		self.validate_credit_debit_note()
 		self.validate_empty_accounts_table()
-		self.set_account_and_party_balance()
 		self.validate_inter_company_accounts()
 		self.validate_depr_entry_voucher_type()
 
@@ -150,6 +153,10 @@
 		if not self.title:
 			self.title = self.get_title()
 
+	def validate_for_repost(self):
+		validate_docs_for_voucher_types(["Journal Entry"])
+		validate_docs_for_deferred_accounting([self.name], [])
+
 	def submit(self):
 		if len(self.accounts) > 100:
 			msgprint(_("The task has been enqueued as a background job."), alert=True)
@@ -173,6 +180,15 @@
 		self.update_inter_company_jv()
 		self.update_invoice_discounting()
 
+	def on_update_after_submit(self):
+		if hasattr(self, "repost_required"):
+			self.needs_repost = self.check_if_fields_updated(
+				fields_to_check=[], child_tables={"accounts": []}
+			)
+			if self.needs_repost:
+				self.validate_for_repost()
+				self.db_set("repost_required", self.needs_repost)
+
 	def on_cancel(self):
 		# References for this Journal are removed on the `on_cancel` event in accounts_controller
 		super(JournalEntry, self).on_cancel()
@@ -1152,21 +1168,6 @@
 		if not self.get("accounts"):
 			frappe.throw(_("Accounts table cannot be blank."))
 
-	def set_account_and_party_balance(self):
-		account_balance = {}
-		party_balance = {}
-		for d in self.get("accounts"):
-			if d.account not in account_balance:
-				account_balance[d.account] = get_balance_on(account=d.account, date=self.posting_date)
-
-			if (d.party_type, d.party) not in party_balance:
-				party_balance[(d.party_type, d.party)] = get_balance_on(
-					party_type=d.party_type, party=d.party, date=self.posting_date, company=self.company
-				)
-
-			d.account_balance = account_balance[d.account]
-			d.party_balance = party_balance[(d.party_type, d.party)]
-
 
 @frappe.whitelist()
 def get_default_bank_cash_account(
@@ -1334,8 +1335,6 @@
 			"account_type": frappe.get_cached_value("Account", args.get("party_account"), "account_type"),
 			"account_currency": args.get("party_account_currency")
 			or get_account_currency(args.get("party_account")),
-			"balance": get_balance_on(args.get("party_account")),
-			"party_balance": get_balance_on(party=args.get("party"), party_type=args.get("party_type")),
 			"exchange_rate": exchange_rate,
 			args.get("amount_field_party"): args.get("amount"),
 			"is_advance": args.get("is_advance"),
@@ -1483,30 +1482,23 @@
 
 
 @frappe.whitelist()
-def get_party_account_and_balance(company, party_type, party, cost_center=None):
+def get_party_account_and_currency(company, party_type, party):
 	if not frappe.has_permission("Account"):
 		frappe.msgprint(_("No Permission"), raise_exception=1)
 
 	account = get_party_account(party_type, party, company)
 
-	account_balance = get_balance_on(account=account, cost_center=cost_center)
-	party_balance = get_balance_on(
-		party_type=party_type, party=party, company=company, cost_center=cost_center
-	)
-
 	return {
 		"account": account,
-		"balance": account_balance,
-		"party_balance": party_balance,
 		"account_currency": frappe.get_cached_value("Account", account, "account_currency"),
 	}
 
 
 @frappe.whitelist()
-def get_account_balance_and_party_type(
-	account, date, company, debit=None, credit=None, exchange_rate=None, cost_center=None
+def get_account_details_and_party_type(
+	account, date, company, debit=None, credit=None, exchange_rate=None
 ):
-	"""Returns dict of account balance and party type to be set in Journal Entry on selection of account."""
+	"""Returns dict of account details and party type to be set in Journal Entry on selection of account."""
 	if not frappe.has_permission("Account"):
 		frappe.msgprint(_("No Permission"), raise_exception=1)
 
@@ -1526,7 +1518,6 @@
 		party_type = ""
 
 	grid_values = {
-		"balance": get_balance_on(account, date, cost_center=cost_center),
 		"party_type": party_type,
 		"account_type": account_details.account_type,
 		"account_currency": account_details.account_currency or company_currency,
diff --git a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py
index a6e920b..798d3bb 100644
--- a/erpnext/accounts/doctype/journal_entry/test_journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/test_journal_entry.py
@@ -166,43 +166,37 @@
 		jv.get("accounts")[1].credit_in_account_currency = 5000
 		jv.submit()
 
-		gl_entries = frappe.db.sql(
-			"""select account, account_currency, debit, credit,
-			debit_in_account_currency, credit_in_account_currency
-			from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s
-			order by account asc""",
-			jv.name,
-			as_dict=1,
-		)
+		self.voucher_no = jv.name
 
-		self.assertTrue(gl_entries)
+		self.fields = [
+			"account",
+			"account_currency",
+			"debit",
+			"debit_in_account_currency",
+			"credit",
+			"credit_in_account_currency",
+		]
 
-		expected_values = {
-			"_Test Bank USD - _TC": {
-				"account_currency": "USD",
-				"debit": 5000,
-				"debit_in_account_currency": 100,
-				"credit": 0,
-				"credit_in_account_currency": 0,
-			},
-			"_Test Bank - _TC": {
+		self.expected_gle = [
+			{
+				"account": "_Test Bank - _TC",
 				"account_currency": "INR",
 				"debit": 0,
 				"debit_in_account_currency": 0,
 				"credit": 5000,
 				"credit_in_account_currency": 5000,
 			},
-		}
+			{
+				"account": "_Test Bank USD - _TC",
+				"account_currency": "USD",
+				"debit": 5000,
+				"debit_in_account_currency": 100,
+				"credit": 0,
+				"credit_in_account_currency": 0,
+			},
+		]
 
-		for field in (
-			"account_currency",
-			"debit",
-			"debit_in_account_currency",
-			"credit",
-			"credit_in_account_currency",
-		):
-			for i, gle in enumerate(gl_entries):
-				self.assertEqual(expected_values[gle.account][field], gle[field])
+		self.check_gl_entries()
 
 		# cancel
 		jv.cancel()
@@ -228,43 +222,37 @@
 		rjv.posting_date = nowdate()
 		rjv.submit()
 
-		gl_entries = frappe.db.sql(
-			"""select account, account_currency, debit, credit,
-			debit_in_account_currency, credit_in_account_currency
-			from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s
-			order by account asc""",
-			rjv.name,
-			as_dict=1,
-		)
+		self.voucher_no = rjv.name
 
-		self.assertTrue(gl_entries)
+		self.fields = [
+			"account",
+			"account_currency",
+			"debit",
+			"credit",
+			"debit_in_account_currency",
+			"credit_in_account_currency",
+		]
 
-		expected_values = {
-			"_Test Bank USD - _TC": {
+		self.expected_gle = [
+			{
+				"account": "_Test Bank USD - _TC",
 				"account_currency": "USD",
 				"debit": 0,
 				"debit_in_account_currency": 0,
 				"credit": 5000,
 				"credit_in_account_currency": 100,
 			},
-			"Sales - _TC": {
+			{
+				"account": "Sales - _TC",
 				"account_currency": "INR",
 				"debit": 5000,
 				"debit_in_account_currency": 5000,
 				"credit": 0,
 				"credit_in_account_currency": 0,
 			},
-		}
+		]
 
-		for field in (
-			"account_currency",
-			"debit",
-			"debit_in_account_currency",
-			"credit",
-			"credit_in_account_currency",
-		):
-			for i, gle in enumerate(gl_entries):
-				self.assertEqual(expected_values[gle.account][field], gle[field])
+		self.check_gl_entries()
 
 	def test_disallow_change_in_account_currency_for_a_party(self):
 		# create jv in USD
@@ -344,23 +332,25 @@
 		jv.insert()
 		jv.submit()
 
-		expected_values = {
-			"_Test Cash - _TC": {"cost_center": cost_center},
-			"_Test Bank - _TC": {"cost_center": cost_center},
-		}
+		self.voucher_no = jv.name
 
-		gl_entries = frappe.db.sql(
-			"""select account, cost_center, debit, credit
-			from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s
-			order by account asc""",
-			jv.name,
-			as_dict=1,
-		)
+		self.fields = [
+			"account",
+			"cost_center",
+		]
 
-		self.assertTrue(gl_entries)
+		self.expected_gle = [
+			{
+				"account": "_Test Bank - _TC",
+				"cost_center": cost_center,
+			},
+			{
+				"account": "_Test Cash - _TC",
+				"cost_center": cost_center,
+			},
+		]
 
-		for gle in gl_entries:
-			self.assertEqual(expected_values[gle.account]["cost_center"], gle.cost_center)
+		self.check_gl_entries()
 
 	def test_jv_with_project(self):
 		from erpnext.projects.doctype.project.test_project import make_project
@@ -387,23 +377,22 @@
 		jv.insert()
 		jv.submit()
 
-		expected_values = {
-			"_Test Cash - _TC": {"project": project_name},
-			"_Test Bank - _TC": {"project": project_name},
-		}
+		self.voucher_no = jv.name
 
-		gl_entries = frappe.db.sql(
-			"""select account, project, debit, credit
-			from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s
-			order by account asc""",
-			jv.name,
-			as_dict=1,
-		)
+		self.fields = ["account", "project"]
 
-		self.assertTrue(gl_entries)
+		self.expected_gle = [
+			{
+				"account": "_Test Bank - _TC",
+				"project": project_name,
+			},
+			{
+				"account": "_Test Cash - _TC",
+				"project": project_name,
+			},
+		]
 
-		for gle in gl_entries:
-			self.assertEqual(expected_values[gle.account]["project"], gle.project)
+		self.check_gl_entries()
 
 	def test_jv_account_and_party_balance_with_cost_centre(self):
 		from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
@@ -426,6 +415,79 @@
 		account_balance = get_balance_on(account="_Test Bank - _TC", cost_center=cost_center)
 		self.assertEqual(expected_account_balance, account_balance)
 
+	def test_repost_accounting_entries(self):
+		from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
+
+		# Configure Repost Accounting Ledger for JVs
+		settings = frappe.get_doc("Repost Accounting Ledger Settings")
+		if not [x for x in settings.allowed_types if x.document_type == "Journal Entry"]:
+			settings.append("allowed_types", {"document_type": "Journal Entry", "allowed": True})
+		settings.save()
+
+		# Create JV with defaut cost center - _Test Cost Center
+		jv = make_journal_entry("_Test Cash - _TC", "_Test Bank - _TC", 100, save=False)
+		jv.multi_currency = 0
+		jv.submit()
+
+		# Check GL entries before reposting
+		self.voucher_no = jv.name
+
+		self.fields = [
+			"account",
+			"debit_in_account_currency",
+			"credit_in_account_currency",
+			"cost_center",
+		]
+
+		self.expected_gle = [
+			{
+				"account": "_Test Bank - _TC",
+				"debit_in_account_currency": 0,
+				"credit_in_account_currency": 100,
+				"cost_center": "_Test Cost Center - _TC",
+			},
+			{
+				"account": "_Test Cash - _TC",
+				"debit_in_account_currency": 100,
+				"credit_in_account_currency": 0,
+				"cost_center": "_Test Cost Center - _TC",
+			},
+		]
+
+		self.check_gl_entries()
+
+		# Change cost center for bank account - _Test Cost Center for BS Account
+		create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
+		jv.accounts[1].cost_center = "_Test Cost Center for BS Account - _TC"
+		jv.save()
+
+		# Check if repost flag gets set on update after submit
+		self.assertTrue(jv.repost_required)
+		jv.repost_accounting_entries()
+
+		# Check GL entries after reposting
+		jv.load_from_db()
+		self.expected_gle[0]["cost_center"] = "_Test Cost Center for BS Account - _TC"
+		self.check_gl_entries()
+
+	def check_gl_entries(self):
+		gl = frappe.qb.DocType("GL Entry")
+		query = frappe.qb.from_(gl)
+		for field in self.fields:
+			query = query.select(gl[field])
+
+		query = query.where(
+			(gl.voucher_type == "Journal Entry")
+			& (gl.voucher_no == self.voucher_no)
+			& (gl.is_cancelled == 0)
+		).orderby(gl.account)
+
+		gl_entries = query.run(as_dict=True)
+
+		for i in range(len(self.expected_gle)):
+			for field in self.fields:
+				self.assertEqual(self.expected_gle[i][field], gl_entries[i][field])
+
 
 def make_journal_entry(
 	account1,
diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
index 99e66e6..a61f8bf 100644
--- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
+++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
@@ -9,12 +9,10 @@
  "field_order": [
   "account",
   "account_type",
-  "balance",
   "col_break1",
   "bank_account",
   "party_type",
   "party",
-  "party_balance",
   "accounting_dimensions_section",
   "cost_center",
   "dimension_col_break",
@@ -64,17 +62,7 @@
    "print_hide": 1
   },
   {
-   "fieldname": "balance",
-   "fieldtype": "Currency",
-   "label": "Account Balance",
-   "no_copy": 1,
-   "oldfieldname": "balance",
-   "oldfieldtype": "Data",
-   "options": "account_currency",
-   "print_hide": 1,
-   "read_only": 1
-  },
-  {
+   "allow_on_submit": 1,
    "default": ":Company",
    "description": "If Income or Expense",
    "fieldname": "cost_center",
@@ -108,14 +96,6 @@
    "options": "party_type"
   },
   {
-   "fieldname": "party_balance",
-   "fieldtype": "Currency",
-   "label": "Party Balance",
-   "options": "account_currency",
-   "print_hide": 1,
-   "read_only": 1
-  },
-  {
    "fieldname": "currency_section",
    "fieldtype": "Section Break",
    "label": "Currency"
@@ -223,6 +203,7 @@
    "no_copy": 1
   },
   {
+   "allow_on_submit": 1,
    "fieldname": "project",
    "fieldtype": "Link",
    "label": "Project",
@@ -286,7 +267,7 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-12-03 23:21:22.205409",
+ "modified": "2024-02-05 01:10:50.224840",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Journal Entry Account",
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
index 62e2181..832b116 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -149,7 +149,7 @@
 	},
 
 	refresh: function(frm) {
-		erpnext.hide_company();
+		erpnext.hide_company(frm);
 		frm.events.hide_unhide_fields(frm);
 		frm.events.set_dynamic_labels(frm);
 		frm.events.show_general_ledger(frm);
diff --git a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json
index ad2889d..b92579e 100644
--- a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json
+++ b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.json
@@ -654,7 +654,7 @@
   {
    "depends_on": "eval:doc.use_serial_batch_fields === 1",
    "fieldname": "serial_no",
-   "fieldtype": "Small Text",
+   "fieldtype": "Text",
    "hidden": 1,
    "in_list_view": 1,
    "label": "Serial No",
@@ -853,7 +853,7 @@
  ],
  "istable": 1,
  "links": [],
- "modified": "2024-02-04 16:36:25.665743",
+ "modified": "2024-02-25 15:50:17.140269",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "POS Invoice Item",
diff --git a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.py b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.py
index 55a577b..c24db1d 100644
--- a/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.py
+++ b/erpnext/accounts/doctype/pos_invoice_item/pos_invoice_item.py
@@ -72,7 +72,7 @@
 		rate_with_margin: DF.Currency
 		sales_order: DF.Link | None
 		serial_and_batch_bundle: DF.Link | None
-		serial_no: DF.SmallText | None
+		serial_no: DF.Text | None
 		service_end_date: DF.Date | None
 		service_start_date: DF.Date | None
 		service_stop_date: DF.Date | None
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index c68ff83..3312f2e 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -724,6 +724,7 @@
 				"cash_bank_account",
 				"write_off_account",
 				"unrealized_profit_loss_account",
+				"is_opening",
 			]
 			child_tables = {"items": ("expense_account",), "taxes": ("account_head",)}
 			self.needs_repost = self.check_if_fields_updated(fields_to_check, child_tables)
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 3a930e0..3352e0d 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -447,7 +447,11 @@
 		# Updating stock ledger should always be called after updating prevdoc status,
 		# because updating reserved qty in bin depends upon updated delivered qty in SO
 		if self.update_stock == 1:
-			self.make_bundle_using_old_serial_batch_fields()
+			for table_name in ["items", "packed_items"]:
+				if not self.get(table_name):
+					continue
+
+				self.make_bundle_using_old_serial_batch_fields(table_name)
 			self.update_stock_ledger()
 
 		# this sequence because outstanding may get -ve
@@ -723,6 +727,7 @@
 				"write_off_account",
 				"loyalty_redemption_account",
 				"unrealized_profit_loss_account",
+				"is_opening",
 			]
 			child_tables = {
 				"items": ("income_account", "expense_account", "discount_account"),
diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
index d06c786..e7536e9 100644
--- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
+++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
@@ -625,7 +625,7 @@
   {
    "depends_on": "eval: doc.use_serial_batch_fields === 1 && parent.update_stock === 1",
    "fieldname": "serial_no",
-   "fieldtype": "Small Text",
+   "fieldtype": "Text",
    "label": "Serial No",
    "oldfieldname": "serial_no",
    "oldfieldtype": "Small Text"
@@ -926,7 +926,7 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2024-02-04 11:52:16.106541",
+ "modified": "2024-02-25 15:56:44.828634",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Sales Invoice Item",
diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py
index c71d08e..9be1b42 100644
--- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py
+++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.py
@@ -75,7 +75,7 @@
 		sales_invoice_item: DF.Data | None
 		sales_order: DF.Link | None
 		serial_and_batch_bundle: DF.Link | None
-		serial_no: DF.SmallText | None
+		serial_no: DF.Text | None
 		service_end_date: DF.Date | None
 		service_start_date: DF.Date | None
 		service_stop_date: DF.Date | None
diff --git a/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py b/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py
index f404d99..57f66dd 100644
--- a/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py
+++ b/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py
@@ -8,6 +8,7 @@
 from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
 from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
 from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
+from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
 
 
 class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase):
@@ -49,6 +50,16 @@
 		)
 		return pe
 
+	def create_sales_order(self):
+		so = make_sales_order(
+			company=self.company,
+			customer=self.customer,
+			item=self.item,
+			rate=100,
+			transaction_date=today(),
+		)
+		return so
+
 	def test_01_unreconcile_invoice(self):
 		si1 = self.create_sales_invoice()
 		si2 = self.create_sales_invoice()
@@ -314,3 +325,41 @@
 			),
 			1,
 		)
+
+	def test_05_unreconcile_order(self):
+		so = self.create_sales_order()
+
+		pe = self.create_payment_entry()
+		# Allocation payment against Sales Order
+		pe.paid_amount = 100
+		pe.append(
+			"references",
+			{"reference_doctype": so.doctype, "reference_name": so.name, "allocated_amount": 100},
+		)
+		pe.save().submit()
+
+		# Assert 'Advance Paid'
+		so.reload()
+		self.assertEqual(so.advance_paid, 100)
+
+		unreconcile = frappe.get_doc(
+			{
+				"doctype": "Unreconcile Payment",
+				"company": self.company,
+				"voucher_type": pe.doctype,
+				"voucher_no": pe.name,
+			}
+		)
+		unreconcile.add_references()
+		self.assertEqual(len(unreconcile.allocations), 1)
+		allocations = [x.reference_name for x in unreconcile.allocations]
+		self.assertEquals([so.name], allocations)
+		# unreconcile so
+		unreconcile.save().submit()
+
+		# Assert 'Advance Paid'
+		so.reload()
+		pe.reload()
+		self.assertEqual(so.advance_paid, 0)
+		self.assertEqual(len(pe.references), 0)
+		self.assertEqual(pe.unallocated_amount, 100)
diff --git a/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.py b/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.py
index 9b56952..664622f 100644
--- a/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.py
+++ b/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.py
@@ -82,6 +82,11 @@
 			update_voucher_outstanding(
 				alloc.reference_doctype, alloc.reference_name, alloc.account, alloc.party_type, alloc.party
 			)
+			if doc.doctype in frappe.get_hooks("advance_payment_payable_doctypes") + frappe.get_hooks(
+				"advance_payment_receivable_doctypes"
+			):
+				doc.set_total_advance_paid()
+
 			frappe.db.set_value("Unreconcile Payment Entries", alloc.name, "unlinked", True)
 
 
diff --git a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py
index 0e3acd7..b18570b 100644
--- a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py
+++ b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py
@@ -242,7 +242,7 @@
 				"width": 120,
 			},
 			{
-				"label": _("Tax Amount"),
+				"label": _("TDS Amount") if filters.get("party_type") == "Supplier" else _("TCS Amount"),
 				"fieldname": "tax_amount",
 				"fieldtype": "Float",
 				"width": 120,
diff --git a/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json
index 8eda441..f79a848 100644
--- a/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json
+++ b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json
@@ -107,7 +107,7 @@
   {
    "depends_on": "eval:doc.use_serial_batch_fields === 1",
    "fieldname": "serial_no",
-   "fieldtype": "Small Text",
+   "fieldtype": "Text",
    "hidden": 1,
    "label": "Serial No",
    "print_hide": 1
@@ -178,7 +178,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2024-02-04 16:41:09.239762",
+ "modified": "2024-02-25 15:57:35.007501",
  "modified_by": "Administrator",
  "module": "Assets",
  "name": "Asset Capitalization Stock Item",
diff --git a/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.py b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.py
index d2b075c..0f06cc7 100644
--- a/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.py
+++ b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.py
@@ -24,7 +24,7 @@
 		parentfield: DF.Data
 		parenttype: DF.Data
 		serial_and_batch_bundle: DF.Link | None
-		serial_no: DF.SmallText | None
+		serial_no: DF.Text | None
 		stock_qty: DF.Float
 		stock_uom: DF.Link
 		use_serial_batch_fields: DF.Check
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 32a5a61..9896cad 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -2453,27 +2453,20 @@
 		doc_before_update = self.get_doc_before_save()
 		accounting_dimensions = get_accounting_dimensions() + ["cost_center", "project"]
 
-		# Check if opening entry check updated
-		needs_repost = doc_before_update.get("is_opening") != self.is_opening
+		# Parent Level Accounts excluding party account
+		fields_to_check += accounting_dimensions
+		for field in fields_to_check:
+			if doc_before_update.get(field) != self.get(field):
+				return True
 
-		if not needs_repost:
-			# Parent Level Accounts excluding party account
-			fields_to_check += accounting_dimensions
-			for field in fields_to_check:
-				if doc_before_update.get(field) != self.get(field):
-					needs_repost = 1
-					break
+		# Check for child tables
+		for table in child_tables:
+			if check_if_child_table_updated(
+				doc_before_update.get(table), self.get(table), child_tables[table]
+			):
+				return True
 
-			if not needs_repost:
-				# Check for child tables
-				for table in child_tables:
-					needs_repost = check_if_child_table_updated(
-						doc_before_update.get(table), self.get(table), child_tables[table]
-					)
-					if needs_repost:
-						break
-
-		return needs_repost
+		return False
 
 	@frappe.whitelist()
 	def repost_accounting_entries(self):
@@ -3502,15 +3495,12 @@
 def check_if_child_table_updated(
 	child_table_before_update, child_table_after_update, fields_to_check
 ):
-	accounting_dimensions = get_accounting_dimensions() + ["cost_center", "project"]
-	# Check if any field affecting accounting entry is altered
-	for index, item in enumerate(child_table_after_update):
-		for field in fields_to_check:
-			if child_table_before_update[index].get(field) != item.get(field):
-				return True
+	fields_to_check = list(fields_to_check) + get_accounting_dimensions() + ["cost_center", "project"]
 
-		for dimension in accounting_dimensions:
-			if child_table_before_update[index].get(dimension) != item.get(dimension):
+	# Check if any field affecting accounting entry is altered
+	for index, item in enumerate(child_table_before_update):
+		for field in fields_to_check:
+			if child_table_after_update[index].get(field) != item.get(field):
 				return True
 
 	return False
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index c8d40ed..359d721 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -704,6 +704,9 @@
 def get_serial_and_batch_bundle(child, parent):
 	from erpnext.stock.serial_batch_bundle import SerialBatchCreation
 
+	if child.get("use_serial_batch_fields"):
+		return
+
 	if not frappe.db.get_single_value(
 		"Stock Settings", "auto_create_serial_and_batch_bundle_for_outward"
 	):
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index fdbfd10..a67fbdc 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -158,7 +158,7 @@
 				# remove extra whitespace and store one serial no on each line
 				row.serial_no = clean_serial_no_string(row.serial_no)
 
-	def make_bundle_using_old_serial_batch_fields(self):
+	def make_bundle_using_old_serial_batch_fields(self, table_name=None):
 		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 		from erpnext.stock.serial_batch_bundle import SerialBatchCreation
 
@@ -169,7 +169,9 @@
 		if frappe.flags.in_test and frappe.flags.use_serial_and_batch_fields:
 			return
 
-		table_name = "items"
+		if not table_name:
+			table_name = "items"
+
 		if self.doctype == "Asset Capitalization":
 			table_name = "stock_items"
 
@@ -192,6 +194,12 @@
 					qty = row.qty
 					type_of_transaction = "Inward"
 					warehouse = row.warehouse
+				elif table_name == "packed_items":
+					qty = row.qty
+					warehouse = row.warehouse
+					type_of_transaction = "Outward"
+					if self.is_return:
+						type_of_transaction = "Inward"
 				else:
 					qty = row.stock_qty if self.doctype != "Stock Entry" else row.transfer_qty
 					type_of_transaction = get_type_of_transaction(self, row)
diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.js b/erpnext/manufacturing/doctype/blanket_order/blanket_order.js
index 7b26a14..716af85 100644
--- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.js
+++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.js
@@ -18,7 +18,7 @@
 	},
 
 	refresh: function(frm) {
-		erpnext.hide_company();
+		erpnext.hide_company(frm);
 		if (frm.doc.customer && frm.doc.docstatus === 1 && frm.doc.to_date > frappe.datetime.get_today()) {
 			frm.add_custom_button(__("Sales Order"), function() {
 				frappe.model.open_mapped_doc({
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index 3daec20..35aebb9 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -239,12 +239,12 @@
 		for row in self.sub_operations:
 			self.total_completed_qty += row.completed_qty
 
-	def get_overlap_for(self, args, check_next_available_slot=False):
+	def get_overlap_for(self, args):
 		time_logs = []
 
-		time_logs.extend(self.get_time_logs(args, "Job Card Time Log", check_next_available_slot))
+		time_logs.extend(self.get_time_logs(args, "Job Card Time Log"))
 
-		time_logs.extend(self.get_time_logs(args, "Job Card Scheduled Time", check_next_available_slot))
+		time_logs.extend(self.get_time_logs(args, "Job Card Scheduled Time"))
 
 		if not time_logs:
 			return {}
@@ -269,7 +269,7 @@
 				self.workstation = workstation_time.get("workstation")
 				return workstation_time
 
-		return time_logs[-1]
+		return time_logs[0]
 
 	def has_overlap(self, production_capacity, time_logs):
 		overlap = False
@@ -308,7 +308,7 @@
 			return True
 		return overlap
 
-	def get_time_logs(self, args, doctype, check_next_available_slot=False):
+	def get_time_logs(self, args, doctype):
 		jc = frappe.qb.DocType("Job Card")
 		jctl = frappe.qb.DocType(doctype)
 
@@ -318,9 +318,6 @@
 			((jctl.from_time >= args.from_time) & (jctl.to_time <= args.to_time)),
 		]
 
-		if check_next_available_slot:
-			time_conditions.append(((jctl.from_time >= args.from_time) & (jctl.to_time >= args.to_time)))
-
 		query = (
 			frappe.qb.from_(jctl)
 			.from_(jc)
@@ -395,18 +392,28 @@
 
 	def validate_overlap_for_workstation(self, args, row):
 		# get the last record based on the to time from the job card
-		data = self.get_overlap_for(args, check_next_available_slot=True)
+		data = self.get_overlap_for(args)
+
 		if not self.workstation:
 			workstations = get_workstations(self.workstation_type)
 			if workstations:
 				# Get the first workstation
 				self.workstation = workstations[0]
 
+		if not data:
+			row.planned_start_time = args.from_time
+			return
+
 		if data:
 			if data.get("planned_start_time"):
-				row.planned_start_time = get_datetime(data.planned_start_time)
+				args.planned_start_time = get_datetime(data.planned_start_time)
 			else:
-				row.planned_start_time = get_datetime(data.to_time + get_mins_between_operations())
+				args.planned_start_time = get_datetime(data.to_time + get_mins_between_operations())
+
+			args.from_time = args.planned_start_time
+			args.to_time = add_to_date(args.planned_start_time, minutes=row.remaining_time_in_mins)
+
+			self.validate_overlap_for_workstation(args, row)
 
 	def check_workstation_time(self, row):
 		workstation_doc = frappe.get_cached_doc("Workstation", self.workstation)
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index efe9f53..c72232a 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -1822,6 +1822,113 @@
 		valuation_rate = sum([item.valuation_rate * item.transfer_qty for item in mce.items]) / 10
 		self.assertEqual(me.items[0].valuation_rate, valuation_rate)
 
+	def test_capcity_planning_for_workstation(self):
+		frappe.db.set_single_value(
+			"Manufacturing Settings",
+			{
+				"disable_capacity_planning": 0,
+				"capacity_planning_for_days": 1,
+				"mins_between_operations": 10,
+			},
+		)
+
+		properties = {"is_stock_item": 1, "valuation_rate": 100}
+		fg_item = make_item("Test FG Item For Capacity Planning", properties).name
+
+		rm_item = make_item("Test RM Item For Capacity Planning", properties).name
+
+		workstation = "Test Workstation For Capacity Planning"
+		if not frappe.db.exists("Workstation", workstation):
+			make_workstation(workstation=workstation, production_capacity=1)
+
+		operation = "Test Operation For Capacity Planning"
+		if not frappe.db.exists("Operation", operation):
+			make_operation(operation=operation, workstation=workstation)
+
+		bom_doc = make_bom(
+			item=fg_item,
+			source_warehouse="Stores - _TC",
+			raw_materials=[rm_item],
+			with_operations=1,
+			do_not_submit=True,
+		)
+
+		bom_doc.append(
+			"operations",
+			{"operation": operation, "time_in_mins": 1420, "hour_rate": 100, "workstation": workstation},
+		)
+		bom_doc.submit()
+
+		# 1st Work Order,
+		# Capacity to run parallel the operation 'Test Operation For Capacity Planning' is 2
+		wo_doc = make_wo_order_test_record(
+			production_item=fg_item, qty=1, planned_start_date="2024-02-25 00:00:00", do_not_submit=1
+		)
+
+		wo_doc.submit()
+		job_cards = frappe.get_all(
+			"Job Card",
+			filters={"work_order": wo_doc.name},
+		)
+
+		self.assertEqual(len(job_cards), 1)
+
+		# 2nd Work Order,
+		wo_doc = make_wo_order_test_record(
+			production_item=fg_item, qty=1, planned_start_date="2024-02-25 00:00:00", do_not_submit=1
+		)
+
+		wo_doc.submit()
+		job_cards = frappe.get_all(
+			"Job Card",
+			filters={"work_order": wo_doc.name},
+		)
+
+		self.assertEqual(len(job_cards), 1)
+
+		# 3rd Work Order, capacity is full
+		wo_doc = make_wo_order_test_record(
+			production_item=fg_item, qty=1, planned_start_date="2024-02-25 00:00:00", do_not_submit=1
+		)
+
+		self.assertRaises(CapacityError, wo_doc.submit)
+
+		frappe.db.set_single_value(
+			"Manufacturing Settings", {"disable_capacity_planning": 1, "mins_between_operations": 0}
+		)
+
+
+def make_operation(**kwargs):
+	kwargs = frappe._dict(kwargs)
+
+	operation_doc = frappe.get_doc(
+		{
+			"doctype": "Operation",
+			"name": kwargs.operation,
+			"workstation": kwargs.workstation,
+		}
+	)
+	operation_doc.insert()
+
+	return operation_doc
+
+
+def make_workstation(**kwargs):
+	kwargs = frappe._dict(kwargs)
+
+	workstation_doc = frappe.get_doc(
+		{
+			"doctype": "Workstation",
+			"workstation_name": kwargs.workstation,
+			"workstation_type": kwargs.workstation_type,
+			"production_capacity": kwargs.production_capacity or 0,
+			"hour_rate": kwargs.hour_rate or 100,
+		}
+	)
+	workstation_doc.insert()
+
+	return workstation_doc
+
 
 def prepare_boms_for_sub_assembly_test():
 	if not frappe.db.exists("BOM", {"item": "Test Final SF Item 1"}):
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index 39beb36..5e22707 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -242,8 +242,12 @@
 	def calculate_operating_cost(self):
 		self.planned_operating_cost, self.actual_operating_cost = 0.0, 0.0
 		for d in self.get("operations"):
-			d.planned_operating_cost = flt(d.hour_rate) * (flt(d.time_in_mins) / 60.0)
-			d.actual_operating_cost = flt(d.hour_rate) * (flt(d.actual_operation_time) / 60.0)
+			d.planned_operating_cost = flt(
+				flt(d.hour_rate) * (flt(d.time_in_mins) / 60.0), d.precision("planned_operating_cost")
+			)
+			d.actual_operating_cost = flt(
+				flt(d.hour_rate) * (flt(d.actual_operation_time) / 60.0), d.precision("actual_operating_cost")
+			)
 
 			self.planned_operating_cost += flt(d.planned_operating_cost)
 			self.actual_operating_cost += flt(d.actual_operating_cost)
@@ -588,7 +592,6 @@
 	def prepare_data_for_job_card(self, row, index, plan_days, enable_capacity_planning):
 		self.set_operation_start_end_time(index, row)
 
-		original_start_time = row.planned_start_time
 		job_card_doc = create_job_card(
 			self, row, auto_create=True, enable_capacity_planning=enable_capacity_planning
 		)
@@ -597,11 +600,15 @@
 			row.planned_start_time = job_card_doc.scheduled_time_logs[-1].from_time
 			row.planned_end_time = job_card_doc.scheduled_time_logs[-1].to_time
 
-			if date_diff(row.planned_start_time, original_start_time) > plan_days:
+			if date_diff(row.planned_end_time, self.planned_start_date) > plan_days:
 				frappe.message_log.pop()
 				frappe.throw(
-					_("Unable to find the time slot in the next {0} days for the operation {1}.").format(
-						plan_days, row.operation
+					_(
+						"Unable to find the time slot in the next {0} days for the operation {1}. Please increase the 'Capacity Planning For (Days)' in the {2}."
+					).format(
+						plan_days,
+						row.operation,
+						get_link_to_form("Manufacturing Settings", "Manufacturing Settings"),
 					),
 					CapacityError,
 				)
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 5dab93b..815b01d 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -354,6 +354,8 @@
 execute:frappe.db.set_default("date_format", frappe.db.get_single_value("System Settings", "date_format"))
 erpnext.patches.v14_0.update_total_asset_cost_field
 erpnext.patches.v15_0.create_advance_payment_status
+erpnext.patches.v15_0.allow_on_submit_dimensions_for_repostable_doctypes
+erpnext.patches.v14_0.create_accounting_dimensions_in_reconciliation_tool
 # below migration patch should always run last
 erpnext.patches.v14_0.migrate_gl_to_payment_ledger
 erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2023-12-20
diff --git a/erpnext/patches/v14_0/create_accounting_dimensions_in_reconciliation_tool.py b/erpnext/patches/v14_0/create_accounting_dimensions_in_reconciliation_tool.py
new file mode 100644
index 0000000..4466eaa
--- /dev/null
+++ b/erpnext/patches/v14_0/create_accounting_dimensions_in_reconciliation_tool.py
@@ -0,0 +1,8 @@
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
+	create_accounting_dimensions_for_doctype,
+)
+
+
+def execute():
+	create_accounting_dimensions_for_doctype(doctype="Payment Reconciliation")
+	create_accounting_dimensions_for_doctype(doctype="Payment Reconciliation Allocation")
diff --git a/erpnext/patches/v15_0/allow_on_submit_dimensions_for_repostable_doctypes.py b/erpnext/patches/v15_0/allow_on_submit_dimensions_for_repostable_doctypes.py
new file mode 100644
index 0000000..e75610d
--- /dev/null
+++ b/erpnext/patches/v15_0/allow_on_submit_dimensions_for_repostable_doctypes.py
@@ -0,0 +1,14 @@
+import frappe
+
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
+	get_accounting_dimensions,
+)
+from erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger import (
+	get_allowed_types_from_settings,
+)
+
+
+def execute():
+	for dt in get_allowed_types_from_settings():
+		for dimension in get_accounting_dimensions():
+			frappe.db.set_value("Custom Field", dt + "-" + dimension, "allow_on_submit", 1)
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 775bdb4..faaa38d 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -397,7 +397,7 @@
 
 	refresh() {
 		erpnext.toggle_naming_series();
-		erpnext.hide_company();
+		erpnext.hide_company(this.frm);
 		this.set_dynamic_labels();
 		this.setup_sms();
 		this.setup_quality_inspection();
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index aee761f..61b8f35 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -26,14 +26,14 @@
 		}
 	},
 
-	hide_company: function() {
-		if(cur_frm.fields_dict.company) {
+	hide_company: function(frm) {
+		if(frm?.fields_dict.company) {
 			var companies = Object.keys(locals[":Company"] || {});
 			if(companies.length === 1) {
-				if(!cur_frm.doc.company) cur_frm.set_value("company", companies[0]);
-				cur_frm.toggle_display("company", false);
+				if(!frm.doc.company) frm.set_value("company", companies[0]);
+				frm.toggle_display("company", false);
 			} else if(erpnext.last_selected_company) {
-				if(!cur_frm.doc.company) cur_frm.set_value("company", erpnext.last_selected_company);
+				if(!frm.doc.company) frm.set_value("company", erpnext.last_selected_company);
 			}
 		}
 	},
diff --git a/erpnext/selling/doctype/installation_note/installation_note.json b/erpnext/selling/doctype/installation_note/installation_note.json
index 18c7d08..1e22f44 100644
--- a/erpnext/selling/doctype/installation_note/installation_note.json
+++ b/erpnext/selling/doctype/installation_note/installation_note.json
@@ -24,6 +24,7 @@
   "inst_time",
   "status",
   "company",
+  "project",
   "amended_from",
   "remarks",
   "item_details",
@@ -225,13 +226,19 @@
    "oldfieldtype": "Table",
    "options": "Installation Note Item",
    "reqd": 1
+  },
+  {
+   "fieldname": "project",
+   "fieldtype": "Link",
+   "label": "Project",
+   "options": "Project"
   }
  ],
  "icon": "fa fa-wrench",
  "idx": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2023-06-03 16:31:08.386961",
+ "modified": "2024-02-04 18:20:12.020313",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Installation Note",
@@ -264,4 +271,4 @@
  "states": [],
  "timeline_field": "customer",
  "title_field": "customer_name"
-}
\ No newline at end of file
+}
diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js
index 63711c5..588b698 100644
--- a/erpnext/selling/page/point_of_sale/pos_payment.js
+++ b/erpnext/selling/page/point_of_sale/pos_payment.js
@@ -389,7 +389,7 @@
 				df: {
 					label: p.mode_of_payment,
 					fieldtype: 'Currency',
-					placeholder: __('Enter {0} amount.', [p.mode_of_payment]),
+					placeholder: __('Enter {0} amount.', [__(p.mode_of_payment)]),
 					onchange: function() {
 						const current_value = frappe.model.get_value(p.doctype, p.name, 'amount');
 						if (current_value != this.value) {
diff --git a/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py b/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py
index 7d28f2b..f2f1e4c 100644
--- a/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py
+++ b/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py
@@ -206,42 +206,36 @@
 
 def get_actual_data(filters, sales_users_or_territory_data, date_field, sales_field):
 	fiscal_year = get_fiscal_year(fiscal_year=filters.get("fiscal_year"), as_dict=1)
-	dates = [fiscal_year.year_start_date, fiscal_year.year_end_date]
 
-	select_field = "`tab{0}`.{1}".format(filters.get("doctype"), sales_field)
-	child_table = "`tab{0}`".format(filters.get("doctype") + " Item")
+	parent_doc = frappe.qb.DocType(filters.get("doctype"))
+	child_doc = frappe.qb.DocType(filters.get("doctype") + " Item")
+	sales_team = frappe.qb.DocType("Sales Team")
+
+	query = (
+		frappe.qb.from_(parent_doc)
+		.inner_join(child_doc)
+		.on(child_doc.parent == parent_doc.name)
+		.inner_join(sales_team)
+		.on(sales_team.parent == parent_doc.name)
+		.select(
+			child_doc.item_group,
+			(child_doc.stock_qty * sales_team.allocated_percentage / 100).as_("stock_qty"),
+			(child_doc.base_net_amount * sales_team.allocated_percentage / 100).as_("base_net_amount"),
+			sales_team.sales_person,
+			parent_doc[date_field],
+		)
+		.where(
+			(parent_doc.docstatus == 1)
+			& (parent_doc[date_field].between(fiscal_year.year_start_date, fiscal_year.year_end_date))
+		)
+	)
 
 	if sales_field == "sales_person":
-		select_field = "`tabSales Team`.sales_person"
-		child_table = "`tab{0}`, `tabSales Team`".format(filters.get("doctype") + " Item")
-		cond = """`tabSales Team`.parent = `tab{0}`.name and
-			`tabSales Team`.sales_person in ({1}) """.format(
-			filters.get("doctype"), ",".join(["%s"] * len(sales_users_or_territory_data))
-		)
+		query = query.where(sales_team.sales_person.isin(sales_users_or_territory_data))
 	else:
-		cond = "`tab{0}`.{1} in ({2})".format(
-			filters.get("doctype"), sales_field, ",".join(["%s"] * len(sales_users_or_territory_data))
-		)
+		query = query.where(parent_doc[sales_field].isin(sales_users_or_territory_data))
 
-	return frappe.db.sql(
-		""" SELECT `tab{child_doc}`.item_group,
-			`tab{child_doc}`.stock_qty, `tab{child_doc}`.base_net_amount,
-			{select_field}, `tab{parent_doc}`.{date_field}
-		FROM `tab{parent_doc}`, {child_table}
-		WHERE
-			`tab{child_doc}`.parent = `tab{parent_doc}`.name
-			and `tab{parent_doc}`.docstatus = 1 and {cond}
-			and `tab{parent_doc}`.{date_field} between %s and %s""".format(
-			cond=cond,
-			date_field=date_field,
-			select_field=select_field,
-			child_table=child_table,
-			parent_doc=filters.get("doctype"),
-			child_doc=filters.get("doctype") + " Item",
-		),
-		tuple(sales_users_or_territory_data + dates),
-		as_dict=1,
-	)
+	return query.run(as_dict=True)
 
 
 def get_parents_data(filters, partner_doctype):
diff --git a/erpnext/selling/report/sales_person_target_variance_based_on_item_group/test_sales_person_target_variance_based_on_item_group.py b/erpnext/selling/report/sales_person_target_variance_based_on_item_group/test_sales_person_target_variance_based_on_item_group.py
new file mode 100644
index 0000000..4ae5d2b
--- /dev/null
+++ b/erpnext/selling/report/sales_person_target_variance_based_on_item_group/test_sales_person_target_variance_based_on_item_group.py
@@ -0,0 +1,84 @@
+import frappe
+from frappe.tests.utils import FrappeTestCase
+from frappe.utils import flt, nowdate
+
+from erpnext.accounts.utils import get_fiscal_year
+from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
+from erpnext.selling.report.sales_person_target_variance_based_on_item_group.sales_person_target_variance_based_on_item_group import (
+	execute,
+)
+
+
+class TestSalesPersonTargetVarianceBasedOnItemGroup(FrappeTestCase):
+	def setUp(self):
+		self.fiscal_year = get_fiscal_year(nowdate())[0]
+
+	def tearDown(self):
+		frappe.db.rollback()
+
+	def test_achieved_target_and_variance(self):
+		# Create a Target Distribution
+		distribution = frappe.new_doc("Monthly Distribution")
+		distribution.distribution_id = "Target Report Distribution"
+		distribution.fiscal_year = self.fiscal_year
+		distribution.get_months()
+		distribution.insert()
+
+		# Create sales people with targets
+		person_1 = create_sales_person_with_target("Sales Person 1", self.fiscal_year, distribution.name)
+		person_2 = create_sales_person_with_target("Sales Person 2", self.fiscal_year, distribution.name)
+
+		# Create a Sales Order with 50-50 contribution
+		so = make_sales_order(
+			rate=1000,
+			qty=20,
+			do_not_submit=True,
+		)
+		so.set(
+			"sales_team",
+			[
+				{
+					"sales_person": person_1.name,
+					"allocated_percentage": 50,
+					"allocated_amount": 10000,
+				},
+				{
+					"sales_person": person_2.name,
+					"allocated_percentage": 50,
+					"allocated_amount": 10000,
+				},
+			],
+		)
+		so.submit()
+
+		# Check Achieved Target and Variance
+		result = execute(
+			frappe._dict(
+				{
+					"fiscal_year": self.fiscal_year,
+					"doctype": "Sales Order",
+					"period": "Yearly",
+					"target_on": "Quantity",
+				}
+			)
+		)[1]
+		row = frappe._dict(result[0])
+		self.assertSequenceEqual(
+			[flt(value, 2) for value in (row.total_target, row.total_achieved, row.total_variance)],
+			[50, 10, -40],
+		)
+
+
+def create_sales_person_with_target(sales_person_name, fiscal_year, distribution_id):
+	sales_person = frappe.new_doc("Sales Person")
+	sales_person.sales_person_name = sales_person_name
+	sales_person.append(
+		"targets",
+		{
+			"fiscal_year": fiscal_year,
+			"target_qty": 50,
+			"target_amount": 30000,
+			"distribution_id": distribution_id,
+		},
+	)
+	return sales_person.insert()
diff --git a/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py b/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py
index 9f3ba0d..847488f 100644
--- a/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py
+++ b/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py
@@ -36,6 +36,7 @@
 					d.base_net_amount,
 					d.sales_person,
 					d.allocated_percentage,
+					(d.stock_qty * d.allocated_percentage / 100),
 					d.contribution_amt,
 					company_currency,
 				]
@@ -103,7 +104,7 @@
 			"fieldtype": "Link",
 			"width": 140,
 		},
-		{"label": _("Qty"), "fieldname": "qty", "fieldtype": "Float", "width": 140},
+		{"label": _("SO Total Qty"), "fieldname": "qty", "fieldtype": "Float", "width": 140},
 		{
 			"label": _("Amount"),
 			"options": "currency",
@@ -120,6 +121,12 @@
 		},
 		{"label": _("Contribution %"), "fieldname": "contribution", "fieldtype": "Float", "width": 140},
 		{
+			"label": _("Contribution Qty"),
+			"fieldname": "contribution_qty",
+			"fieldtype": "Float",
+			"width": 140,
+		},
+		{
 			"label": _("Contribution Amount"),
 			"options": "currency",
 			"fieldname": "contribution_amt",
diff --git a/erpnext/setup/demo.py b/erpnext/setup/demo.py
index df2c49b..688d45a 100644
--- a/erpnext/setup/demo.py
+++ b/erpnext/setup/demo.py
@@ -95,7 +95,17 @@
 
 def make_transactions(company):
 	frappe.db.set_single_value("Stock Settings", "allow_negative_stock", 1)
-	start_date = get_fiscal_year(date=getdate())[1]
+	from erpnext.accounts.utils import FiscalYearError
+
+	try:
+		start_date = get_fiscal_year(date=getdate())[1]
+	except FiscalYearError:
+		# User might have setup fiscal year for previous or upcoming years
+		active_fiscal_years = frappe.db.get_all("Fiscal Year", filters={"disabled": 0}, as_list=1)
+		if active_fiscal_years:
+			start_date = frappe.db.get_value("Fiscal Year", active_fiscal_years[0][0], "year_start_date")
+		else:
+			frappe.throw(_("There are no active Fiscal Years for which Demo Data can be generated."))
 
 	for doctype in frappe.get_hooks("demo_transaction_doctypes"):
 		data = read_data_file_using_hooks(doctype)
@@ -159,6 +169,7 @@
 
 			if i % 2 != 0:
 				payment = get_payment_entry(invoice.doctype, invoice.name)
+				payment.posting_date = order.transaction_date
 				payment.reference_no = invoice.name
 				payment.submit()
 
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index 4eacbc1..a3903a3 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -399,7 +399,12 @@
 		elif self.issue_credit_note:
 			self.make_return_invoice()
 
-		self.make_bundle_using_old_serial_batch_fields()
+		for table_name in ["items", "packed_items"]:
+			if not self.get(table_name):
+				continue
+
+			self.make_bundle_using_old_serial_batch_fields(table_name)
+
 		# Updating stock ledger should always be called after updating prevdoc status,
 		# because updating reserved qty in bin depends upon updated delivered qty in SO
 		self.update_stock_ledger()
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index 459e7e7..293ef9f 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -1078,6 +1078,8 @@
 		self.assertEqual(si2.items[1].qty, 1)
 
 	def test_delivery_note_bundle_with_batched_item(self):
+		frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 0)
+
 		batched_bundle = make_item("_Test Batched bundle", {"is_stock_item": 0})
 		batched_item = make_item(
 			"_Test Batched Item",
@@ -1099,6 +1101,8 @@
 		batch_no = get_batch_from_bundle(dn.packed_items[0].serial_and_batch_bundle)
 		self.assertTrue(batch_no)
 
+		frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1)
+
 	def test_payment_terms_are_fetched_when_creating_sales_invoice(self):
 		from erpnext.accounts.doctype.payment_entry.test_payment_entry import (
 			create_payment_terms_template,
@@ -1551,6 +1555,53 @@
 		self.assertEqual(so.items[0].rate, rate)
 		self.assertEqual(dn.items[0].rate, so.items[0].rate)
 
+	def test_use_serial_batch_fields_for_packed_items(self):
+		bundle_item = make_item("Test _Packed Product Bundle Item ", {"is_stock_item": 0})
+		serial_item = make_item(
+			"Test _Packed Serial Item ",
+			{"is_stock_item": 1, "has_serial_no": 1, "serial_no_series": "SN-TESTSERIAL-.#####"},
+		)
+		batch_item = make_item(
+			"Test _Packed Batch Item ",
+			{
+				"is_stock_item": 1,
+				"has_batch_no": 1,
+				"batch_no_series": "BATCH-TESTSERIAL-.#####",
+				"create_new_batch": 1,
+			},
+		)
+		make_product_bundle(parent=bundle_item.name, items=[serial_item.name, batch_item.name])
+
+		item_details = {}
+		for item in [serial_item, batch_item]:
+			se = make_stock_entry(
+				item_code=item.name, target="_Test Warehouse - _TC", qty=5, basic_rate=100
+			)
+			item_details[item.name] = se.items[0].serial_and_batch_bundle
+
+		dn = create_delivery_note(item_code=bundle_item.name, qty=1, do_not_submit=True)
+		serial_no = ""
+		for row in dn.packed_items:
+			row.use_serial_batch_fields = 1
+
+			if row.item_code == serial_item.name:
+				serial_and_batch_bundle = item_details[serial_item.name]
+				row.serial_no = get_serial_nos_from_bundle(serial_and_batch_bundle)[3]
+				serial_no = row.serial_no
+			else:
+				serial_and_batch_bundle = item_details[batch_item.name]
+				row.batch_no = get_batch_from_bundle(serial_and_batch_bundle)
+
+		dn.submit()
+		dn.load_from_db()
+
+		for row in dn.packed_items:
+			self.assertTrue(row.serial_no or row.batch_no)
+			self.assertTrue(row.serial_and_batch_bundle)
+
+			if row.serial_no:
+				self.assertEqual(row.serial_no, serial_no)
+
 
 def create_delivery_note(**args):
 	dn = frappe.new_doc("Delivery Note")
diff --git a/erpnext/stock/doctype/landed_cost_purchase_receipt/landed_cost_purchase_receipt.json b/erpnext/stock/doctype/landed_cost_purchase_receipt/landed_cost_purchase_receipt.json
index 9b2b5da..736eb9d 100644
--- a/erpnext/stock/doctype/landed_cost_purchase_receipt/landed_cost_purchase_receipt.json
+++ b/erpnext/stock/doctype/landed_cost_purchase_receipt/landed_cost_purchase_receipt.json
@@ -1,187 +1,77 @@
 {
- "allow_copy": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2013-02-22 01:28:02", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "Document", 
- "editable_grid": 1, 
+ "actions": [],
+ "creation": "2013-02-22 01:28:02",
+ "doctype": "DocType",
+ "document_type": "Document",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "receipt_document_type",
+  "receipt_document",
+  "supplier",
+  "col_break1",
+  "posting_date",
+  "grand_total"
+ ],
  "fields": [
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "fieldname": "receipt_document_type", 
-   "fieldtype": "Select", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 1, 
-   "label": "Receipt Document Type", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "\nPurchase Invoice\nPurchase Receipt", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "receipt_document_type",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "label": "Receipt Document Type",
+   "options": "\nPurchase Invoice\nPurchase Receipt",
+   "reqd": 1
+  },
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "fieldname": "receipt_document", 
-   "fieldtype": "Dynamic Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 1, 
-   "label": "Receipt Document", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "purchase_receipt_no", 
-   "oldfieldtype": "Link", 
-   "options": "receipt_document_type", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "print_width": "220px", 
-   "read_only": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0, 
+   "fieldname": "receipt_document",
+   "fieldtype": "Dynamic Link",
+   "in_list_view": 1,
+   "label": "Receipt Document",
+   "oldfieldname": "purchase_receipt_no",
+   "oldfieldtype": "Link",
+   "options": "receipt_document_type",
+   "print_width": "220px",
+   "reqd": 1,
    "width": "220px"
-  }, 
+  },
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "fieldname": "supplier", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 1, 
-   "label": "Supplier", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Supplier", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "supplier",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Supplier",
+   "options": "Supplier",
+   "read_only": 1
+  },
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "fieldname": "col_break1", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0, 
+   "fieldname": "col_break1",
+   "fieldtype": "Column Break",
    "width": "50%"
-  }, 
+  },
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "fieldname": "posting_date", 
-   "fieldtype": "Date", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 0, 
-   "label": "Posting Date", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "posting_date",
+   "fieldtype": "Date",
+   "label": "Posting Date",
+   "read_only": 1
+  },
   {
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "fieldname": "grand_total", 
-   "fieldtype": "Currency", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_list_view": 1, 
-   "label": "Grand Total", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "unique": 0
+   "fieldname": "grand_total",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Grand Total",
+   "options": "Company:company:default_currency",
+   "read_only": 1
   }
- ], 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 1, 
- "image_view": 0, 
- "in_create": 0, 
-
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2020-09-18 17:26:09.703215", 
- "modified_by": "Administrator", 
- "module": "Stock", 
- "name": "Landed Cost Purchase Receipt", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 0, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "sort_order": "ASC", 
- "track_seen": 0
+ ],
+ "idx": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2024-02-26 18:41:06.281750",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Landed Cost Purchase Receipt",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "ASC",
+ "states": []
 }
\ No newline at end of file
diff --git a/erpnext/stock/doctype/material_request_item/material_request_item.json b/erpnext/stock/doctype/material_request_item/material_request_item.json
index c7239b5..c705d59 100644
--- a/erpnext/stock/doctype/material_request_item/material_request_item.json
+++ b/erpnext/stock/doctype/material_request_item/material_request_item.json
@@ -198,12 +198,14 @@
    "fieldname": "rate",
    "fieldtype": "Currency",
    "label": "Rate",
+   "options": "Company:company:default_currency",
    "print_hide": 1
   },
   {
    "fieldname": "amount",
    "fieldtype": "Currency",
    "label": "Amount",
+   "options": "Company:company:default_currency",
    "print_hide": 1,
    "read_only": 1
   },
@@ -489,7 +491,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2024-02-08 16:30:56.137858",
+ "modified": "2024-02-26 18:30:03.684872",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Material Request Item",
diff --git a/erpnext/stock/doctype/packed_item/packed_item.py b/erpnext/stock/doctype/packed_item/packed_item.py
index c115e33..c5fed0d 100644
--- a/erpnext/stock/doctype/packed_item/packed_item.py
+++ b/erpnext/stock/doctype/packed_item/packed_item.py
@@ -227,6 +227,9 @@
 	bin = get_packed_item_bin_qty(packing_item.item_code, pi_row.warehouse)
 	pi_row.actual_qty = flt(bin.get("actual_qty"))
 	pi_row.projected_qty = flt(bin.get("projected_qty"))
+	pi_row.use_serial_batch_fields = frappe.db.get_single_value(
+		"Stock Settings", "use_serial_batch_fields"
+	)
 
 
 def update_packed_item_price_data(pi_row, item_data, doc):
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index daa0166..d542601 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -2237,6 +2237,10 @@
 			create_stock_reconciliation,
 		)
 
+		frappe.db.set_single_value(
+			"Stock Settings", "do_not_update_serial_batch_on_creation_of_auto_bundle", 0
+		)
+
 		item_code = make_item(
 			"_Test Use Serial Fields Item Serial Item",
 			properties={"has_serial_no": 1, "serial_no_series": "SNU-TSFISI-.#####"},
@@ -2317,6 +2321,10 @@
 			serial_no_status = frappe.db.get_value("Serial No", sn, "status")
 			self.assertTrue(serial_no_status != "Active")
 
+		frappe.db.set_single_value(
+			"Stock Settings", "do_not_update_serial_batch_on_creation_of_auto_bundle", 1
+		)
+
 	def test_sle_qty_after_transaction(self):
 		item = make_item(
 			"_Test Item Qty After Transaction",
@@ -2416,7 +2424,9 @@
 			},
 		).name
 
-		frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1)
+		frappe.db.set_single_value(
+			"Stock Settings", "do_not_update_serial_batch_on_creation_of_auto_bundle", 0
+		)
 
 		pr = make_purchase_receipt(
 			item_code=item_code,
@@ -2428,7 +2438,9 @@
 		batch_no = get_batch_from_bundle(pr.items[0].serial_and_batch_bundle)
 		self.assertEqual(pr.items[0].batch_no, batch_no)
 
-		frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 0)
+		frappe.db.set_single_value(
+			"Stock Settings", "do_not_update_serial_batch_on_creation_of_auto_bundle", 1
+		)
 
 
 def prepare_data_for_internal_transfer():
diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
index eb4df29..b6e4d6f 100644
--- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
+++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
@@ -257,9 +257,9 @@
 				if sn_obj.batch_avg_rate.get(d.batch_no):
 					d.incoming_rate = abs(sn_obj.batch_avg_rate.get(d.batch_no))
 
-				available_qty = flt(sn_obj.available_qty.get(d.batch_no))
+				available_qty = flt(sn_obj.available_qty.get(d.batch_no), d.precision("qty"))
 				if self.docstatus == 1:
-					available_qty += flt(d.qty)
+					available_qty += flt(d.qty, d.precision("qty"))
 
 				if not allow_negative_stock:
 					self.validate_negative_batch(d.batch_no, available_qty)
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 7f79f04..1b7089b 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -943,7 +943,7 @@
 		if (this.frm.doc.docstatus===1 && erpnext.is_perpetual_inventory_enabled(this.frm.doc.company)) {
 			this.show_general_ledger();
 		}
-		erpnext.hide_company();
+		erpnext.hide_company(this.frm);
 		erpnext.utils.add_item(this.frm);
 	}
 
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 832894b..399e698 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -1033,7 +1033,7 @@
 		already_picked_serial_nos = []
 
 		for row in self.items:
-			if row.use_serial_batch_fields and (row.serial_no or row.batch_no):
+			if row.use_serial_batch_fields:
 				continue
 
 			if not row.s_warehouse:
diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
index c7b3daa..48fc31a 100644
--- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
+++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json
@@ -294,7 +294,7 @@
   {
    "depends_on": "eval:doc.use_serial_batch_fields === 1",
    "fieldname": "serial_no",
-   "fieldtype": "Small Text",
+   "fieldtype": "Text",
    "label": "Serial No",
    "no_copy": 1,
    "oldfieldname": "serial_no",
@@ -610,7 +610,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2024-02-04 16:16:47.606270",
+ "modified": "2024-02-25 15:58:40.982582",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Entry Detail",
diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py
index 47c443c..bd3dda1 100644
--- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py
+++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py
@@ -54,7 +54,7 @@
 		sample_quantity: DF.Int
 		sco_rm_detail: DF.Data | None
 		serial_and_batch_bundle: DF.Link | None
-		serial_no: DF.SmallText | None
+		serial_no: DF.Text | None
 		set_basic_rate_manually: DF.Check
 		ste_detail: DF.Data | None
 		stock_uom: DF.Link
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json
index fb8089e..51036ad 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.json
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.json
@@ -51,6 +51,7 @@
   "use_naming_series",
   "naming_series_prefix",
   "use_serial_batch_fields",
+  "do_not_update_serial_batch_on_creation_of_auto_bundle",
   "stock_planning_tab",
   "auto_material_request",
   "auto_indent",
@@ -428,6 +429,14 @@
    "fieldname": "use_serial_batch_fields",
    "fieldtype": "Check",
    "label": "Use Serial / Batch Fields"
+  },
+  {
+   "default": "1",
+   "depends_on": "use_serial_batch_fields",
+   "description": "If enabled, do not update serial / batch values in the stock transactions on creation of auto Serial \n / Batch Bundle. ",
+   "fieldname": "do_not_update_serial_batch_on_creation_of_auto_bundle",
+   "fieldtype": "Check",
+   "label": "Do Not Update Serial / Batch on Creation of Auto Bundle"
   }
  ],
  "icon": "icon-cog",
@@ -435,7 +444,7 @@
  "index_web_pages_for_search": 1,
  "issingle": 1,
  "links": [],
- "modified": "2024-02-24 12:50:51.740097",
+ "modified": "2024-02-25 16:32:01.084453",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Settings",
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.py b/erpnext/stock/doctype/stock_settings/stock_settings.py
index c4960aa..d975c29 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.py
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.py
@@ -39,6 +39,7 @@
 		clean_description_html: DF.Check
 		default_warehouse: DF.Link | None
 		disable_serial_no_and_batch_selector: DF.Check
+		do_not_update_serial_batch_on_creation_of_auto_bundle: DF.Check
 		enable_stock_reservation: DF.Check
 		item_group: DF.Link | None
 		item_naming_by: DF.Literal["Item Code", "Naming Series"]
diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py
index 52c5b00..24dd9d1 100644
--- a/erpnext/stock/serial_batch_bundle.py
+++ b/erpnext/stock/serial_batch_bundle.py
@@ -142,7 +142,9 @@
 				"serial_and_batch_bundle": sn_doc.name,
 			}
 
-			if frappe.db.get_single_value("Stock Settings", "use_serial_batch_fields"):
+			if not frappe.db.get_single_value(
+				"Stock Settings", "do_not_update_serial_batch_on_creation_of_auto_bundle"
+			):
 				if sn_doc.has_serial_no:
 					values_to_update["serial_no"] = ",".join(cstr(d.serial_no) for d in sn_doc.entries)
 				elif sn_doc.has_batch_no and len(sn_doc.entries) == 1: