Merge remote-tracking branch 'upstream/develop' into remove-unused-coa
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 30be903..6ea121f 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -5,7 +5,7 @@
 
 repos:
   - repo: https://github.com/pre-commit/pre-commit-hooks
-    rev: v4.0.1
+    rev: v4.3.0
     hooks:
       - id: trailing-whitespace
         files: "erpnext.*"
@@ -15,6 +15,10 @@
         args: ['--branch', 'develop']
       - id: check-merge-conflict
       - id: check-ast
+      - id: check-json
+      - id: check-toml
+      - id: check-yaml
+      - id: debug-statements
 
   - repo: https://github.com/pre-commit/mirrors-eslint
     rev: v8.44.0
diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py
index 367b017..6282e9a 100644
--- a/erpnext/accounts/deferred_revenue.py
+++ b/erpnext/accounts/deferred_revenue.py
@@ -358,9 +358,11 @@
 
 		account_currency = get_account_currency(item.expense_account or item.income_account)
 		if doc.doctype == "Sales Invoice":
+			against_type = "Customer"
 			against, project = doc.customer, doc.project
 			credit_account, debit_account = item.income_account, item.deferred_revenue_account
 		else:
+			against_type = "Supplier"
 			against, project = doc.supplier, item.project
 			credit_account, debit_account = item.deferred_expense_account, item.expense_account
 
@@ -413,6 +415,7 @@
 				doc,
 				credit_account,
 				debit_account,
+				against_type,
 				against,
 				amount,
 				base_amount,
@@ -494,6 +497,7 @@
 	doc,
 	credit_account,
 	debit_account,
+	against_type,
 	against,
 	amount,
 	base_amount,
@@ -515,7 +519,9 @@
 		doc.get_gl_dict(
 			{
 				"account": credit_account,
+				"against_type": against_type,
 				"against": against,
+				"against_link": against,
 				"credit": base_amount,
 				"credit_in_account_currency": amount,
 				"cost_center": cost_center,
@@ -534,7 +540,9 @@
 		doc.get_gl_dict(
 			{
 				"account": debit_account,
+				"against_type": against_type,
 				"against": against,
+				"against_link": against,
 				"debit": base_amount,
 				"debit_in_account_currency": amount,
 				"cost_center": cost_center,
diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/unverified/at_austria_chart_template.json b/erpnext/accounts/doctype/account/chart_of_accounts/unverified/at_austria_chart_template.json
index 58d67be..bd7228e 100644
--- a/erpnext/accounts/doctype/account/chart_of_accounts/unverified/at_austria_chart_template.json
+++ b/erpnext/accounts/doctype/account/chart_of_accounts/unverified/at_austria_chart_template.json
@@ -26,7 +26,7 @@
 				"0360 Bauliche Investitionen in fremden (gepachteten) Betriebs- und Geschäftsgebäuden": {"account_type": "Fixed Asset"},
 				"0370 Bauliche Investitionen in fremden (gepachteten) Wohn- und Sozialgebäuden": {"account_type": "Fixed Asset"},
 				"0390 Kumulierte Abschreibungen zu Grundstücken ": {"account_type": "Fixed Asset"},
-				"0400 Maschinen und Geräte ": {"account_type": "Fixed Asset"},				
+				"0400 Maschinen und Geräte ": {"account_type": "Fixed Asset"},
 				"0500 Maschinenwerkzeuge ": {"account_type": "Fixed Asset"},
 				"0510 Allgemeine Werkzeuge und Handwerkzeuge ": {"account_type": "Fixed Asset"},
 				"0520 Prototypen, Formen, Modelle ": {"account_type": "Fixed Asset"},
@@ -65,42 +65,41 @@
 				"0980 Geleistete Anzahlungen auf Finanzanlagen ": {"account_type": "Fixed Asset"},
 				"0990 Kumulierte Abschreibungen zu Finanzanlagen ": {"account_type": "Fixed Asset"},
             "root_type": "Asset"
-        },    
+        },
         "Klasse 1 Aktiva: Vorr\u00e4te": {
             "1000 Bezugsverrechnung": {"account_type": "Stock"},
             "1100 Rohstoffe": {"account_type": "Stock"},
             "1200 Bezogene Teile": {"account_type": "Stock"},
             "1300 Hilfsstoffe": {"account_type": "Stock"},
             "1350 Betriebsstoffe": {"account_type": "Stock"},
-            "1360 Vorrat Energietraeger": {"account_type": "Stock"},            
+            "1360 Vorrat Energietraeger": {"account_type": "Stock"},
             "1400 Unfertige Erzeugnisse": {"account_type": "Stock"},
             "1500 Fertige Erzeugnisse": {"account_type": "Stock"},
             "1600 Handelswarenvorrat": {"account_type": "Stock Received But Not Billed"},
             "1700 Noch nicht abrechenbare Leistungen": {"account_type": "Stock"},
-            "1900 Wertberichtigungen": {"account_type": "Stock"},
             "1800 Geleistete Anzahlungen": {"account_type": "Stock"},
             "1900 Wertberichtigungen": {"account_type": "Stock"},
             "root_type": "Asset"
-        },    
+        },
         "Klasse 3 Passiva: Verbindlichkeiten": {
             "3000 Allgemeine Verbindlichkeiten (Schuld)": {"account_type": "Payable"},
             "3010 R\u00fcckstellungen f\u00fcr Pensionen": {"account_type": "Payable"},
             "3020 Steuerr\u00fcckstellungen": {"account_type": "Tax"},
-            "3041 Sonstige R\u00fcckstellungen": {"account_type": "Payable"},            
+            "3041 Sonstige R\u00fcckstellungen": {"account_type": "Payable"},
             "3110 Verbindlichkeiten gegen\u00fcber Bank": {"account_type": "Payable"},
             "3150 Verbindlichkeiten Darlehen": {"account_type": "Payable"},
-            "3185 Verbindlichkeiten Kreditkarte": {"account_type": "Payable"},            
+            "3185 Verbindlichkeiten Kreditkarte": {"account_type": "Payable"},
             "3380 Verbindlichkeiten aus der Annahme gezogener Wechsel u. d. Ausstellungen eigener Wechsel": {
                 "account_type": "Payable"
             },
             "3400 Verbindlichkeiten gegen\u00fc. verb. Untern., Verbindl. gegen\u00fc. Untern., mit denen eine Beteiligungsverh\u00e4lnis besteht": {},
             "3460 Verbindlichkeiten gegenueber Gesellschaftern": {"account_type": "Payable"},
             "3470 Einlagen stiller Gesellschafter": {"account_type": "Payable"},
-            "3585 Verbindlichkeiten Lohnsteuer": {"account_type": "Tax"},             
-            "3590 Verbindlichkeiten Kommunalabgaben": {"account_type": "Tax"},  
-            "3595 Verbindlichkeiten Dienstgeberbeitrag": {"account_type": "Tax"},                       
+            "3585 Verbindlichkeiten Lohnsteuer": {"account_type": "Tax"},
+            "3590 Verbindlichkeiten Kommunalabgaben": {"account_type": "Tax"},
+            "3595 Verbindlichkeiten Dienstgeberbeitrag": {"account_type": "Tax"},
             "3600 Verbindlichkeiten Sozialversicherung": {"account_type": "Payable"},
-            "3640 Verbindlichkeiten Loehne und Gehaelter": {"account_type": "Payable"},            
+            "3640 Verbindlichkeiten Loehne und Gehaelter": {"account_type": "Payable"},
             "3700 Sonstige Verbindlichkeiten": {"account_type": "Payable"},
             "3900 Passive Rechnungsabgrenzungsposten": {"account_type": "Payable"},
             "3100 Anleihen (einschlie\u00dflich konvertibler)": {"account_type": "Payable"},
@@ -119,13 +118,13 @@
             },
             "3515 Umsatzsteuer Inland 10%": {
                 "account_type": "Tax"
-            },            
+            },
             "3520 Umsatzsteuer aus i.g. Erwerb 20%": {
                 "account_type": "Tax"
             },
             "3525 Umsatzsteuer aus i.g. Erwerb 10%": {
                 "account_type": "Tax"
-            },            
+            },
             "3560 Umsatzsteuer-Evidenzkonto f\u00fcr erhaltene Anzahlungen auf Bestellungen": {},
             "3360 Verbindlichkeiten aus Lieferungen u. Leistungen EU": {
                 "account_type": "Payable"
@@ -141,7 +140,7 @@
                 "account_type": "Tax"
             },
             "root_type": "Liability"
-        },      
+        },
         "Klasse 2 Aktiva: Umlaufverm\u00f6gen, Rechnungsabgrenzungen": {
             "2030 Forderungen aus Lieferungen und Leistungen Inland (0% USt, umsatzsteuerfrei)": {
                 "account_type": "Receivable"
@@ -154,7 +153,7 @@
             },
             "2040 Forderungen aus Lieferungen und Leistungen Inland (sonstiger USt-Satz)": {
                 "account_type": "Receivable"
-            },                                    
+            },
             "2100 Forderungen aus Lieferungen und Leistungen EU": {
                 "account_type": "Receivable"
             },
@@ -192,7 +191,7 @@
                 "account_type": "Receivable"
             },
             "2570 Einfuhrumsatzsteuer (bezahlt)": {"account_type": "Tax"},
-            
+
             "2460 Eingeforderte aber noch nicht eingezahlte Einlagen": {
                 "account_type": "Receivable"
             },
@@ -243,10 +242,10 @@
             },
             "2800 Guthaben bei Bank": {
                 "account_type": "Bank"
-            },  
+            },
             "2801 Guthaben bei Bank - Sparkonto": {
                 "account_type": "Bank"
-            },                      
+            },
             "2810 Guthaben bei Paypal": {
                 "account_type": "Bank"
             },
@@ -264,19 +263,19 @@
             },
             "2895 Schwebende Geldbewegugen": {
                 "account_type": "Bank"
-            },            
+            },
             "2513 Vorsteuer Inland 5%": {
                 "account_type": "Tax"
             },
             "2515 Vorsteuer Inland 20%": {
                 "account_type": "Tax"
-            },                        
+            },
             "2520 Vorsteuer aus innergemeinschaftlichem Erwerb 10%": {
                 "account_type": "Tax"
             },
             "2525 Vorsteuer aus innergemeinschaftlichem Erwerb 20%": {
                 "account_type": "Tax"
-            },            
+            },
             "2530 Vorsteuer \u00a719/Art 19 ( reverse charge ) ": {
                 "account_type": "Tax"
             },
@@ -286,16 +285,16 @@
             "root_type": "Asset"
         },
           "Klasse 4: Betriebliche Erträge": {
-            "4000 Erlöse 20 %": {"account_type": "Income Account"},          
-            "4020 Erl\u00f6se 0 % steuerbefreit": {"account_type": "Income Account"},            
+            "4000 Erlöse 20 %": {"account_type": "Income Account"},
+            "4020 Erl\u00f6se 0 % steuerbefreit": {"account_type": "Income Account"},
             "4010 Erl\u00f6se 10 %": {"account_type": "Income Account"},
-            "4030 Erl\u00f6se 13 %": {"account_type": "Income Account"},            
-            "4040 Erl\u00f6se 0 % innergemeinschaftliche Lieferungen": {"account_type": "Income Account"},      
-            "4400 Erl\u00f6sreduktion 0 % steuerbefreit": {"account_type": "Expense Account"},            
+            "4030 Erl\u00f6se 13 %": {"account_type": "Income Account"},
+            "4040 Erl\u00f6se 0 % innergemeinschaftliche Lieferungen": {"account_type": "Income Account"},
+            "4400 Erl\u00f6sreduktion 0 % steuerbefreit": {"account_type": "Expense Account"},
             "4410 Erl\u00f6sreduktion 10 %": {"account_type": "Expense Account"},
             "4420 Erl\u00f6sreduktion 20 %": {"account_type": "Expense Account"},
-            "4430 Erl\u00f6sreduktion 13 %": {"account_type": "Expense Account"},            
-            "4440 Erl\u00f6sreduktion 0 % innergemeinschaftliche Lieferungen": {"account_type": "Expense Account"}, 
+            "4430 Erl\u00f6sreduktion 13 %": {"account_type": "Expense Account"},
+            "4440 Erl\u00f6sreduktion 0 % innergemeinschaftliche Lieferungen": {"account_type": "Expense Account"},
             "4500 Ver\u00e4nderungen des Bestandes an fertigen und unfertigen Erzeugn. sowie an noch nicht abrechenbaren Leistungen": {"account_type": "Income Account"},
             "4580 Aktivierte Eigenleistungen": {"account_type": "Income Account"},
             "4600 Erl\u00f6se aus dem Abgang vom Anlageverm\u00f6gen, ausgen. Finanzanlagen": {"account_type": "Income Account"},
@@ -304,15 +303,15 @@
             "4700 Ertr\u00e4ge aus der Aufl\u00f6sung von R\u00fcckstellungen": {"account_type": "Income Account"},
             "4800 \u00dcbrige betriebliche Ertr\u00e4ge": {"account_type": "Income Account"},
             "root_type": "Income"
-        },        
+        },
         "Klasse 5: Aufwand f\u00fcr Material und Leistungen": {
-            "5000 Einkauf Partnerleistungen": {"account_type": "Cost of Goods Sold"},        
+            "5000 Einkauf Partnerleistungen": {"account_type": "Cost of Goods Sold"},
             "5100 Verbrauch an Rohstoffen": {"account_type": "Cost of Goods Sold"},
             "5200 Verbrauch von bezogenen Fertig- und Einzelteilen": {"account_type": "Cost of Goods Sold"},
             "5300 Verbrauch von Hilfsstoffen": {"account_type": "Cost of Goods Sold"},
             "5340 Verbrauch Verpackungsmaterial": {"account_type": "Cost of Goods Sold"},
             "5470 Verbrauch von Kleinmaterial": {"account_type": "Cost of Goods Sold"},
-            "5450 Verbrauch von Reinigungsmaterial": {"account_type": "Cost of Goods Sold"},                                  
+            "5450 Verbrauch von Reinigungsmaterial": {"account_type": "Cost of Goods Sold"},
             "5400 Verbrauch von Betriebsstoffen": {"account_type": "Cost of Goods Sold"},
             "5500 Verbrauch von Werkzeugen und anderen Erzeugungshilfsmittel": {"account_type": "Cost of Goods Sold"},
             "5600 Verbrauch von Brenn- und Treibstoffen, Energie und Wasser": {"account_type": "Cost of Goods Sold"},
@@ -340,7 +339,7 @@
             "6700 Sonstige Sozialaufwendungen": {"account_type": "Payable"},
             "6900 Aufwandsstellenrechnung Personal": {"account_type": "Payable"},
             "root_type": "Expense"
-        },       
+        },
          "Klasse 7: Abschreibungen und sonstige betriebliche Aufwendungen": {
             "7010 Abschreibungen auf das Anlageverm\u00f6gen (ausgenommen Finanzanlagen)": {"account_type": "Depreciation"},
             "7100 Sonstige Steuern und Geb\u00fchren": {"account_type": "Tax"},
@@ -349,7 +348,7 @@
             "7310 Fahrrad - Aufwand": {"account_type": "Expense Account"},
             "7320 Kfz - Aufwand": {"account_type": "Expense Account"},
             "7330 LKW - Aufwand": {"account_type": "Expense Account"},
-            "7340 Lastenrad - Aufwand": {"account_type": "Expense Account"},            
+            "7340 Lastenrad - Aufwand": {"account_type": "Expense Account"},
             "7350 Reise- und Fahraufwand": {"account_type": "Expense Account"},
             "7360 Tag- und N\u00e4chtigungsgelder": {"account_type": "Expense Account"},
             "7380 Nachrichtenaufwand": {"account_type": "Expense Account"},
@@ -409,7 +408,7 @@
             "8990 Gewinnabfuhr bzw. Verlust\u00fcberrechnung aus Ergebnisabf\u00fchrungsvertr\u00e4gen": {"account_type": "Expense Account"},
             "8350 nicht ausgenutzte Lieferantenskonti": {"account_type": "Expense Account"},
             "root_type": "Income"
-        },       
+        },
         "Klasse 9 Passiva: Eigenkapital, R\u00fccklagen, stille Einlagen, Abschlusskonten": {
             "9000 Gezeichnetes bzw. gewidmetes Kapital": {
                 "account_type": "Equity"
@@ -435,5 +434,5 @@
             },
             "root_type": "Equity"
         }
-     }   
+     }
   }
diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/ca_plan_comptable_pour_les_provinces_francophones.json b/erpnext/accounts/doctype/account/chart_of_accounts/verified/ca_plan_comptable_pour_les_provinces_francophones.json
index 2811fc5..2a30cbc 100644
--- a/erpnext/accounts/doctype/account/chart_of_accounts/verified/ca_plan_comptable_pour_les_provinces_francophones.json
+++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/ca_plan_comptable_pour_les_provinces_francophones.json
@@ -33,7 +33,9 @@
                 }, 
                 "Stocks": {
                     "Mati\u00e8res premi\u00e8res": {}, 
-                    "Stock de produits fini": {}, 
+                    "Stock de produits fini": {
+                        "account_type": "Stock"
+                    }, 
                     "Stock exp\u00e9di\u00e9 non-factur\u00e9": {}, 
                     "Travaux en cours": {}, 
                     "account_type": "Stock"
@@ -395,9 +397,11 @@
         }, 
         "Produits": {
             "Revenus de ventes": {
-                " Escomptes de volume sur ventes": {}, 
+                "Escomptes de volume sur ventes": {}, 
                 "Autres produits d'exploitation": {}, 
-                "Ventes": {}, 
+                "Ventes": {
+                    "account_type": "Income Account"
+                }, 
                 "Ventes avec des provinces harmonis\u00e9es": {}, 
                 "Ventes avec des provinces non-harmonis\u00e9es": {}, 
                 "Ventes \u00e0 l'\u00e9tranger": {}
diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR03.json b/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR03.json
index 741d428..daf2e21 100644
--- a/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR03.json
+++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR03.json
@@ -53,8 +53,13 @@
 				},
 				"II. Forderungen und sonstige Vermögensgegenstände": {
 					"is_group": 1,
-					"Ford. a. Lieferungen und Leistungen": {
+					"Forderungen aus Lieferungen und Leistungen mit Kontokorrent": {
 						"account_number": "1400",
+						"account_type": "Receivable",
+						"is_group": 1
+					},
+					"Forderungen aus Lieferungen und Leistungen ohne Kontokorrent": {
+						"account_number": "1410",
 						"account_type": "Receivable"
 					},
 					"Durchlaufende Posten": {
@@ -180,8 +185,13 @@
 				},
 				"IV. Verbindlichkeiten aus Lieferungen und Leistungen": {
 					"is_group": 1,
-					"Verbindlichkeiten aus Lieferungen u. Leistungen": {
+					"Verbindlichkeiten aus Lieferungen und Leistungen mit Kontokorrent": {
 						"account_number": "1600",
+						"account_type": "Payable",
+						"is_group": 1
+					},
+					"Verbindlichkeiten aus Lieferungen und Leistungen ohne Kontokorrent": {
+						"account_number": "1610",
 						"account_type": "Payable"
 					}
 				},
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.json b/erpnext/accounts/doctype/gl_entry/gl_entry.json
index 5063ec6..09912e9 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.json
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.json
@@ -17,10 +17,13 @@
   "account_currency",
   "debit_in_account_currency",
   "credit_in_account_currency",
+  "against_type",
   "against",
+  "against_link",
   "against_voucher_type",
   "against_voucher",
   "voucher_type",
+  "voucher_subtype",
   "voucher_no",
   "voucher_detail_no",
   "project",
@@ -129,6 +132,13 @@
    "options": "account_currency"
   },
   {
+   "fieldname": "against_type",
+   "fieldtype": "Link",
+   "in_filter": 1,
+   "label": "Against Type",
+   "options": "DocType"
+  },
+  {
    "fieldname": "against",
    "fieldtype": "Text",
    "in_filter": 1,
@@ -137,13 +147,19 @@
    "oldfieldtype": "Text"
   },
   {
+   "fieldname": "against_link",
+   "fieldtype": "Dynamic Link",
+   "in_filter": 1,
+   "label": "Against",
+   "options": "against_type"
+  },
+  {
    "fieldname": "against_voucher_type",
    "fieldtype": "Link",
    "label": "Against Voucher Type",
    "oldfieldname": "against_voucher_type",
    "oldfieldtype": "Data",
-   "options": "DocType",
-   "search_index": 1
+   "options": "DocType"
   },
   {
    "fieldname": "against_voucher",
@@ -162,8 +178,7 @@
    "label": "Voucher Type",
    "oldfieldname": "voucher_type",
    "oldfieldtype": "Select",
-   "options": "DocType",
-   "search_index": 1
+   "options": "DocType"
   },
   {
    "fieldname": "voucher_no",
@@ -280,13 +295,18 @@
    "fieldtype": "Currency",
    "label": "Credit Amount in Transaction Currency",
    "options": "transaction_currency"
+  },
+  {
+   "fieldname": "voucher_subtype",
+   "fieldtype": "Small Text",
+   "label": "Voucher Subtype"
   }
  ],
  "icon": "fa fa-list",
  "idx": 1,
  "in_create": 1,
  "links": [],
- "modified": "2023-08-16 21:38:44.072267",
+ "modified": "2023-12-18 15:38:14.006208",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "GL Entry",
@@ -321,4 +341,4 @@
  "sort_field": "modified",
  "sort_order": "DESC",
  "states": []
-}
\ No newline at end of file
+}
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py
index f7dd29a..139f526 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py
@@ -39,6 +39,8 @@
 		account: DF.Link | None
 		account_currency: DF.Link | None
 		against: DF.Text | None
+		against_link: DF.DynamicLink | None
+		against_type: DF.Link | None
 		against_voucher: DF.DynamicLink | None
 		against_voucher_type: DF.Link | None
 		company: DF.Link | None
@@ -66,6 +68,7 @@
 		transaction_exchange_rate: DF.Float
 		voucher_detail_no: DF.Data | None
 		voucher_no: DF.DynamicLink | None
+		voucher_subtype: DF.SmallText | None
 		voucher_type: DF.Link | None
 	# end: auto-generated types
 
diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
index 76f4dad..69b0860 100644
--- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
+++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
@@ -153,7 +153,9 @@
 							"account": inv.debit_to,
 							"party_type": "Customer",
 							"party": d.customer,
+							"against_type": "Account",
 							"against": self.accounts_receivable_credit,
+							"against_link": self.accounts_receivable_credit,
 							"credit": outstanding_in_company_currency,
 							"credit_in_account_currency": outstanding_in_company_currency
 							if inv.party_account_currency == company_currency
@@ -173,7 +175,9 @@
 							"account": self.accounts_receivable_credit,
 							"party_type": "Customer",
 							"party": d.customer,
+							"against_type": "Account",
 							"against": inv.debit_to,
+							"against_link": inv.debit_to,
 							"debit": outstanding_in_company_currency,
 							"debit_in_account_currency": outstanding_in_company_currency
 							if ar_credit_account_currency == company_currency
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js
index 266154d..c97a8dc 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.js
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js
@@ -220,6 +220,16 @@
 			return erpnext.journal_entry.account_query(me.frm);
 		});
 
+		me.frm.set_query("against_account_link", "accounts", function(doc, cdt, cdn) {
+			return erpnext.journal_entry.against_account_query(me.frm);
+		});
+
+		me.frm.set_query("against_type", "accounts", function(){
+			return {
+				query: "erpnext.accounts.doctype.journal_entry.journal_entry.get_against_type",
+			}
+		})
+
 		me.frm.set_query("party_type", "accounts", function(doc, cdt, cdn) {
 			const row = locals[cdt][cdn];
 
@@ -591,6 +601,21 @@
 		return { filters: filters };
 	},
 
+	against_account_query: function(frm) {
+		if (frm.doc.against_type != "Account"){
+			return { filters: {} };
+		}
+		else {
+			let filters = { company: frm.doc.company, is_group: 0 };
+			if(!frm.doc.multi_currency) {
+				$.extend(filters, {
+					account_currency: ['in', [frappe.get_doc(":Company", frm.doc.company).default_currency, null]]
+				});
+			}
+			return { filters: filters };
+		}
+	},
+
 	reverse_journal_entry: function() {
 		frappe.model.open_mapped_doc({
 			method: "erpnext.accounts.doctype.journal_entry.journal_entry.make_reverse_journal_entry",
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index 40d552b..79f1ab0 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -304,6 +304,7 @@
 					"account": tax_withholding_details.get("account_head"),
 					rev_debit_or_credit: tax_withholding_details.get("tax_amount"),
 					"against_account": parties[0],
+					"against_account_link": parties[0],
 				},
 			)
 
@@ -750,27 +751,90 @@
 					)
 
 	def set_against_account(self):
-		accounts_debited, accounts_credited = [], []
 		if self.voucher_type in ("Deferred Revenue", "Deferred Expense"):
 			for d in self.get("accounts"):
 				if d.reference_type == "Sales Invoice":
-					field = "customer"
+					against_type = "Customer"
 				else:
-					field = "supplier"
+					against_type = "Supplier"
 
-				d.against_account = frappe.db.get_value(d.reference_type, d.reference_name, field)
+				against_account = frappe.db.get_value(d.reference_type, d.reference_name, against_type.lower())
+				d.against_type = against_type
+				d.against_account_link = against_account
 		else:
-			for d in self.get("accounts"):
-				if flt(d.debit) > 0:
-					accounts_debited.append(d.party or d.account)
-				if flt(d.credit) > 0:
-					accounts_credited.append(d.party or d.account)
+			self.get_debited_credited_accounts()
+			if len(self.accounts_credited) > 1 and len(self.accounts_debited) > 1:
+				self.auto_set_against_accounts()
+				return
+			self.get_against_accounts()
 
-			for d in self.get("accounts"):
-				if flt(d.debit) > 0:
-					d.against_account = ", ".join(list(set(accounts_credited)))
-				if flt(d.credit) > 0:
-					d.against_account = ", ".join(list(set(accounts_debited)))
+	def auto_set_against_accounts(self):
+		for i in range(0, len(self.accounts), 2):
+			acc = self.accounts[i]
+			against_acc = self.accounts[i + 1]
+			if acc.debit_in_account_currency > 0:
+				current_val = acc.debit_in_account_currency * flt(acc.exchange_rate)
+				against_val = against_acc.credit_in_account_currency * flt(against_acc.exchange_rate)
+			else:
+				current_val = acc.credit_in_account_currency * flt(acc.exchange_rate)
+				against_val = against_acc.debit_in_account_currency * flt(against_acc.exchange_rate)
+
+			if current_val == against_val:
+				acc.against_type = against_acc.party_type or "Account"
+				against_acc.against_type = acc.party_type or "Account"
+
+				acc.against_account_link = against_acc.party or against_acc.account
+				against_acc.against_account_link = acc.party or acc.account
+			else:
+				frappe.msgprint(
+					_(
+						"Unable to automatically determine {0} accounts. Set them up in the {1} table if needed."
+					).format(frappe.bold("against"), frappe.bold("Accounting Entries")),
+					alert=True,
+				)
+				break
+
+	def get_against_accounts(self):
+		self.against_accounts = []
+		self.split_account = {}
+		self.get_debited_credited_accounts()
+
+		if self.separate_against_account_entries:
+			no_of_credited_acc, no_of_debited_acc = len(self.accounts_credited), len(self.accounts_debited)
+			if no_of_credited_acc <= 1 and no_of_debited_acc <= 1:
+				self.set_against_accounts_for_single_dr_cr()
+				self.separate_against_account_entries = 0
+			elif no_of_credited_acc == 1:
+				self.against_accounts = self.accounts_debited
+				self.split_account = self.accounts_credited[0]
+			elif no_of_debited_acc == 1:
+				self.against_accounts = self.accounts_credited
+				self.split_account = self.accounts_debited[0]
+
+	def get_debited_credited_accounts(self):
+		self.accounts_debited, self.accounts_credited = [], []
+		self.separate_against_account_entries = 1
+		for d in self.get("accounts"):
+			if flt(d.debit) > 0:
+				self.accounts_debited.append(d)
+			elif flt(d.credit) > 0:
+				self.accounts_credited.append(d)
+
+			if d.against_account_link:
+				self.separate_against_account_entries = 0
+				break
+
+	def set_against_accounts_for_single_dr_cr(self):
+		against_account = None
+		for d in self.accounts:
+			if flt(d.debit) > 0:
+				against_account = self.accounts_credited[0]
+			elif flt(d.credit) > 0:
+				against_account = self.accounts_debited[0]
+			if against_account:
+				d.against_type = against_account.party_type or "Account"
+				d.against_account = against_account.party or against_account.account
+				d.against_account_link = against_account.party or against_account.account
 
 	def validate_debit_credit_amount(self):
 		if not (self.voucher_type == "Exchange Gain Or Loss" and self.multi_currency):
@@ -967,40 +1031,82 @@
 
 	def build_gl_map(self):
 		gl_map = []
+		self.get_against_accounts()
 		for d in self.get("accounts"):
 			if d.debit or d.credit or (self.voucher_type == "Exchange Gain Or Loss"):
 				r = [d.user_remark, self.remark]
 				r = [x for x in r if x]
 				remarks = "\n".join(r)
 
-				gl_map.append(
-					self.get_gl_dict(
-						{
-							"account": d.account,
-							"party_type": d.party_type,
-							"due_date": self.due_date,
-							"party": d.party,
-							"against": d.against_account,
-							"debit": flt(d.debit, d.precision("debit")),
-							"credit": flt(d.credit, d.precision("credit")),
-							"account_currency": d.account_currency,
-							"debit_in_account_currency": flt(
-								d.debit_in_account_currency, d.precision("debit_in_account_currency")
-							),
-							"credit_in_account_currency": flt(
-								d.credit_in_account_currency, d.precision("credit_in_account_currency")
-							),
-							"against_voucher_type": d.reference_type,
-							"against_voucher": d.reference_name,
-							"remarks": remarks,
-							"voucher_detail_no": d.reference_detail_no,
-							"cost_center": d.cost_center,
-							"project": d.project,
-							"finance_book": self.finance_book,
-						},
-						item=d,
-					)
+				gl_dict = self.get_gl_dict(
+					{
+						"account": d.account,
+						"party_type": d.party_type,
+						"due_date": self.due_date,
+						"party": d.party,
+						"debit": flt(d.debit, d.precision("debit")),
+						"credit": flt(d.credit, d.precision("credit")),
+						"account_currency": d.account_currency,
+						"debit_in_account_currency": flt(
+							d.debit_in_account_currency, d.precision("debit_in_account_currency")
+						),
+						"credit_in_account_currency": flt(
+							d.credit_in_account_currency, d.precision("credit_in_account_currency")
+						),
+						"against_voucher_type": d.reference_type,
+						"against_voucher": d.reference_name,
+						"remarks": remarks,
+						"voucher_detail_no": d.reference_detail_no,
+						"cost_center": d.cost_center,
+						"project": d.project,
+						"finance_book": self.finance_book,
+					},
+					item=d,
 				)
+
+				if not self.separate_against_account_entries:
+					gl_dict.update(
+						{
+							"against_type": d.against_type,
+							"against_link": d.against_account_link,
+						}
+					)
+					gl_map.append(gl_dict)
+
+				elif d in self.against_accounts:
+					gl_dict.update(
+						{
+							"against_type": self.split_account.get("party_type") or "Account",
+							"against": self.split_account.get("party") or self.split_account.get("account"),
+							"against_link": self.split_account.get("party") or self.split_account.get("account"),
+						}
+					)
+					gl_map.append(gl_dict)
+
+				else:
+					for against_account in self.against_accounts:
+						against_account = against_account.as_dict()
+						debit = against_account.credit or against_account.credit_in_account_currency
+						credit = against_account.debit or against_account.debit_in_account_currency
+						gl_dict = gl_dict.copy()
+						gl_dict.update(
+							{
+								"against_type": against_account.party_type or "Account",
+								"against": against_account.party or against_account.account,
+								"against_link": against_account.party or against_account.account,
+								"debit": flt(debit, d.precision("debit")),
+								"credit": flt(credit, d.precision("credit")),
+								"account_currency": d.account_currency,
+								"debit_in_account_currency": flt(
+									debit / d.exchange_rate, d.precision("debit_in_account_currency")
+								),
+								"credit_in_account_currency": flt(
+									credit / d.exchange_rate, d.precision("credit_in_account_currency")
+								),
+							}
+						)
+						gl_map.append(gl_dict)
+
 		return gl_map
 
 	def make_gl_entries(self, cancel=0, adv_adj=0):
@@ -1625,3 +1731,10 @@
 	)
 
 	return doclist
+
+
+@frappe.whitelist()
+def get_against_type(doctype, txt, searchfield, start, page_len, filters):
+	against_types = frappe.db.get_list("Party Type", pluck="name") + ["Account"]
+	doctype = frappe.qb.DocType("DocType")
+	return frappe.qb.from_(doctype).select(doctype.name).where(doctype.name.isin(against_types)).run()
diff --git a/erpnext/accounts/doctype/journal_entry/test_records.json b/erpnext/accounts/doctype/journal_entry/test_records.json
index dafcf56..717c579 100644
--- a/erpnext/accounts/doctype/journal_entry/test_records.json
+++ b/erpnext/accounts/doctype/journal_entry/test_records.json
@@ -1,97 +1,94 @@
 [
- {
-  "cheque_date": "2013-03-14",
-  "cheque_no": "33",
-  "company": "_Test Company",
-  "doctype": "Journal Entry",
-  "accounts": [
-   {
-    "account": "Debtors - _TC",
-	  "party_type": "Customer",
-	  "party": "_Test Customer",
-    "credit_in_account_currency": 400.0,
-    "debit_in_account_currency": 0.0,
-    "doctype": "Journal Entry Account",
-    "parentfield": "accounts",
-    "cost_center": "_Test Cost Center - _TC"
-   },
-   {
-    "account": "_Test Bank - _TC",
-    "credit_in_account_currency": 0.0,
-    "debit_in_account_currency": 400.0,
-    "doctype": "Journal Entry Account",
-    "parentfield": "accounts",
-    "cost_center": "_Test Cost Center - _TC"
-   }
-  ],
-  "naming_series": "_T-Journal Entry-",
-  "posting_date": "2013-02-14",
-  "user_remark": "test",
-  "voucher_type": "Bank Entry"
- },
- 
- 
- {
-  "cheque_date": "2013-02-14",
-  "cheque_no": "33",
-  "company": "_Test Company",
-  "doctype": "Journal Entry",
-  "accounts": [
-   {
-    "account": "_Test Payable - _TC",
-  	"party_type": "Supplier",
-	  "party": "_Test Supplier",
-    "credit_in_account_currency": 0.0,
-    "debit_in_account_currency": 400.0,
-    "doctype": "Journal Entry Account",
-    "parentfield": "accounts",
-    "cost_center": "_Test Cost Center - _TC"
-   },
-   {
-    "account": "_Test Bank - _TC",
-    "credit_in_account_currency": 400.0,
-    "debit_in_account_currency": 0.0,
-    "doctype": "Journal Entry Account",
-    "parentfield": "accounts",
-    "cost_center": "_Test Cost Center - _TC"
-   }
-  ],
-  "naming_series": "_T-Journal Entry-",
-  "posting_date": "2013-02-14",
-  "user_remark": "test",
-  "voucher_type": "Bank Entry"
- },
- 
- 
- {
-  "cheque_date": "2013-02-14",
-  "cheque_no": "33",
-  "company": "_Test Company",
-  "doctype": "Journal Entry",
-  "accounts": [
-   {
-    "account": "Debtors - _TC",
-	  "party_type": "Customer",
-  	"party": "_Test Customer",
-    "credit_in_account_currency": 0.0,
-    "debit_in_account_currency": 400.0,
-    "doctype": "Journal Entry Account",
-    "parentfield": "accounts",
-    "cost_center": "_Test Cost Center - _TC"
-   },
-   {
-    "account": "Sales - _TC",
-    "cost_center": "_Test Cost Center - _TC",
-    "credit_in_account_currency": 400.0,
-    "debit_in_account_currency": 0.0,
-    "doctype": "Journal Entry Account",
-    "parentfield": "accounts",
-    "cost_center": "_Test Cost Center - _TC"
-   }
-  ],
-  "naming_series": "_T-Journal Entry-",
-  "posting_date": "2013-02-14",
-  "user_remark": "test",
-  "voucher_type": "Bank Entry"
- }
+	{
+		"cheque_date": "2013-03-14",
+		"cheque_no": "33",
+		"company": "_Test Company",
+		"doctype": "Journal Entry",
+		"accounts": [
+			{
+				"account": "Debtors - _TC",
+				"party_type": "Customer",
+				"party": "_Test Customer",
+				"credit_in_account_currency": 400.0,
+				"debit_in_account_currency": 0.0,
+				"doctype": "Journal Entry Account",
+				"parentfield": "accounts",
+				"cost_center": "_Test Cost Center - _TC"
+			},
+			{
+				"account": "_Test Bank - _TC",
+				"credit_in_account_currency": 0.0,
+				"debit_in_account_currency": 400.0,
+				"doctype": "Journal Entry Account",
+				"parentfield": "accounts",
+				"cost_center": "_Test Cost Center - _TC"
+			}
+		],
+		"naming_series": "_T-Journal Entry-",
+		"posting_date": "2013-02-14",
+		"user_remark": "test",
+		"voucher_type": "Bank Entry"
+	},
+
+	{
+		"cheque_date": "2013-02-14",
+		"cheque_no": "33",
+		"company": "_Test Company",
+		"doctype": "Journal Entry",
+		"accounts": [
+			{
+				"account": "_Test Payable - _TC",
+				"party_type": "Supplier",
+				"party": "_Test Supplier",
+				"credit_in_account_currency": 0.0,
+				"debit_in_account_currency": 400.0,
+				"doctype": "Journal Entry Account",
+				"parentfield": "accounts",
+				"cost_center": "_Test Cost Center - _TC"
+			},
+			{
+				"account": "_Test Bank - _TC",
+				"credit_in_account_currency": 400.0,
+				"debit_in_account_currency": 0.0,
+				"doctype": "Journal Entry Account",
+				"parentfield": "accounts",
+				"cost_center": "_Test Cost Center - _TC"
+			}
+		],
+		"naming_series": "_T-Journal Entry-",
+		"posting_date": "2013-02-14",
+		"user_remark": "test",
+		"voucher_type": "Bank Entry"
+	},
+
+	{
+		"cheque_date": "2013-02-14",
+		"cheque_no": "33",
+		"company": "_Test Company",
+		"doctype": "Journal Entry",
+		"accounts": [
+			{
+				"account": "Debtors - _TC",
+				"party_type": "Customer",
+				"party": "_Test Customer",
+				"credit_in_account_currency": 0.0,
+				"debit_in_account_currency": 400.0,
+				"doctype": "Journal Entry Account",
+				"parentfield": "accounts",
+				"cost_center": "_Test Cost Center - _TC"
+			},
+			{
+				"account": "Sales - _TC",
+				"credit_in_account_currency": 400.0,
+				"debit_in_account_currency": 0.0,
+				"doctype": "Journal Entry Account",
+				"parentfield": "accounts",
+				"cost_center": "_Test Cost Center - _TC"
+			}
+		],
+		"naming_series": "_T-Journal Entry-",
+		"posting_date": "2013-02-14",
+		"user_remark": "test",
+		"voucher_type": "Bank Entry"
+	}
 ]
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 3132fe9..01006bd 100644
--- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
+++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
@@ -37,7 +37,9 @@
   "col_break3",
   "is_advance",
   "user_remark",
-  "against_account"
+  "against_type",
+  "against_account",
+  "against_account_link"
  ],
  "fields": [
   {
@@ -250,14 +252,21 @@
    "print_hide": 1
   },
   {
-   "fieldname": "against_account",
-   "fieldtype": "Text",
-   "hidden": 1,
+    "fieldname": "against_account",
+    "fieldtype": "Text",
+    "hidden": 1,
+    "label": "Against Account",
+    "no_copy": 1,
+    "oldfieldname": "against_account",
+    "oldfieldtype": "Text",
+    "print_hide": 1
+  },
+  {
+   "fieldname": "against_account_link",
+   "fieldtype": "Dynamic Link",
    "label": "Against Account",
    "no_copy": 1,
-   "oldfieldname": "against_account",
-   "oldfieldtype": "Text",
-   "print_hide": 1
+   "options": "against_type"
   },
   {
    "collapsible": 1,
@@ -280,14 +289,19 @@
    "fieldtype": "Data",
    "hidden": 1,
    "label": "Reference Detail No",
-   "no_copy": 1,
-   "search_index": 1
+   "no_copy": 1
+  },
+  {
+   "fieldname": "against_type",
+   "fieldtype": "Link",
+   "label": "Against Type",
+   "options": "DocType"
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-11-23 11:44:25.841187",
+ "modified": "2023-12-02 23:21:22.205409",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Journal Entry Account",
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index a6ddce5..11c7c17 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -1061,7 +1061,9 @@
 					"account": self.party_account,
 					"party_type": self.party_type,
 					"party": self.party,
+					"against_type": "Account",
 					"against": against_account,
+					"against_link": against_account,
 					"account_currency": self.party_account_currency,
 					"cost_center": self.cost_center,
 				},
@@ -1226,7 +1228,9 @@
 					{
 						"account": self.paid_from,
 						"account_currency": self.paid_from_account_currency,
+						"against_type": self.party_type if self.payment_type == "Pay" else "Account",
 						"against": self.party if self.payment_type == "Pay" else self.paid_to,
+						"against_link": self.party if self.payment_type == "Pay" else self.paid_to,
 						"credit_in_account_currency": self.paid_amount,
 						"credit": self.base_paid_amount,
 						"cost_center": self.cost_center,
@@ -1241,7 +1245,9 @@
 					{
 						"account": self.paid_to,
 						"account_currency": self.paid_to_account_currency,
+						"against_type": self.party_type if self.payment_type == "Receive" else "Account",
 						"against": self.party if self.payment_type == "Receive" else self.paid_from,
+						"against_link": self.party if self.payment_type == "Receive" else self.paid_from,
 						"debit_in_account_currency": self.received_amount,
 						"debit": self.base_received_amount,
 						"cost_center": self.cost_center,
@@ -1265,6 +1271,7 @@
 				rev_dr_or_cr = "credit" if dr_or_cr == "debit" else "debit"
 				against = self.party or self.paid_to
 
+			against_type = self.party_type or "Account"
 			payment_account = self.get_party_account_for_taxes()
 			tax_amount = d.tax_amount
 			base_tax_amount = d.base_tax_amount
@@ -1273,7 +1280,9 @@
 				self.get_gl_dict(
 					{
 						"account": d.account_head,
+						"against_type": against_type,
 						"against": against,
+						"against_link": against,
 						dr_or_cr: tax_amount,
 						dr_or_cr + "_in_account_currency": base_tax_amount
 						if account_currency == self.company_currency
@@ -1298,7 +1307,9 @@
 					self.get_gl_dict(
 						{
 							"account": payment_account,
+							"against_type": against_type,
 							"against": against,
+							"against_link": against,
 							rev_dr_or_cr: tax_amount,
 							rev_dr_or_cr + "_in_account_currency": base_tax_amount
 							if account_currency == self.company_currency
@@ -1323,7 +1334,9 @@
 						{
 							"account": d.account,
 							"account_currency": account_currency,
+							"against_type": self.party_type or "Account",
 							"against": self.party or self.paid_from,
+							"against_link": self.party or self.paid_from,
 							"debit_in_account_currency": d.amount,
 							"debit": d.amount,
 							"cost_center": d.cost_center,
diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json
index a3a953f..ae6059c 100644
--- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json
+++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json
@@ -15,6 +15,7 @@
   "group_by",
   "cost_center",
   "territory",
+  "ignore_exchange_rate_revaluation_journals",
   "column_break_14",
   "to_date",
   "finance_book",
@@ -376,10 +377,16 @@
    "fieldname": "pdf_name",
    "fieldtype": "Data",
    "label": "PDF Name"
+  },
+  {
+   "default": "0",
+   "fieldname": "ignore_exchange_rate_revaluation_journals",
+   "fieldtype": "Check",
+   "label": "Ignore Exchange Rate Revaluation Journals"
   }
  ],
  "links": [],
- "modified": "2023-08-28 12:59:53.071334",
+ "modified": "2023-12-18 12:20:08.965120",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Process Statement Of Accounts",
diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
index 9ad2548..c03b18a 100644
--- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
+++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
@@ -56,6 +56,7 @@
 		frequency: DF.Literal["Weekly", "Monthly", "Quarterly"]
 		from_date: DF.Date | None
 		group_by: DF.Literal["", "Group by Voucher", "Group by Voucher (Consolidated)"]
+		ignore_exchange_rate_revaluation_journals: DF.Check
 		include_ageing: DF.Check
 		include_break: DF.Check
 		letter_head: DF.Link | None
@@ -119,6 +120,18 @@
 	statement_dict = {}
 	ageing = ""
 
+	err_journals = None
+	if doc.report == "General Ledger" and doc.ignore_exchange_rate_revaluation_journals:
+		err_journals = frappe.db.get_all(
+			"Journal Entry",
+			filters={
+				"company": doc.company,
+				"docstatus": 1,
+				"voucher_type": ("in", ["Exchange Rate Revaluation", "Exchange Gain Or Loss"]),
+			},
+			as_list=True,
+		)
+
 	for entry in doc.customers:
 		if doc.include_ageing:
 			ageing = set_ageing(doc, entry)
@@ -131,6 +144,8 @@
 		)
 
 		filters = get_common_filters(doc)
+		if err_journals:
+			filters.update({"voucher_no_not_in": [x[0] for x in err_journals]})
 
 		if doc.report == "General Ledger":
 			filters.update(get_gl_filters(doc, entry, tax_id, presentation_currency))
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index cebd61a..215d8ec 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -163,6 +163,18 @@
 					}
 				})
 			}, __("Get Items From"));
+
+			if (!this.frm.doc.is_return) {
+				frappe.db.get_single_value("Buying Settings", "maintain_same_rate").then((value) => {
+					if (value) {
+						this.frm.doc.items.forEach((item) => {
+							this.frm.fields_dict.items.grid.update_docfield_property(
+								"rate", "read_only", (item.purchase_receipt && item.pr_detail)
+							);
+						});
+					}
+				});
+			}
 		}
 		this.frm.toggle_reqd("supplier_warehouse", this.frm.doc.is_subcontracted);
 
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index ae377eb..f40824d 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -815,7 +815,9 @@
 						"party_type": "Supplier",
 						"party": self.supplier,
 						"due_date": self.due_date,
+						"against_type": "Account",
 						"against": self.against_expense_account,
+						"against_link": self.against_expense_account,
 						"credit": base_grand_total,
 						"credit_in_account_currency": base_grand_total
 						if self.party_account_currency == self.company_currency
@@ -888,7 +890,9 @@
 							self.get_gl_dict(
 								{
 									"account": warehouse_account[item.warehouse]["account"],
+									"against_type": "Account",
 									"against": warehouse_account[item.from_warehouse]["account"],
+									"against_link": warehouse_account[item.from_warehouse]["account"],
 									"cost_center": item.cost_center,
 									"project": item.project or self.project,
 									"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
@@ -908,7 +912,9 @@
 							self.get_gl_dict(
 								{
 									"account": warehouse_account[item.from_warehouse]["account"],
+									"against_type": "Account",
 									"against": warehouse_account[item.warehouse]["account"],
+									"against_link": warehouse_account[item.warehouse]["account"],
 									"cost_center": item.cost_center,
 									"project": item.project or self.project,
 									"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
@@ -925,7 +931,9 @@
 								self.get_gl_dict(
 									{
 										"account": item.expense_account,
+										"against_type": "Supplier",
 										"against": self.supplier,
+										"against_link": self.supplier,
 										"debit": flt(item.base_net_amount, item.precision("base_net_amount")),
 										"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
 										"cost_center": item.cost_center,
@@ -942,7 +950,9 @@
 								self.get_gl_dict(
 									{
 										"account": item.expense_account,
+										"against_type": "Supplier",
 										"against": self.supplier,
+										"against_link": self.supplier,
 										"debit": warehouse_debit_amount,
 										"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
 										"cost_center": item.cost_center,
@@ -961,7 +971,9 @@
 									self.get_gl_dict(
 										{
 											"account": account,
+											"against_type": "Account",
 											"against": item.expense_account,
+											"against_link": item.expense_account,
 											"cost_center": item.cost_center,
 											"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
 											"credit": flt(amount["base_amount"]),
@@ -981,7 +993,9 @@
 							self.get_gl_dict(
 								{
 									"account": supplier_warehouse_account,
+									"against_type": "Account",
 									"against": item.expense_account,
+									"against_link": item.expense_account,
 									"cost_center": item.cost_center,
 									"project": item.project or self.project,
 									"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
@@ -1036,7 +1050,9 @@
 							self.get_gl_dict(
 								{
 									"account": expense_account,
+									"against_type": "Supplier",
 									"against": self.supplier,
+									"against_link": self.supplier,
 									"debit": amount,
 									"cost_center": item.cost_center,
 									"project": item.project or self.project,
@@ -1062,7 +1078,9 @@
 									self.get_gl_dict(
 										{
 											"account": expense_account,
+											"against_type": "Supplier",
 											"against": self.supplier,
+											"against_link": self.supplier,
 											"debit": discrepancy_caused_by_exchange_rate_difference,
 											"cost_center": item.cost_center,
 											"project": item.project or self.project,
@@ -1075,7 +1093,9 @@
 									self.get_gl_dict(
 										{
 											"account": self.get_company_default("exchange_gain_loss_account"),
+											"against_type": "Supplier",
 											"against": self.supplier,
+											"against_link": self.supplier,
 											"credit": discrepancy_caused_by_exchange_rate_difference,
 											"cost_center": item.cost_center,
 											"project": item.project or self.project,
@@ -1120,7 +1140,9 @@
 							self.get_gl_dict(
 								{
 									"account": stock_rbnb,
+									"against_type": "Supplier",
 									"against": self.supplier,
+									"against_link": self.supplier,
 									"debit": flt(item.item_tax_amount, item.precision("item_tax_amount")),
 									"remarks": self.remarks or _("Accounting Entry for Stock"),
 									"cost_center": self.cost_center,
@@ -1168,7 +1190,9 @@
 				self.get_gl_dict(
 					{
 						"account": cost_of_goods_sold_account,
+						"against_type": "Account",
 						"against": item.expense_account,
+						"against_link": item.expense_account,
 						"debit": stock_adjustment_amt,
 						"remarks": self.get("remarks") or _("Stock Adjustment"),
 						"cost_center": item.cost_center,
@@ -1198,7 +1222,9 @@
 					self.get_gl_dict(
 						{
 							"account": tax.account_head,
+							"against_type": "Supplier",
 							"against": self.supplier,
+							"against_link": self.supplier,
 							dr_or_cr: base_amount,
 							dr_or_cr + "_in_account_currency": base_amount
 							if account_currency == self.company_currency
@@ -1246,8 +1272,10 @@
 						self.get_gl_dict(
 							{
 								"account": tax.account_head,
+								"against_type": "Supplier",
 								"cost_center": tax.cost_center,
 								"against": self.supplier,
+								"against_link": self.supplier,
 								"credit": applicable_amount,
 								"remarks": self.remarks or _("Accounting Entry for Stock"),
 							},
@@ -1265,7 +1293,9 @@
 							{
 								"account": tax.account_head,
 								"cost_center": tax.cost_center,
+								"against_type": "Supplier",
 								"against": self.supplier,
+								"against_link": self.supplier,
 								"credit": valuation_tax[tax.name],
 								"remarks": self.remarks or _("Accounting Entry for Stock"),
 							},
@@ -1280,7 +1310,9 @@
 				self.get_gl_dict(
 					{
 						"account": self.unrealized_profit_loss_account,
+						"against_type": "Supplier",
 						"against": self.supplier,
+						"against_link": self.supplier,
 						"credit": flt(self.total_taxes_and_charges),
 						"credit_in_account_currency": flt(self.base_total_taxes_and_charges),
 						"cost_center": self.cost_center,
@@ -1301,7 +1333,9 @@
 						"account": self.credit_to,
 						"party_type": "Supplier",
 						"party": self.supplier,
+						"against_type": "Account",
 						"against": self.cash_bank_account,
+						"against_link": self.cash_bank_account,
 						"debit": self.base_paid_amount,
 						"debit_in_account_currency": self.base_paid_amount
 						if self.party_account_currency == self.company_currency
@@ -1322,7 +1356,9 @@
 				self.get_gl_dict(
 					{
 						"account": self.cash_bank_account,
+						"against_type": "Supplier",
 						"against": self.supplier,
+						"against_link": self.supplier,
 						"credit": self.base_paid_amount,
 						"credit_in_account_currency": self.base_paid_amount
 						if bank_account_currency == self.company_currency
@@ -1346,7 +1382,9 @@
 						"account": self.credit_to,
 						"party_type": "Supplier",
 						"party": self.supplier,
+						"against_type": "Account",
 						"against": self.write_off_account,
+						"against_link": self.write_off_account,
 						"debit": self.base_write_off_amount,
 						"debit_in_account_currency": self.base_write_off_amount
 						if self.party_account_currency == self.company_currency
@@ -1366,7 +1404,9 @@
 				self.get_gl_dict(
 					{
 						"account": self.write_off_account,
+						"against_type": "Supplier",
 						"against": self.supplier,
+						"against_link": self.supplier,
 						"credit": flt(self.base_write_off_amount),
 						"credit_in_account_currency": self.base_write_off_amount
 						if write_off_account_currency == self.company_currency
@@ -1393,7 +1433,9 @@
 				self.get_gl_dict(
 					{
 						"account": round_off_account,
+						"against_type": "Supplier",
 						"against": self.supplier,
+						"against_link": self.supplier,
 						"debit_in_account_currency": self.rounding_adjustment,
 						"debit": self.base_rounding_adjustment,
 						"cost_center": round_off_cost_center
@@ -1816,10 +1858,6 @@
 	return make_inter_company_transaction("Purchase Invoice", source_name, target_doc)
 
 
-def on_doctype_update():
-	frappe.db.add_index("Purchase Invoice", ["supplier", "is_return", "return_against"])
-
-
 @frappe.whitelist()
 def make_purchase_receipt(source_name, target_doc=None):
 	def update_item(obj, target, source_parent):
diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
index 7cad3ae..9cf4e4f 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
@@ -288,7 +288,6 @@
    "oldfieldname": "import_rate",
    "oldfieldtype": "Currency",
    "options": "currency",
-   "read_only_depends_on": "eval: (!parent.is_return && doc.purchase_receipt && doc.pr_detail)",
    "reqd": 1
   },
   {
@@ -919,7 +918,7 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-11-30 16:26:05.629780",
+ "modified": "2023-12-25 22:00:28.043555",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Purchase Invoice Item",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index f2f4dda..2cddb86 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -7,7 +7,7 @@
 from frappe.contacts.doctype.address.address import get_address_display
 from frappe.model.mapper import get_mapped_doc
 from frappe.model.utils import get_fetch_values
-from frappe.utils import add_days, cint, cstr, flt, formatdate, get_link_to_form, getdate, nowdate
+from frappe.utils import add_days, cint, flt, formatdate, get_link_to_form, getdate, nowdate
 
 import erpnext
 from erpnext.accounts.deferred_revenue import validate_service_stop_date
@@ -1225,7 +1225,9 @@
 						"party_type": "Customer",
 						"party": self.customer,
 						"due_date": self.due_date,
+						"against_type": "Account",
 						"against": self.against_income_account,
+						"against_link": self.against_income_account,
 						"debit": base_grand_total,
 						"debit_in_account_currency": base_grand_total
 						if self.party_account_currency == self.company_currency
@@ -1254,7 +1256,9 @@
 					self.get_gl_dict(
 						{
 							"account": tax.account_head,
+							"against_type": "Customer",
 							"against": self.customer,
+							"against_link": self.customer,
 							"credit": flt(base_amount, tax.precision("tax_amount_after_discount_amount")),
 							"credit_in_account_currency": (
 								flt(base_amount, tax.precision("base_tax_amount_after_discount_amount"))
@@ -1275,7 +1279,9 @@
 				self.get_gl_dict(
 					{
 						"account": self.unrealized_profit_loss_account,
+						"against_type": "Customer",
 						"against": self.customer,
+						"against_link": self.customer,
 						"debit": flt(self.total_taxes_and_charges),
 						"debit_in_account_currency": flt(self.base_total_taxes_and_charges),
 						"cost_center": self.cost_center,
@@ -1343,7 +1349,9 @@
 						add_asset_activity(asset.name, _("Asset sold"))
 
 					for gle in fixed_asset_gl_entries:
+						gle["against_type"] = "Customer"
 						gle["against"] = self.customer
+						gle["against_link"] = self.customer
 						gl_entries.append(self.get_gl_dict(gle, item=item))
 
 					self.set_asset_status(asset)
@@ -1364,7 +1372,9 @@
 							self.get_gl_dict(
 								{
 									"account": income_account,
+									"against_type": "Customer",
 									"against": self.customer,
+									"against_link": self.customer,
 									"credit": flt(base_amount, item.precision("base_net_amount")),
 									"credit_in_account_currency": (
 										flt(base_amount, item.precision("base_net_amount"))
@@ -1418,9 +1428,9 @@
 						"account": self.debit_to,
 						"party_type": "Customer",
 						"party": self.customer,
-						"against": "Expense account - "
-						+ cstr(self.loyalty_redemption_account)
-						+ " for the Loyalty Program",
+						"against_type": "Account",
+						"against": self.loyalty_redemption_account,
+						"against_link": self.loyalty_redemption_account,
 						"credit": self.loyalty_amount,
 						"against_voucher": self.return_against if cint(self.is_return) else self.name,
 						"against_voucher_type": self.doctype,
@@ -1434,7 +1444,9 @@
 					{
 						"account": self.loyalty_redemption_account,
 						"cost_center": self.cost_center or self.loyalty_redemption_cost_center,
+						"against_type": "Customer",
 						"against": self.customer,
+						"against_link": self.customer,
 						"debit": self.loyalty_amount,
 						"remark": "Loyalty Points redeemed by the customer",
 					},
@@ -1461,7 +1473,9 @@
 								"account": self.debit_to,
 								"party_type": "Customer",
 								"party": self.customer,
+								"against_type": "Account",
 								"against": payment_mode.account,
+								"against_link": payment_mode.account,
 								"credit": payment_mode.base_amount,
 								"credit_in_account_currency": payment_mode.base_amount
 								if self.party_account_currency == self.company_currency
@@ -1482,7 +1496,9 @@
 						self.get_gl_dict(
 							{
 								"account": payment_mode.account,
+								"against_type": "Customer",
 								"against": self.customer,
+								"against_link": self.customer,
 								"debit": payment_mode.base_amount,
 								"debit_in_account_currency": payment_mode.base_amount
 								if payment_mode_account_currency == self.company_currency
@@ -1506,7 +1522,9 @@
 							"account": self.debit_to,
 							"party_type": "Customer",
 							"party": self.customer,
+							"against_type": "Account",
 							"against": self.account_for_change_amount,
+							"against_link": self.account_for_change_amount,
 							"debit": flt(self.base_change_amount),
 							"debit_in_account_currency": flt(self.base_change_amount)
 							if self.party_account_currency == self.company_currency
@@ -1527,7 +1545,9 @@
 					self.get_gl_dict(
 						{
 							"account": self.account_for_change_amount,
+							"against_type": "Customer",
 							"against": self.customer,
+							"against_link": self.customer,
 							"credit": self.base_change_amount,
 							"cost_center": self.cost_center,
 						},
@@ -1553,7 +1573,9 @@
 						"account": self.debit_to,
 						"party_type": "Customer",
 						"party": self.customer,
+						"against_type": "Account",
 						"against": self.write_off_account,
+						"against_link": self.write_off_account,
 						"credit": flt(self.base_write_off_amount, self.precision("base_write_off_amount")),
 						"credit_in_account_currency": (
 							flt(self.base_write_off_amount, self.precision("base_write_off_amount"))
@@ -1573,7 +1595,9 @@
 				self.get_gl_dict(
 					{
 						"account": self.write_off_account,
+						"against_type": "Customer",
 						"against": self.customer,
+						"against_link": self.customer,
 						"debit": flt(self.base_write_off_amount, self.precision("base_write_off_amount")),
 						"debit_in_account_currency": (
 							flt(self.base_write_off_amount, self.precision("base_write_off_amount"))
@@ -1601,7 +1625,9 @@
 				self.get_gl_dict(
 					{
 						"account": round_off_account,
+						"against_type": "Customer",
 						"against": self.customer,
+						"against_link": self.customer,
 						"credit_in_account_currency": flt(
 							self.rounding_adjustment, self.precision("rounding_adjustment")
 						),
@@ -2356,9 +2382,18 @@
 
 
 def get_received_items(reference_name, doctype, reference_fieldname):
+	reference_field = "inter_company_invoice_reference"
+	if doctype == "Purchase Order":
+		reference_field = "inter_company_order_reference"
+
+	filters = {
+		reference_field: reference_name,
+		"docstatus": 1,
+	}
+
 	target_doctypes = frappe.get_all(
 		doctype,
-		filters={"inter_company_invoice_reference": reference_name, "docstatus": 1},
+		filters=filters,
 		as_list=True,
 	)
 
@@ -2540,10 +2575,6 @@
 		return lp_details
 
 
-def on_doctype_update():
-	frappe.db.add_index("Sales Invoice", ["customer", "is_return", "return_against"])
-
-
 @frappe.whitelist()
 def create_invoice_discounting(source_name, target_doc=None):
 	invoice = frappe.get_doc("Sales Invoice", source_name)
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index 030a41e..91ba239 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -280,6 +280,7 @@
 		"project",
 		"finance_book",
 		"voucher_no",
+		"against_link",
 	]
 
 	if dimensions:
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index 50d5eae..9feda11 100644
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -225,7 +225,7 @@
 		if not row:
 			return
 
-		if self.filters.get("in_party_currency"):
+		if self.filters.get("in_party_currency") or self.filters.get("party_account"):
 			amount = ple.amount_in_account_currency
 		else:
 			amount = ple.amount
@@ -244,8 +244,12 @@
 				row.invoiced_in_account_currency += amount_in_account_currency
 		else:
 			if self.is_invoice(ple):
-				row.credit_note -= amount
-				row.credit_note_in_account_currency -= amount_in_account_currency
+				if row.voucher_no == ple.voucher_no == ple.against_voucher_no:
+					row.paid -= amount
+					row.paid_in_account_currency -= amount_in_account_currency
+				else:
+					row.credit_note -= amount
+					row.credit_note_in_account_currency -= amount_in_account_currency
 			else:
 				row.paid -= amount
 				row.paid_in_account_currency -= amount_in_account_currency
@@ -451,7 +455,7 @@
 		party_details = self.get_party_details(row.party) or {}
 		row.update(party_details)
 
-		if self.filters.get("in_party_currency"):
+		if self.filters.get("in_party_currency") or self.filters.get("party_account"):
 			row.currency = row.account_currency
 		else:
 			row.currency = self.company_currency
diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
index dd0842d..976935b 100644
--- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
@@ -76,6 +76,41 @@
 
 		return credit_note
 
+	def test_pos_receivable(self):
+		filters = {
+			"company": self.company,
+			"party_type": "Customer",
+			"party": [self.customer],
+			"report_date": add_days(today(), 2),
+			"based_on_payment_terms": 0,
+			"range1": 30,
+			"range2": 60,
+			"range3": 90,
+			"range4": 120,
+			"show_remarks": False,
+		}
+
+		pos_inv = self.create_sales_invoice(no_payment_schedule=True, do_not_submit=True)
+		pos_inv.posting_date = add_days(today(), 2)
+		pos_inv.is_pos = 1
+		pos_inv.append(
+			"payments",
+			frappe._dict(
+				mode_of_payment="Cash",
+				amount=flt(pos_inv.grand_total / 2),
+			),
+		)
+		pos_inv.disable_rounded_total = 1
+		pos_inv.save()
+		pos_inv.submit()
+
+		report = execute(filters)
+		expected_data = [[pos_inv.grand_total, pos_inv.paid_amount, 0]]
+
+		row = report[1][-1]
+		self.assertEqual(expected_data[0], [row.invoiced, row.paid, row.credit_note])
+		pos_inv.cancel()
+
 	def test_accounts_receivable(self):
 		filters = {
 			"company": self.company,
@@ -544,7 +579,7 @@
 		filters.update({"party_account": self.debtors_usd})
 		report = execute(filters)[1]
 		self.assertEqual(len(report), 1)
-		expected_data = [8000.0, 8000.0, self.debtors_usd, si2.currency]
+		expected_data = [100.0, 100.0, self.debtors_usd, si2.currency]
 		row = report[0]
 		self.assertEqual(
 			expected_data, [row.invoiced, row.outstanding, row.party_account, row.account_currency]
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py
index ac06a12..4054dca 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/general_ledger.py
@@ -200,10 +200,10 @@
 		"""
 		select
 			name as gl_entry, posting_date, account, party_type, party,
-			voucher_type, voucher_no, {dimension_fields}
+			voucher_type, voucher_subtype, voucher_no, {dimension_fields}
 			cost_center, project, {transaction_currency_fields}
 			against_voucher_type, against_voucher, account_currency,
-			against, is_opening, creation {select_fields}
+			against_link, against, is_opening, creation {select_fields}
 		from `tabGL Entry`
 		where company=%(company)s {conditions}
 		{order_by_statement}
@@ -238,6 +238,9 @@
 	if filters.get("voucher_no"):
 		conditions.append("voucher_no=%(voucher_no)s")
 
+	if filters.get("voucher_no_not_in"):
+		conditions.append("voucher_no not in %(voucher_no_not_in)s")
+
 	if filters.get("group_by") == "Group by Party" and not filters.get("party_type"):
 		conditions.append("party_type in ('Customer', 'Supplier')")
 
@@ -392,6 +395,7 @@
 	group_by = group_by_field(filters.get("group_by"))
 
 	for gle in gl_entries:
+		gle.against = gle.get("against_link") or gle.get("against")
 		gle_map.setdefault(gle.get(group_by), _dict(totals=get_totals_dict(), entries=[]))
 	return gle_map
 
@@ -606,6 +610,12 @@
 	columns += [
 		{"label": _("Voucher Type"), "fieldname": "voucher_type", "width": 120},
 		{
+			"label": _("Voucher Subtype"),
+			"fieldname": "voucher_subtype",
+			"fieldtype": "Data",
+			"width": 180,
+		},
+		{
 			"label": _("Voucher No"),
 			"fieldname": "voucher_no",
 			"fieldtype": "Dynamic Link",
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 0f21c86..adec0ab 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -642,6 +642,7 @@
 	new_row.set("reference_name", d["against_voucher"])
 
 	new_row.against_account = cstr(jv_detail.against_account)
+	new_row.against_account_link = cstr(jv_detail.against_account)
 	new_row.is_advance = cstr(jv_detail.is_advance)
 	new_row.docstatus = 1
 
diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json
index 540a4f5..ac712d4 100644
--- a/erpnext/assets/doctype/asset/asset.json
+++ b/erpnext/assets/doctype/asset/asset.json
@@ -35,6 +35,8 @@
   "purchase_receipt",
   "purchase_invoice",
   "available_for_use_date",
+  "total_asset_cost",
+  "additional_asset_cost",
   "column_break_23",
   "gross_purchase_amount",
   "asset_quantity",
@@ -529,6 +531,22 @@
    "label": "Capitalized In",
    "options": "Asset Capitalization",
    "read_only": 1
+  },
+  {
+   "depends_on": "eval:doc.docstatus > 0",
+   "fieldname": "total_asset_cost",
+   "fieldtype": "Currency",
+   "label": "Total Asset Cost",
+   "options": "Company:company:default_currency",
+   "read_only": 1
+  },
+  {
+   "depends_on": "eval:doc.docstatus > 0",
+   "fieldname": "additional_asset_cost",
+   "fieldtype": "Currency",
+   "label": "Additional Asset Cost",
+   "options": "Company:company:default_currency",
+   "read_only": 1
   }
  ],
  "idx": 72,
@@ -572,7 +590,7 @@
    "link_fieldname": "target_asset"
   }
  ],
- "modified": "2023-11-20 20:57:37.010467",
+ "modified": "2023-12-21 16:46:20.732869",
  "modified_by": "Administrator",
  "module": "Assets",
  "name": "Asset",
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 707ce19..dd34189 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -50,6 +50,7 @@
 
 		from erpnext.assets.doctype.asset_finance_book.asset_finance_book import AssetFinanceBook
 
+		additional_asset_cost: DF.Currency
 		amended_from: DF.Link | None
 		asset_category: DF.Link | None
 		asset_name: DF.Data
@@ -111,6 +112,7 @@
 			"Decapitalized",
 		]
 		supplier: DF.Link | None
+		total_asset_cost: DF.Currency
 		total_number_of_depreciations: DF.Int
 		value_after_depreciation: DF.Currency
 	# end: auto-generated types
@@ -144,6 +146,7 @@
 							).format(asset_depr_schedules_links)
 						)
 
+		self.total_asset_cost = self.gross_purchase_amount
 		self.status = self.get_status()
 
 	def on_submit(self):
@@ -689,7 +692,9 @@
 				self.get_gl_dict(
 					{
 						"account": cwip_account,
+						"against_type": "Account",
 						"against": fixed_asset_account,
+						"against_link": fixed_asset_account,
 						"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
 						"posting_date": self.available_for_use_date,
 						"credit": self.purchase_receipt_amount,
@@ -704,7 +709,9 @@
 				self.get_gl_dict(
 					{
 						"account": fixed_asset_account,
+						"against_type": "Account",
 						"against": cwip_account,
+						"against_link": cwip_account,
 						"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
 						"posting_date": self.available_for_use_date,
 						"debit": self.purchase_receipt_amount,
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index dc80aa5..0773698 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -251,7 +251,16 @@
 				flt(18000.0 + pro_rata_amount, asset.precision("gross_purchase_amount")),
 				0.0,
 			),
-			("_Test Fixed Asset - _TC", 0.0, 100000.0),
+			(
+				"_Test Fixed Asset - _TC",
+				0.0,
+				flt(18000.0 + pro_rata_amount, asset.precision("gross_purchase_amount")),
+			),
+			(
+				"_Test Fixed Asset - _TC",
+				0.0,
+				flt(82000.0 - pro_rata_amount, asset.precision("gross_purchase_amount")),
+			),
 			(
 				"_Test Gain/Loss on Asset Disposal - _TC",
 				flt(82000.0 - pro_rata_amount, asset.precision("gross_purchase_amount")),
diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
index 66997ca..de75841 100644
--- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
+++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
@@ -485,7 +485,9 @@
 						self.get_gl_dict(
 							{
 								"account": account,
+								"against_type": "Account",
 								"against": target_account,
+								"against_link": target_account,
 								"cost_center": item_row.cost_center,
 								"project": item_row.get("project") or self.get("project"),
 								"remarks": self.get("remarks") or "Accounting Entry for Stock",
@@ -526,7 +528,9 @@
 			self.set_consumed_asset_status(asset)
 
 			for gle in fixed_asset_gl_entries:
+				gle["against_type"] = "Account"
 				gle["against"] = target_account
+				gle["against_link"] = target_account
 				gl_entries.append(self.get_gl_dict(gle, item=item))
 				target_against.add(gle["account"])
 
@@ -542,7 +546,9 @@
 				self.get_gl_dict(
 					{
 						"account": item_row.expense_account,
+						"against_type": "Account",
 						"against": target_account,
+						"against_link": target_account,
 						"cost_center": item_row.cost_center,
 						"project": item_row.get("project") or self.get("project"),
 						"remarks": self.get("remarks") or "Accounting Entry for Stock",
@@ -553,41 +559,46 @@
 			)
 
 	def get_gl_entries_for_target_item(self, gl_entries, target_against, precision):
-		if self.target_is_fixed_asset:
-			# Capitalization
-			gl_entries.append(
-				self.get_gl_dict(
-					{
-						"account": self.target_fixed_asset_account,
-						"against": ", ".join(target_against),
-						"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
-						"debit": flt(self.total_value, precision),
-						"cost_center": self.get("cost_center"),
-					},
-					item=self,
-				)
-			)
-		else:
-			# Target Stock Item
-			sle_list = self.sle_map.get(self.name)
-			for sle in sle_list:
-				stock_value_difference = flt(sle.stock_value_difference, precision)
-				account = self.warehouse_account[sle.warehouse]["account"]
-
+		for target_account in target_against:
+			if self.target_is_fixed_asset:
+				# Capitalization
 				gl_entries.append(
 					self.get_gl_dict(
 						{
-							"account": account,
-							"against": ", ".join(target_against),
-							"cost_center": self.cost_center,
-							"project": self.get("project"),
-							"remarks": self.get("remarks") or "Accounting Entry for Stock",
-							"debit": stock_value_difference,
+							"account": self.target_fixed_asset_account,
+							"against_type": "Account",
+							"against": target_account,
+							"against_link": target_account,
+							"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
+							"debit": flt(self.total_value, precision) / len(target_against),
+							"cost_center": self.get("cost_center"),
 						},
-						self.warehouse_account[sle.warehouse]["account_currency"],
 						item=self,
 					)
 				)
+			else:
+				# Target Stock Item
+				sle_list = self.sle_map.get(self.name)
+				for sle in sle_list:
+					stock_value_difference = flt(sle.stock_value_difference, precision)
+					account = self.warehouse_account[sle.warehouse]["account"]
+
+					gl_entries.append(
+						self.get_gl_dict(
+							{
+								"account": account,
+								"against_type": "Account",
+								"against": target_account,
+								"against_link": target_account,
+								"cost_center": self.cost_center,
+								"project": self.get("project"),
+								"remarks": self.get("remarks") or "Accounting Entry for Stock",
+								"debit": stock_value_difference / len(target_against),
+							},
+							self.warehouse_account[sle.warehouse]["account_currency"],
+							item=self,
+						)
+					)
 
 	def create_target_asset(self):
 		if (
diff --git a/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py
index ac7c90d..7a7a10d 100644
--- a/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py
+++ b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py
@@ -98,12 +98,12 @@
 
 		# Test General Ledger Entries
 		expected_gle = {
-			"_Test Fixed Asset - TCP1": 3000,
+			"_Test Fixed Asset - TCP1": 2999.99,
 			"Expenses Included In Asset Valuation - TCP1": -1000,
 			"_Test Warehouse - TCP1": -2000,
+			"Round Off - TCP1": 0.01,
 		}
 		actual_gle = get_actual_gle_dict(asset_capitalization.name)
-
 		self.assertEqual(actual_gle, expected_gle)
 
 		# Test Stock Ledger Entries
@@ -189,9 +189,10 @@
 		# Test General Ledger Entries
 		default_expense_account = frappe.db.get_value("Company", company, "default_expense_account")
 		expected_gle = {
-			"_Test Fixed Asset - _TC": 3000,
+			"_Test Fixed Asset - _TC": 2999.99,
 			"Expenses Included In Asset Valuation - _TC": -1000,
 			default_expense_account: -2000,
+			"Round Off - _TC": 0.01,
 		}
 		actual_gle = get_actual_gle_dict(asset_capitalization.name)
 
@@ -376,9 +377,10 @@
 
 		# Test General Ledger Entries
 		expected_gle = {
-			"_Test Warehouse - TCP1": consumed_asset_value_before_disposal,
 			"_Test Accumulated Depreciations - TCP1": accumulated_depreciation,
 			"_Test Fixed Asset - TCP1": -consumed_asset_purchase_value,
+			"_Test Warehouse - TCP1": consumed_asset_value_before_disposal - 0.01,
+			"Round Off - TCP1": 0.01,
 		}
 		actual_gle = get_actual_gle_dict(asset_capitalization.name)
 		self.assertEqual(actual_gle, expected_gle)
diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py
index c0fb3c2..bb627d4 100644
--- a/erpnext/assets/doctype/asset_repair/asset_repair.py
+++ b/erpnext/assets/doctype/asset_repair/asset_repair.py
@@ -93,6 +93,10 @@
 
 			self.increase_asset_value()
 
+			if self.capitalize_repair_cost:
+				self.asset_doc.total_asset_cost += self.repair_cost
+				self.asset_doc.additional_asset_cost += self.repair_cost
+
 			if self.get("stock_consumption"):
 				self.check_for_stock_items_and_warehouse()
 				self.decrease_stock_quantity()
@@ -128,6 +132,10 @@
 
 			self.decrease_asset_value()
 
+			if self.capitalize_repair_cost:
+				self.asset_doc.total_asset_cost -= self.repair_cost
+				self.asset_doc.additional_asset_cost -= self.repair_cost
+
 			if self.get("stock_consumption"):
 				self.increase_stock_quantity()
 			if self.get("capitalize_repair_cost"):
@@ -277,7 +285,9 @@
 					"account": fixed_asset_account,
 					"debit": self.repair_cost,
 					"debit_in_account_currency": self.repair_cost,
+					"against_type": "Account",
 					"against": pi_expense_account,
+					"against_link": pi_expense_account,
 					"voucher_type": self.doctype,
 					"voucher_no": self.name,
 					"cost_center": self.cost_center,
@@ -296,7 +306,9 @@
 					"account": pi_expense_account,
 					"credit": self.repair_cost,
 					"credit_in_account_currency": self.repair_cost,
+					"against_type": "Account",
 					"against": fixed_asset_account,
+					"against_link": fixed_asset_account,
 					"voucher_type": self.doctype,
 					"voucher_no": self.name,
 					"cost_center": self.cost_center,
@@ -330,7 +342,9 @@
 							"account": item.expense_account or default_expense_account,
 							"credit": item.amount,
 							"credit_in_account_currency": item.amount,
+							"against_type": "Account",
 							"against": fixed_asset_account,
+							"against_link": fixed_asset_account,
 							"voucher_type": self.doctype,
 							"voucher_no": self.name,
 							"cost_center": self.cost_center,
@@ -347,7 +361,9 @@
 							"account": fixed_asset_account,
 							"debit": item.amount,
 							"debit_in_account_currency": item.amount,
+							"against_type": "Account",
 							"against": item.expense_account or default_expense_account,
+							"against_link": item.expense_account or default_expense_account,
 							"voucher_type": self.doctype,
 							"voucher_no": self.name,
 							"cost_center": self.cost_center,
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js
index 88faeee..3b671bb 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.js
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.js
@@ -214,7 +214,7 @@
 		}
 	},
 
-	fg_item_qty: async function(frm, cdt, cdn) {
+	qty: async function (frm, cdt, cdn) {
 		if (frm.doc.is_subcontracted && !frm.doc.is_old_subcontracting_flow) {
 			var row = locals[cdt][cdn];
 
@@ -222,7 +222,7 @@
 				var result = await frm.events.get_subcontracting_boms_for_finished_goods(row.fg_item)
 
 				if (result.message && row.item_code == result.message.service_item && row.uom == result.message.service_item_uom) {
-					frappe.model.set_value(cdt, cdn, "qty", flt(row.fg_item_qty) * flt(result.message.conversion_factor));
+					frappe.model.set_value(cdt, cdn, "fg_item_qty", flt(row.qty) / flt(result.message.conversion_factor));
 				}
 			}
 		}
diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
index 98c1b38..5a24cc2 100644
--- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
+++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
@@ -123,8 +123,7 @@
    "oldfieldname": "item_code",
    "oldfieldtype": "Link",
    "options": "Item",
-   "reqd": 1,
-   "search_index": 1
+   "reqd": 1
   },
   {
    "fieldname": "supplier_part_no",
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 05c6a35..febad18 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -874,6 +874,7 @@
 				"project": self.get("project"),
 				"post_net_value": args.get("post_net_value"),
 				"voucher_detail_no": args.get("voucher_detail_no"),
+				"voucher_subtype": self.get_voucher_subtype(),
 			}
 		)
 
@@ -929,6 +930,25 @@
 
 		return gl_dict
 
+	def get_voucher_subtype(self):
+		voucher_subtypes = {
+			"Journal Entry": "voucher_type",
+			"Payment Entry": "payment_type",
+			"Stock Entry": "stock_entry_type",
+			"Asset Capitalization": "entry_type",
+		}
+		if self.doctype in voucher_subtypes:
+			return self.get(voucher_subtypes[self.doctype])
+		elif self.doctype == "Purchase Receipt" and self.is_return:
+			return "Purchase Return"
+		elif self.doctype == "Delivery Note" and self.is_return:
+			return "Sales Return"
+		elif (self.doctype == "Sales Invoice" and self.is_return) or self.doctype == "Purchase Invoice":
+			return "Credit Note"
+		elif (self.doctype == "Purchase Invoice" and self.is_return) or self.doctype == "Sales Invoice":
+			return "Debit Note"
+		return self.doctype
+
 	def get_value_in_transaction_currency(self, account_currency, args, field):
 		if account_currency == self.get("currency"):
 			return args.get(field + "_in_account_currency")
@@ -1120,6 +1140,7 @@
 		)
 
 		credit_or_debit = "credit" if self.doctype == "Purchase Invoice" else "debit"
+		against_type = "Supplier" if self.doctype == "Purchase Invoice" else "Customer"
 		against = self.supplier if self.doctype == "Purchase Invoice" else self.customer
 
 		if precision_loss:
@@ -1127,7 +1148,9 @@
 				self.get_gl_dict(
 					{
 						"account": round_off_account,
+						"against_type": against_type,
 						"against": against,
+						"against_link": against,
 						credit_or_debit: precision_loss,
 						"cost_center": round_off_cost_center
 						if self.use_company_roundoff_cost_center
@@ -1476,11 +1499,13 @@
 		if self.doctype == "Purchase Invoice":
 			dr_or_cr = "credit"
 			rev_dr_cr = "debit"
+			against_type = "Supplier"
 			supplier_or_customer = self.supplier
 
 		else:
 			dr_or_cr = "debit"
 			rev_dr_cr = "credit"
+			against_type = "Customer"
 			supplier_or_customer = self.customer
 
 		if enable_discount_accounting:
@@ -1505,7 +1530,9 @@
 						self.get_gl_dict(
 							{
 								"account": item.discount_account,
+								"against_type": against_type,
 								"against": supplier_or_customer,
+								"against_link": supplier_or_customer,
 								dr_or_cr: flt(
 									discount_amount * self.get("conversion_rate"), item.precision("discount_amount")
 								),
@@ -1523,7 +1550,9 @@
 						self.get_gl_dict(
 							{
 								"account": income_or_expense_account,
+								"against_type": against_type,
 								"against": supplier_or_customer,
+								"against_link": supplier_or_customer,
 								rev_dr_cr: flt(
 									discount_amount * self.get("conversion_rate"), item.precision("discount_amount")
 								),
@@ -1546,7 +1575,9 @@
 				self.get_gl_dict(
 					{
 						"account": self.additional_discount_account,
+						"against_type": against_type,
 						"against": supplier_or_customer,
+						"against_link": supplier_or_customer,
 						dr_or_cr: self.base_discount_amount,
 						"cost_center": self.cost_center or erpnext.get_default_cost_center(self.company),
 					},
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index 3d863e9..572fa51 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -381,7 +381,11 @@
 
 					rate = flt(outgoing_rate * (d.conversion_factor or 1), d.precision("rate"))
 				else:
-					field = "incoming_rate" if self.get("is_internal_supplier") else "rate"
+					field = (
+						"incoming_rate"
+						if self.get("is_internal_supplier") and not self.doctype == "Purchase Order"
+						else "rate"
+					)
 					rate = flt(
 						frappe.db.get_value(ref_doctype, d.get(frappe.scrub(ref_doctype)), field)
 						* (d.conversion_factor or 1),
diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py
index c8785a5..ea7fb23 100644
--- a/erpnext/controllers/item_variant.py
+++ b/erpnext/controllers/item_variant.py
@@ -56,10 +56,24 @@
 
 	copy_attributes_to_variant(template, variant)
 
-	variant.manufacturer = manufacturer
-	variant.manufacturer_part_no = manufacturer_part_no
-
 	variant.item_code = append_number_if_name_exists("Item", template.name)
+	variant.flags.ignore_mandatory = True
+	variant.save()
+
+	if not frappe.db.exists(
+		"Item Manufacturer", {"item_code": variant.name, "manufacturer": manufacturer}
+	):
+		manufacturer_doc = frappe.new_doc("Item Manufacturer")
+		manufacturer_doc.update(
+			{
+				"item_code": variant.name,
+				"manufacturer": manufacturer,
+				"manufacturer_part_no": manufacturer_part_no,
+			}
+		)
+
+		manufacturer_doc.flags.ignore_mandatory = True
+		manufacturer_doc.save(ignore_permissions=True)
 
 	return variant
 
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index 63dca63..2650753 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -896,3 +896,31 @@
 			as_list=1,
 		)
 	return terms
+
+
+@frappe.whitelist()
+@frappe.validate_and_sanitize_search_inputs
+def get_filtered_child_rows(doctype, txt, searchfield, start, page_len, filters) -> list:
+	table = frappe.qb.DocType(doctype)
+	query = (
+		frappe.qb.from_(table)
+		.select(
+			table.name,
+			Concat("#", table.idx, ", ", table.item_code),
+		)
+		.orderby(table.idx)
+		.offset(start)
+		.limit(page_len)
+	)
+
+	if filters:
+		for field, value in filters.items():
+			query = query.where(table[field] == value)
+
+	if txt:
+		txt += "%"
+		query = query.where(
+			((table.idx.like(txt.replace("#", ""))) | (table.item_code.like(txt))) | (table.name.like(txt))
+		)
+
+	return query.run(as_dict=False)
diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py
index 81e71e3..81080f0 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.py
@@ -8,6 +8,8 @@
 from frappe.utils import flt, format_datetime, get_datetime
 
 import erpnext
+from erpnext.stock.serial_batch_bundle import get_batches_from_bundle
+from erpnext.stock.serial_batch_bundle import get_serial_nos as get_serial_nos_from_bundle
 from erpnext.stock.utils import get_incoming_rate
 
 
@@ -69,8 +71,6 @@
 
 
 def validate_returned_items(doc):
-	from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
-
 	valid_items = frappe._dict()
 
 	select_fields = "item_code, qty, stock_qty, rate, parenttype, conversion_factor"
@@ -123,26 +123,6 @@
 						)
 					)
 
-				elif ref.batch_no and d.batch_no not in ref.batch_no:
-					frappe.throw(
-						_("Row # {0}: Batch No must be same as {1} {2}").format(
-							d.idx, doc.doctype, doc.return_against
-						)
-					)
-
-				elif ref.serial_no:
-					if d.qty and not d.serial_no:
-						frappe.throw(_("Row # {0}: Serial No is mandatory").format(d.idx))
-					else:
-						serial_nos = get_serial_nos(d.serial_no)
-						for s in serial_nos:
-							if s not in ref.serial_no:
-								frappe.throw(
-									_("Row # {0}: Serial No {1} does not match with {2} {3}").format(
-										d.idx, s, doc.doctype, doc.return_against
-									)
-								)
-
 				if (
 					warehouse_mandatory
 					and not d.get("warehouse")
@@ -397,71 +377,92 @@
 		else:
 			doc.run_method("calculate_taxes_and_totals")
 
-	def update_item(source_doc, target_doc, source_parent):
+	def update_serial_batch_no(source_doc, target_doc, source_parent, item_details, qty_field):
+		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 		from erpnext.stock.serial_batch_bundle import SerialBatchCreation
 
-		target_doc.qty = -1 * source_doc.qty
-		item_details = frappe.get_cached_value(
-			"Item", source_doc.item_code, ["has_batch_no", "has_serial_no"], as_dict=1
-		)
-
 		returned_serial_nos = []
-		if source_doc.get("serial_and_batch_bundle"):
-			if item_details.has_serial_no:
-				returned_serial_nos = get_returned_serial_nos(source_doc, source_parent)
+		returned_batches = frappe._dict()
+		serial_and_batch_field = (
+			"serial_and_batch_bundle" if qty_field == "stock_qty" else "rejected_serial_and_batch_bundle"
+		)
+		old_serial_no_field = "serial_no" if qty_field == "stock_qty" else "rejected_serial_no"
+		old_batch_no_field = "batch_no"
 
-			type_of_transaction = "Inward"
-			if (
-				frappe.db.get_value(
-					"Serial and Batch Bundle", source_doc.serial_and_batch_bundle, "type_of_transaction"
-				)
-				== "Inward"
-			):
-				type_of_transaction = "Outward"
-
-			cls_obj = SerialBatchCreation(
-				{
-					"type_of_transaction": type_of_transaction,
-					"serial_and_batch_bundle": source_doc.serial_and_batch_bundle,
-					"returned_against": source_doc.name,
-					"item_code": source_doc.item_code,
-					"returned_serial_nos": returned_serial_nos,
-				}
-			)
-
-			cls_obj.duplicate_package()
-			if cls_obj.serial_and_batch_bundle:
-				target_doc.serial_and_batch_bundle = cls_obj.serial_and_batch_bundle
-
-		if source_doc.get("rejected_serial_and_batch_bundle"):
+		if (
+			source_doc.get(serial_and_batch_field)
+			or source_doc.get(old_serial_no_field)
+			or source_doc.get(old_batch_no_field)
+		):
 			if item_details.has_serial_no:
 				returned_serial_nos = get_returned_serial_nos(
-					source_doc, source_parent, serial_no_field="rejected_serial_and_batch_bundle"
+					source_doc, source_parent, serial_no_field=serial_and_batch_field
+				)
+			else:
+				returned_batches = get_returned_batches(
+					source_doc, source_parent, batch_no_field=serial_and_batch_field
 				)
 
 			type_of_transaction = "Inward"
-			if (
+			if source_doc.get(serial_and_batch_field) and (
 				frappe.db.get_value(
-					"Serial and Batch Bundle", source_doc.rejected_serial_and_batch_bundle, "type_of_transaction"
+					"Serial and Batch Bundle", source_doc.get(serial_and_batch_field), "type_of_transaction"
 				)
 				== "Inward"
 			):
 				type_of_transaction = "Outward"
+			elif source_parent.doctype in [
+				"Purchase Invoice",
+				"Purchase Receipt",
+				"Subcontracting Receipt",
+			]:
+				type_of_transaction = "Outward"
 
 			cls_obj = SerialBatchCreation(
 				{
 					"type_of_transaction": type_of_transaction,
-					"serial_and_batch_bundle": source_doc.rejected_serial_and_batch_bundle,
+					"serial_and_batch_bundle": source_doc.get(serial_and_batch_field),
 					"returned_against": source_doc.name,
 					"item_code": source_doc.item_code,
 					"returned_serial_nos": returned_serial_nos,
+					"voucher_type": source_parent.doctype,
+					"do_not_submit": True,
+					"warehouse": source_doc.warehouse,
+					"has_serial_no": item_details.has_serial_no,
+					"has_batch_no": item_details.has_batch_no,
 				}
 			)
 
-			cls_obj.duplicate_package()
-			if cls_obj.serial_and_batch_bundle:
-				target_doc.serial_and_batch_bundle = cls_obj.serial_and_batch_bundle
+			serial_nos = []
+			batches = frappe._dict()
+			if source_doc.get(old_batch_no_field):
+				batches = frappe._dict({source_doc.batch_no: source_doc.get(qty_field)})
+			elif source_doc.get(old_serial_no_field):
+				serial_nos = get_serial_nos(source_doc.get(old_serial_no_field))
+			elif source_doc.get(serial_and_batch_field):
+				if item_details.has_serial_no:
+					serial_nos = get_serial_nos_from_bundle(source_doc.get(serial_and_batch_field))
+				else:
+					batches = get_batches_from_bundle(source_doc.get(serial_and_batch_field))
 
+			if serial_nos:
+				cls_obj.serial_nos = sorted(list(set(serial_nos) - set(returned_serial_nos)))
+			elif batches:
+				for batch in batches:
+					if batch in returned_batches:
+						batches[batch] -= flt(returned_batches.get(batch))
+
+				cls_obj.batches = batches
+
+			if source_doc.get(serial_and_batch_field):
+				cls_obj.duplicate_package()
+				if cls_obj.serial_and_batch_bundle:
+					target_doc.set(serial_and_batch_field, cls_obj.serial_and_batch_bundle)
+			else:
+				target_doc.set(serial_and_batch_field, cls_obj.make_serial_and_batch_bundle().name)
+
+	def update_item(source_doc, target_doc, source_parent):
+		target_doc.qty = -1 * source_doc.qty
 		if doctype in ["Purchase Receipt", "Subcontracting Receipt"]:
 			returned_qty_map = get_returned_qty_map_for_row(
 				source_parent.name, source_parent.supplier, source_doc.name, doctype
@@ -561,6 +562,17 @@
 			if default_warehouse_for_sales_return:
 				target_doc.warehouse = default_warehouse_for_sales_return
 
+		item_details = frappe.get_cached_value(
+			"Item", source_doc.item_code, ["has_batch_no", "has_serial_no"], as_dict=1
+		)
+
+		if not item_details.has_batch_no and not item_details.has_serial_no:
+			return
+
+		for qty_field in ["stock_qty", "rejected_qty"]:
+			if target_doc.get(qty_field):
+				update_serial_batch_no(source_doc, target_doc, source_parent, item_details, qty_field)
+
 	def update_terms(source_doc, target_doc, source_parent):
 		target_doc.payment_amount = -source_doc.payment_amount
 
@@ -716,6 +728,9 @@
 		[parent_doc.doctype, "docstatus", "=", 1],
 	]
 
+	if serial_no_field == "rejected_serial_and_batch_bundle":
+		filters.append([child_doc.doctype, "rejected_qty", ">", 0])
+
 	# Required for POS Invoice
 	if ignore_voucher_detail_no:
 		filters.append([child_doc.doctype, "name", "!=", ignore_voucher_detail_no])
@@ -723,9 +738,57 @@
 	ids = []
 	for row in frappe.get_all(parent_doc.doctype, fields=fields, filters=filters):
 		ids.append(row.get("serial_and_batch_bundle"))
-		if row.get(old_field):
+		if row.get(old_field) and not row.get(serial_no_field):
 			serial_nos.extend(get_serial_nos_from_serial_no(row.get(old_field)))
 
-	serial_nos.extend(get_serial_nos(ids))
+	if ids:
+		serial_nos.extend(get_serial_nos(ids))
 
 	return serial_nos
+
+
+def get_returned_batches(
+	child_doc, parent_doc, batch_no_field=None, ignore_voucher_detail_no=None
+):
+	from erpnext.stock.serial_batch_bundle import get_batches_from_bundle
+
+	batches = frappe._dict()
+
+	old_field = "batch_no"
+	if not batch_no_field:
+		batch_no_field = "serial_and_batch_bundle"
+
+	return_ref_field = frappe.scrub(child_doc.doctype)
+	if child_doc.doctype == "Delivery Note Item":
+		return_ref_field = "dn_detail"
+
+	fields = [
+		f"`{'tab' + child_doc.doctype}`.`{batch_no_field}`",
+		f"`{'tab' + child_doc.doctype}`.`batch_no`",
+		f"`{'tab' + child_doc.doctype}`.`stock_qty`",
+	]
+
+	filters = [
+		[parent_doc.doctype, "return_against", "=", parent_doc.name],
+		[parent_doc.doctype, "is_return", "=", 1],
+		[child_doc.doctype, return_ref_field, "=", child_doc.name],
+		[parent_doc.doctype, "docstatus", "=", 1],
+	]
+
+	if batch_no_field == "rejected_serial_and_batch_bundle":
+		filters.append([child_doc.doctype, "rejected_qty", ">", 0])
+
+	# Required for POS Invoice
+	if ignore_voucher_detail_no:
+		filters.append([child_doc.doctype, "name", "!=", ignore_voucher_detail_no])
+
+	ids = []
+	for row in frappe.get_all(parent_doc.doctype, fields=fields, filters=filters):
+		ids.append(row.get("serial_and_batch_bundle"))
+		if row.get(old_field) and not row.get(batch_no_field):
+			batches.setdefault(row.get(old_field), row.get("stock_qty"))
+
+	if ids:
+		batches.update(get_batches_from_bundle(ids))
+
+	return batches
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index fdadb30..919e459 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -12,7 +12,7 @@
 from erpnext.controllers.stock_controller import StockController
 from erpnext.stock.doctype.item.item import set_item_default
 from erpnext.stock.get_item_details import get_bin_details, get_conversion_factor
-from erpnext.stock.utils import get_incoming_rate
+from erpnext.stock.utils import get_incoming_rate, get_valuation_method
 
 
 class SellingController(StockController):
@@ -308,6 +308,8 @@
 									"warehouse": p.warehouse or d.warehouse,
 									"item_code": p.item_code,
 									"qty": flt(p.qty),
+									"serial_no": p.serial_no if self.docstatus == 2 else None,
+									"batch_no": p.batch_no if self.docstatus == 2 else None,
 									"uom": p.uom,
 									"serial_and_batch_bundle": p.serial_and_batch_bundle
 									or get_serial_and_batch_bundle(p, self),
@@ -330,6 +332,8 @@
 							"warehouse": d.warehouse,
 							"item_code": d.item_code,
 							"qty": d.stock_qty,
+							"serial_no": d.serial_no if self.docstatus == 2 else None,
+							"batch_no": d.batch_no if self.docstatus == 2 else None,
 							"uom": d.uom,
 							"stock_uom": d.stock_uom,
 							"conversion_factor": d.conversion_factor,
@@ -428,11 +432,15 @@
 
 		items = self.get("items") + (self.get("packed_items") or [])
 		for d in items:
-			if not self.get("return_against"):
+			if not self.get("return_against") or (
+				get_valuation_method(d.item_code) == "Moving Average" and self.get("is_return")
+			):
 				# Get incoming rate based on original item cost based on valuation method
 				qty = flt(d.get("stock_qty") or d.get("actual_qty"))
 
-				if not (self.get("is_return") and d.incoming_rate):
+				if not d.incoming_rate or (
+					get_valuation_method(d.item_code) == "Moving Average" and self.get("is_return")
+				):
 					d.incoming_rate = get_incoming_rate(
 						{
 							"item_code": d.item_code,
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 2fda9cc..671d2fb 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -162,7 +162,9 @@
 							self.get_gl_dict(
 								{
 									"account": warehouse_account[sle.warehouse]["account"],
+									"against_type": "Account",
 									"against": expense_account,
+									"against_link": expense_account,
 									"cost_center": item_row.cost_center,
 									"project": item_row.project or self.get("project"),
 									"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
@@ -178,7 +180,9 @@
 							self.get_gl_dict(
 								{
 									"account": expense_account,
+									"against_type": "Account",
 									"against": warehouse_account[sle.warehouse]["account"],
+									"against_link": warehouse_account[sle.warehouse]["account"],
 									"cost_center": item_row.cost_center,
 									"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
 									"debit": -1 * flt(sle.stock_value_difference, precision),
@@ -210,7 +214,9 @@
 					self.get_gl_dict(
 						{
 							"account": expense_account,
+							"against_type": "Account",
 							"against": warehouse_asset_account,
+							"against_link": warehouse_asset_account,
 							"cost_center": item_row.cost_center,
 							"project": item_row.project or self.get("project"),
 							"remarks": _("Rounding gain/loss Entry for Stock Transfer"),
@@ -226,7 +232,9 @@
 					self.get_gl_dict(
 						{
 							"account": warehouse_asset_account,
+							"against_type": "Account",
 							"against": expense_account,
+							"against_link": expense_account,
 							"cost_center": item_row.cost_center,
 							"remarks": _("Rounding gain/loss Entry for Stock Transfer"),
 							"credit": sle_rounding_diff,
@@ -455,6 +463,12 @@
 		sl_dict.update(args)
 		self.update_inventory_dimensions(d, sl_dict)
 
+		if self.docstatus == 2:
+			# To handle denormalized serial no records, will br deprecated in v16
+			for field in ["serial_no", "batch_no"]:
+				if d.get(field):
+					sl_dict[field] = d.get(field)
+
 		return sl_dict
 
 	def update_inventory_dimensions(self, row, sl_dict) -> None:
@@ -826,6 +840,7 @@
 		credit,
 		remarks,
 		against_account,
+		against_type="Account",
 		debit_in_account_currency=None,
 		credit_in_account_currency=None,
 		account_currency=None,
@@ -840,7 +855,9 @@
 			"cost_center": cost_center,
 			"debit": debit,
 			"credit": credit,
+			"against_type": against_type,
 			"against": against_account,
+			"against_link": against_account,
 			"remarks": remarks,
 		}
 
diff --git a/erpnext/manufacturing/doctype/bom/bom.json b/erpnext/manufacturing/doctype/bom/bom.json
index e8d3542..5083873 100644
--- a/erpnext/manufacturing/doctype/bom/bom.json
+++ b/erpnext/manufacturing/doctype/bom/bom.json
@@ -218,6 +218,7 @@
    "options": "\nWork Order\nJob Card"
   },
   {
+   "default": "1",
    "fieldname": "conversion_rate",
    "fieldtype": "Float",
    "label": "Conversion Rate",
@@ -636,7 +637,7 @@
  "image_field": "image",
  "is_submittable": 1,
  "links": [],
- "modified": "2023-08-07 11:38:08.152294",
+ "modified": "2023-12-26 19:34:08.159312",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "BOM",
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index f0381d2..d86b6d4 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -744,6 +744,9 @@
 		base_total_rm_cost = 0
 
 		for d in self.get("items"):
+			if not d.is_stock_item and self.rm_cost_as_per == "Valuation Rate":
+				continue
+
 			old_rate = d.rate
 			if self.rm_cost_as_per != "Manual":
 				d.rate = self.get_rm_rate(
@@ -1017,6 +1020,8 @@
 		item_doc = frappe.get_cached_doc("Item", args.get("item_code"))
 		price_list_data = get_price_list_rate(bom_args, item_doc)
 		rate = price_list_data.price_list_rate
+	elif bom_doc.rm_cost_as_per == "Manual":
+		return
 
 	return flt(rate)
 
diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py
index 051b475..2debf91 100644
--- a/erpnext/manufacturing/doctype/bom/test_bom.py
+++ b/erpnext/manufacturing/doctype/bom/test_bom.py
@@ -698,6 +698,35 @@
 		bom.update_cost()
 		self.assertFalse(bom.flags.cost_updated)
 
+	def test_bom_with_service_item_cost(self):
+		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
+
+		rm_item = make_item(properties={"is_stock_item": 1, "valuation_rate": 1000.0}).name
+
+		service_item = make_item(properties={"is_stock_item": 0}).name
+
+		fg_item = make_item(properties={"is_stock_item": 1}).name
+
+		from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
+
+		bom = make_bom(item=fg_item, raw_materials=[rm_item, service_item], do_not_save=True)
+		bom.rm_cost_as_per = "Valuation Rate"
+
+		for row in bom.items:
+			if row.item_code == service_item:
+				row.rate = 566.00
+			else:
+				row.rate = 800.00
+
+		bom.save()
+
+		for row in bom.items:
+			if row.item_code == service_item:
+				self.assertEqual(row.is_stock_item, 0)
+				self.assertEqual(row.rate, 566.00)
+			else:
+				self.assertEqual(row.is_stock_item, 1)
+
 	def test_do_not_include_manufacturing_and_fixed_items(self):
 		from erpnext.manufacturing.doctype.bom.bom import item_query
 
diff --git a/erpnext/manufacturing/doctype/bom_item/bom_item.json b/erpnext/manufacturing/doctype/bom_item/bom_item.json
index cb58af1..dfd6612 100644
--- a/erpnext/manufacturing/doctype/bom_item/bom_item.json
+++ b/erpnext/manufacturing/doctype/bom_item/bom_item.json
@@ -14,6 +14,7 @@
   "bom_no",
   "source_warehouse",
   "allow_alternative_item",
+  "is_stock_item",
   "section_break_5",
   "description",
   "col_break1",
@@ -185,7 +186,7 @@
    "in_list_view": 1,
    "label": "Rate",
    "options": "currency",
-   "read_only": 1,
+   "read_only_depends_on": "eval:doc.is_stock_item == 1",
    "reqd": 1
   },
   {
@@ -284,13 +285,21 @@
    "fieldname": "do_not_explode",
    "fieldtype": "Check",
    "label": "Do Not Explode"
+  },
+  {
+   "default": "0",
+   "fetch_from": "item_code.is_stock_item",
+   "fieldname": "is_stock_item",
+   "fieldtype": "Check",
+   "label": "Is Stock Item",
+   "read_only": 1
   }
  ],
  "idx": 1,
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-11-14 18:35:51.378513",
+ "modified": "2023-12-20 16:21:55.477883",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "BOM Item",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js
index dd102b0..cd92263 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.js
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js
@@ -305,6 +305,8 @@
 			frappe.throw(__("Select the Warehouse"));
 		}
 
+		frm.set_value("consider_minimum_order_qty", 0);
+
 		if (frm.doc.ignore_existing_ordered_qty) {
 			frm.events.get_items_for_material_requests(frm);
 		} else {
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json
index 49386c4..257b60c 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.json
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json
@@ -48,6 +48,7 @@
   "material_request_planning",
   "include_non_stock_items",
   "include_subcontracted_items",
+  "consider_minimum_order_qty",
   "include_safety_stock",
   "ignore_existing_ordered_qty",
   "column_break_25",
@@ -423,13 +424,19 @@
    "fieldtype": "Link",
    "label": "Sub Assembly Warehouse",
    "options": "Warehouse"
+  },
+  {
+   "default": "0",
+   "fieldname": "consider_minimum_order_qty",
+   "fieldtype": "Check",
+   "label": "Consider Minimum Order Qty"
   }
  ],
  "icon": "fa fa-calendar",
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2023-11-03 14:08:11.928027",
+ "modified": "2023-12-26 16:31:13.740777",
  "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 c201c4f..2bfd4be 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -67,6 +67,7 @@
 		combine_items: DF.Check
 		combine_sub_items: DF.Check
 		company: DF.Link
+		consider_minimum_order_qty: DF.Check
 		customer: DF.Link | None
 		for_warehouse: DF.Link | None
 		from_date: DF.Date | None
@@ -583,6 +584,7 @@
 
 		if close:
 			self.db_set("status", "Closed")
+			self.update_bin_qty()
 			return
 
 		if self.total_produced_qty > 0:
@@ -597,6 +599,9 @@
 		if close is not None:
 			self.db_set("status", self.status)
 
+		if self.docstatus == 1 and self.status != "Completed":
+			self.update_bin_qty()
+
 	def update_ordered_status(self):
 		update_status = False
 		for d in self.po_items:
@@ -1207,7 +1212,14 @@
 
 
 def get_material_request_items(
-	row, sales_order, company, ignore_existing_ordered_qty, include_safety_stock, warehouse, bin_dict
+	doc,
+	row,
+	sales_order,
+	company,
+	ignore_existing_ordered_qty,
+	include_safety_stock,
+	warehouse,
+	bin_dict,
 ):
 	total_qty = row["qty"]
 
@@ -1216,8 +1228,14 @@
 		required_qty = total_qty
 	elif total_qty > bin_dict.get("projected_qty", 0):
 		required_qty = total_qty - bin_dict.get("projected_qty", 0)
-	if required_qty > 0 and required_qty < row["min_order_qty"]:
+
+	if (
+		doc.get("consider_minimum_order_qty")
+		and required_qty > 0
+		and required_qty < row["min_order_qty"]
+	):
 		required_qty = row["min_order_qty"]
+
 	item_group_defaults = get_item_group_defaults(row.item_code, company)
 
 	if not row["purchase_uom"]:
@@ -1555,6 +1573,7 @@
 
 			if details.qty > 0:
 				items = get_material_request_items(
+					doc,
 					details,
 					sales_order,
 					company,
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index cc9d9a0..cb99b88 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -1458,6 +1458,70 @@
 			self.assertEqual(row.get("uom"), "Nos")
 			self.assertEqual(row.get("conversion_factor"), 10.0)
 
+	def test_unreserve_qty_on_closing_of_pp(self):
+		from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
+		from erpnext.stock.utils import get_or_make_bin
+
+		fg_item = make_item(properties={"is_stock_item": 1, "stock_uom": "_Test UOM 1"}).name
+		rm_item = make_item(properties={"is_stock_item": 1, "stock_uom": "_Test UOM 1"}).name
+
+		store_warehouse = create_warehouse("Store Warehouse", company="_Test Company")
+		rm_warehouse = create_warehouse("RM Warehouse", company="_Test Company")
+
+		make_bom(item=fg_item, raw_materials=[rm_item], source_warehouse="_Test Warehouse - _TC")
+
+		pln = create_production_plan(
+			item_code=fg_item, planned_qty=10, stock_uom="_Test UOM 1", do_not_submit=1
+		)
+
+		pln.for_warehouse = rm_warehouse
+		mr_items = get_items_for_material_requests(pln.as_dict())
+		for d in mr_items:
+			pln.append("mr_items", d)
+
+		pln.save()
+		pln.submit()
+
+		bin_name = get_or_make_bin(rm_item, rm_warehouse)
+		before_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan"))
+
+		pln.reload()
+		pln.set_status(close=True)
+
+		bin_name = get_or_make_bin(rm_item, rm_warehouse)
+		after_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan"))
+		self.assertAlmostEqual(after_qty, before_qty - 10)
+
+		pln.reload()
+		pln.set_status(close=False)
+
+		bin_name = get_or_make_bin(rm_item, rm_warehouse)
+		after_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan"))
+		self.assertAlmostEqual(after_qty, before_qty)
+
+	def test_min_order_qty_in_pp(self):
+		from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
+		from erpnext.stock.utils import get_or_make_bin
+
+		fg_item = make_item(properties={"is_stock_item": 1}).name
+		rm_item = make_item(properties={"is_stock_item": 1, "min_order_qty": 1000}).name
+
+		rm_warehouse = create_warehouse("RM Warehouse", company="_Test Company")
+
+		make_bom(item=fg_item, raw_materials=[rm_item], source_warehouse="_Test Warehouse - _TC")
+
+		pln = create_production_plan(item_code=fg_item, planned_qty=10, do_not_submit=1)
+
+		pln.for_warehouse = rm_warehouse
+		mr_items = get_items_for_material_requests(pln.as_dict())
+		for d in mr_items:
+			self.assertEqual(d.get("quantity"), 10.0)
+
+		pln.consider_minimum_order_qty = 1
+		mr_items = get_items_for_material_requests(pln.as_dict())
+		for d in mr_items:
+			self.assertEqual(d.get("quantity"), 1000.0)
+
 
 def create_production_plan(**args):
 	"""
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 9b070bd..7ade21d 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -352,5 +352,8 @@
 erpnext.patches.v14_0.update_zero_asset_quantity_field
 execute:frappe.db.set_single_value("Buying Settings", "project_update_frequency", "Each Transaction")
 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
 # 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
+erpnext.patches.v14_0.set_maintain_stock_for_bom_item
diff --git a/erpnext/patches/v14_0/set_maintain_stock_for_bom_item.py b/erpnext/patches/v14_0/set_maintain_stock_for_bom_item.py
new file mode 100644
index 0000000..f0b618f
--- /dev/null
+++ b/erpnext/patches/v14_0/set_maintain_stock_for_bom_item.py
@@ -0,0 +1,19 @@
+import frappe
+
+
+def execute():
+	if not frappe.db.exists("BOM", {"docstatus": 1}):
+		return
+
+	# Added is_stock_item to handle Read Only based on condition for the rate field
+	frappe.db.sql(
+		"""
+		UPDATE
+			`tabBOM Item` boi,
+			`tabItem` i
+		SET
+			boi.is_stock_item = i.is_stock_item
+		WHERE
+			boi.item_code = i.name
+	"""
+	)
diff --git a/erpnext/patches/v14_0/update_total_asset_cost_field.py b/erpnext/patches/v14_0/update_total_asset_cost_field.py
new file mode 100644
index 0000000..57cf71b
--- /dev/null
+++ b/erpnext/patches/v14_0/update_total_asset_cost_field.py
@@ -0,0 +1,17 @@
+import frappe
+
+
+def execute():
+	asset = frappe.qb.DocType("Asset")
+	frappe.qb.update(asset).set(asset.total_asset_cost, asset.gross_purchase_amount).run()
+
+	asset_repair_list = frappe.db.get_all(
+		"Asset Repair",
+		filters={"docstatus": 1, "repair_status": "Completed", "capitalize_repair_cost": 1},
+		fields=["asset", "repair_cost"],
+	)
+
+	for asset_repair in asset_repair_list:
+		frappe.qb.update(asset).set(
+			asset.total_asset_cost, asset.total_asset_cost + asset_repair.repair_cost
+		).where(asset.name == asset_repair.asset).run()
diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js
index 3ed7fc7..77ecf75 100644
--- a/erpnext/public/js/controllers/buying.js
+++ b/erpnext/public/js/controllers/buying.js
@@ -361,9 +361,14 @@
 								new erpnext.SerialBatchPackageSelector(
 									me.frm, item, (r) => {
 										if (r) {
+											let qty = Math.abs(r.total_qty);
+											if (doc.is_return) {
+												qty = qty * -1;
+											}
+
 											let update_values = {
 												"serial_and_batch_bundle": r.name,
-												"qty": Math.abs(r.total_qty)
+												"qty": qty
 											}
 
 											if (r.warehouse) {
@@ -396,9 +401,14 @@
 								new erpnext.SerialBatchPackageSelector(
 									me.frm, item, (r) => {
 										if (r) {
+											let qty = Math.abs(r.total_qty);
+											if (doc.is_return) {
+												qty = qty * -1;
+											}
+
 											let update_values = {
 												"serial_and_batch_bundle": r.name,
-												"rejected_qty": Math.abs(r.total_qty)
+												"rejected_qty": qty
 											}
 
 											if (r.warehouse) {
diff --git a/erpnext/public/js/utils/sales_common.js b/erpnext/public/js/utils/sales_common.js
index 5514963..b92b02e 100644
--- a/erpnext/public/js/utils/sales_common.js
+++ b/erpnext/public/js/utils/sales_common.js
@@ -184,6 +184,12 @@
 				refresh_field("incentives",row.name,row.parentfield);
 			}
 
+			warehouse(doc, cdt, cdn) {
+				if (doc.docstatus === 0 && doc.is_return && !doc.return_against) {
+					frappe.model.set_value(cdt, cdn, "incoming_rate", 0.0);
+				}
+			}
+
 			toggle_editable_price_list_rate() {
 				var df = frappe.meta.get_docfield(this.frm.doc.doctype + " Item", "price_list_rate", this.frm.doc.name);
 				var editable_price_list_rate = cint(frappe.defaults.get_default("editable_price_list_rate"));
@@ -317,9 +323,14 @@
 								new erpnext.SerialBatchPackageSelector(
 									me.frm, item, (r) => {
 										if (r) {
+											let qty = Math.abs(r.total_qty);
+											if (doc.is_return) {
+												qty = qty * -1;
+											}
+
 											frappe.model.set_value(item.doctype, item.name, {
 												"serial_and_batch_bundle": r.name,
-												"qty": Math.abs(r.total_qty)
+												"qty": qty
 											});
 										}
 									}
diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js
index 7b9cdfe..4abc8fa 100644
--- a/erpnext/public/js/utils/serial_no_batch_selector.js
+++ b/erpnext/public/js/utils/serial_no_batch_selector.js
@@ -32,22 +32,39 @@
 		});
 
 		this.dialog.show();
-
-		let qty = this.item.stock_qty || this.item.transfer_qty || this.item.qty;
-		this.dialog.set_value("qty", qty).then(() => {
-			if (this.item.serial_no) {
-				this.dialog.set_value("scan_serial_no", this.item.serial_no);
-				frappe.model.set_value(this.item.doctype, this.item.name, 'serial_no', '');
-			} else if (this.item.batch_no) {
-				this.dialog.set_value("scan_batch_no", this.item.batch_no);
-				frappe.model.set_value(this.item.doctype, this.item.name, 'batch_no', '');
-			}
-
-			this.dialog.fields_dict.entries.grid.refresh();
-		});
-
 		this.$scan_btn = this.dialog.$wrapper.find(".link-btn");
 		this.$scan_btn.css("display", "inline");
+
+		let qty = this.item.stock_qty || this.item.transfer_qty || this.item.qty;
+
+		if (this.item?.is_rejected) {
+			qty = this.item.rejected_qty;
+		}
+
+		qty = Math.abs(qty);
+		if (qty > 0) {
+			this.dialog.set_value("qty", qty).then(() => {
+				if (this.item.serial_no && !this.item.serial_and_batch_bundle) {
+					let serial_nos = this.item.serial_no.split('\n');
+					if (serial_nos.length > 1) {
+						serial_nos.forEach(serial_no => {
+							this.dialog.fields_dict.entries.df.data.push({
+								serial_no: serial_no,
+								batch_no: this.item.batch_no
+							});
+						});
+					} else {
+						this.dialog.set_value("scan_serial_no", this.item.serial_no);
+					}
+					frappe.model.set_value(this.item.doctype, this.item.name, 'serial_no', '');
+				} else if (this.item.batch_no && !this.item.serial_and_batch_bundle) {
+					this.dialog.set_value("scan_batch_no", this.item.batch_no);
+					frappe.model.set_value(this.item.doctype, this.item.name, 'batch_no', '');
+				}
+
+				this.dialog.fields_dict.entries.grid.refresh();
+			});
+		}
 	}
 
 	get_serial_no_filters() {
@@ -467,13 +484,13 @@
 	}
 
 	render_data() {
-		if (!this.frm.is_new() && this.bundle) {
+		if (this.bundle) {
 			frappe.call({
 				method: 'erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle.get_serial_batch_ledgers',
 				args: {
 					item_code: this.item.item_code,
 					name: this.bundle,
-					voucher_no: this.item.parent,
+					voucher_no: !this.frm.is_new() ? this.item.parent : "",
 				}
 			}).then(r => {
 				if (r.message) {
diff --git a/erpnext/regional/united_arab_emirates/utils.py b/erpnext/regional/united_arab_emirates/utils.py
index efeaeed..250a4b1 100644
--- a/erpnext/regional/united_arab_emirates/utils.py
+++ b/erpnext/regional/united_arab_emirates/utils.py
@@ -153,7 +153,9 @@
 					"account": tax.account_head,
 					"cost_center": tax.cost_center,
 					"posting_date": doc.posting_date,
+					"against_type": "Supplier",
 					"against": doc.supplier,
+					"against_link": doc.supplier,
 					dr_or_cr: tax.base_tax_amount_after_discount_amount,
 					dr_or_cr + "_in_account_currency": tax.base_tax_amount_after_discount_amount
 					if account_currency == doc.company_currency
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index 97b214e..b206e3f 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -182,7 +182,7 @@
 	create_stock_reservation_entries(frm) {
 		const dialog = new frappe.ui.Dialog({
 			title: __("Stock Reservation"),
-			size: "large",
+			size: "extra-large",
 			fields: [
 				{
 					fieldname: "set_warehouse",
@@ -207,6 +207,50 @@
 					},
 				},
 				{fieldtype: "Column Break"},
+				{
+					fieldname: "add_item",
+					fieldtype: "Link",
+					label: __("Add Item"),
+					options: "Sales Order Item",
+					get_query: () => {
+						return {
+							query: "erpnext.controllers.queries.get_filtered_child_rows",
+							filters: {
+								"parenttype": frm.doc.doctype,
+								"parent": frm.doc.name,
+								"reserve_stock": 1,
+							}
+						}
+					},
+					onchange: () => {
+						let sales_order_item = dialog.get_value("add_item");
+
+						if (sales_order_item) {
+							frm.doc.items.forEach(item => {
+								if (item.name === sales_order_item) {
+									let unreserved_qty = (flt(item.stock_qty) - (item.stock_reserved_qty ? flt(item.stock_reserved_qty) : (flt(item.delivered_qty) * flt(item.conversion_factor)))) / flt(item.conversion_factor);
+
+									if (unreserved_qty > 0) {
+										dialog.fields_dict.items.df.data.forEach((row) => {
+											if (row.sales_order_item === sales_order_item) {
+												unreserved_qty -= row.qty_to_reserve;
+											}
+										});
+									}
+
+									dialog.fields_dict.items.df.data.push({
+										'sales_order_item': item.name,
+										'item_code': item.item_code,
+										'warehouse': dialog.get_value("set_warehouse") || item.warehouse,
+										'qty_to_reserve': Math.max(unreserved_qty, 0)
+									});
+									dialog.fields_dict.items.grid.refresh();
+									dialog.set_value("add_item", undefined);
+								}
+							});
+						}
+					},
+				},
 				{fieldtype: "Section Break"},
 				{
 					fieldname: "items",
@@ -218,10 +262,34 @@
 					fields: [
 						{
 							fieldname: "sales_order_item",
-							fieldtype: "Data",
+							fieldtype: "Link",
 							label: __("Sales Order Item"),
+							options: "Sales Order Item",
 							reqd: 1,
-							read_only: 1,
+							in_list_view: 1,
+							get_query: () => {
+								return {
+									query: "erpnext.controllers.queries.get_filtered_child_rows",
+									filters: {
+										"parenttype": frm.doc.doctype,
+										"parent": frm.doc.name,
+										"reserve_stock": 1,
+									}
+								}
+							},
+							onchange: (event) => {
+								if (event) {
+									let name = $(event.currentTarget).closest(".grid-row").attr("data-name");
+									let item_row = dialog.fields_dict.items.grid.grid_rows_by_docname[name].doc;
+
+									frm.doc.items.forEach(item => {
+										if (item.name === item_row.sales_order_item) {
+											item_row.item_code = item.item_code;
+										}
+									});
+									dialog.fields_dict.items.grid.refresh();
+								}
+							}
 						},
 						{
 							fieldname: "item_code",
@@ -284,14 +352,14 @@
 
 		frm.doc.items.forEach(item => {
 			if (item.reserve_stock) {
-				let unreserved_qty = (flt(item.stock_qty) - (item.stock_reserved_qty ? flt(item.stock_reserved_qty) : (flt(item.delivered_qty) * flt(item.conversion_factor))))
+				let unreserved_qty = (flt(item.stock_qty) - (item.stock_reserved_qty ? flt(item.stock_reserved_qty) : (flt(item.delivered_qty) * flt(item.conversion_factor)))) / flt(item.conversion_factor);
 
 				if (unreserved_qty > 0) {
 					dialog.fields_dict.items.df.data.push({
 						'sales_order_item': item.name,
 						'item_code': item.item_code,
 						'warehouse': item.warehouse,
-						'qty_to_reserve': (unreserved_qty / flt(item.conversion_factor))
+						'qty_to_reserve': unreserved_qty
 					});
 				}
 			}
diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js
index 193048f..bd85792 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_cart.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js
@@ -520,7 +520,7 @@
 	}
 
 	render_taxes(taxes) {
-		if (taxes.length) {
+		if (taxes && taxes.length) {
 			const currency = this.events.get_frm().doc.currency;
 			const taxes_html = taxes.map(t => {
 				if (t.tax_amount_after_discount_amount == 0.0) return;
diff --git a/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.py b/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.py
index a58f403..40aa9ac 100644
--- a/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.py
+++ b/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.py
@@ -3,11 +3,11 @@
 
 
 import frappe
-from frappe import _
+from frappe import _, qb
+from frappe.query_builder import Criterion
 
 from erpnext import get_default_company
 from erpnext.accounts.party import get_party_details
-from erpnext.stock.get_item_details import get_price_list_rate_for
 
 
 def execute(filters=None):
@@ -50,6 +50,42 @@
 	]
 
 
+def fetch_item_prices(
+	customer: str = None, price_list: str = None, selling_price_list: str = None, items: list = None
+):
+	price_list_map = frappe._dict()
+	ip = qb.DocType("Item Price")
+	and_conditions = []
+	or_conditions = []
+	if items:
+		and_conditions.append(ip.item_code.isin([x.item_code for x in items]))
+		and_conditions.append(ip.selling == True)
+
+		or_conditions.append(ip.customer == None)
+		or_conditions.append(ip.price_list == None)
+
+		if customer:
+			or_conditions.append(ip.customer == customer)
+
+		if price_list:
+			or_conditions.append(ip.price_list == price_list)
+
+		if selling_price_list:
+			or_conditions.append(ip.price_list == selling_price_list)
+
+		res = (
+			qb.from_(ip)
+			.select(ip.item_code, ip.price_list, ip.price_list_rate)
+			.where(Criterion.all(and_conditions))
+			.where(Criterion.any(or_conditions))
+			.run(as_dict=True)
+		)
+		for x in res:
+			price_list_map.update({(x.item_code, x.price_list): x.price_list_rate})
+
+	return price_list_map
+
+
 def get_data(filters=None):
 	data = []
 	customer_details = get_customer_details(filters)
@@ -59,9 +95,17 @@
 		"Bin", fields=["item_code", "sum(actual_qty) AS available"], group_by="item_code"
 	)
 	item_stock_map = {item.item_code: item.available for item in item_stock_map}
+	price_list_map = fetch_item_prices(
+		customer_details.customer,
+		customer_details.price_list,
+		customer_details.selling_price_list,
+		items,
+	)
 
 	for item in items:
-		price_list_rate = get_price_list_rate_for(customer_details, item.item_code) or 0.0
+		price_list_rate = price_list_map.get(
+			(item.item_code, customer_details.price_list or customer_details.selling_price_list), 0.0
+		)
 		available_stock = item_stock_map.get(item.item_code)
 
 		data.append(
diff --git a/erpnext/setup/demo.py b/erpnext/setup/demo.py
index 926283f..4bc98b9 100644
--- a/erpnext/setup/demo.py
+++ b/erpnext/setup/demo.py
@@ -112,9 +112,9 @@
 	warehouse = get_warehouse(company)
 
 	if document_type == "Purchase Order":
-		posting_date = get_random_date(start_date, 1, 30)
+		posting_date = get_random_date(start_date, 1, 25)
 	else:
-		posting_date = get_random_date(start_date, 31, 364)
+		posting_date = get_random_date(start_date, 31, 350)
 
 	doctype.update(
 		{
diff --git a/erpnext/setup/demo_data/journal_entry.json b/erpnext/setup/demo_data/journal_entry.json
index b751c7c..a681be4 100644
--- a/erpnext/setup/demo_data/journal_entry.json
+++ b/erpnext/setup/demo_data/journal_entry.json
@@ -4,22 +4,22 @@
         "cheque_no": "33",
         "doctype": "Journal Entry",
         "accounts": [
-         {
-          "party_type": "Customer",
-          "party": "ABC Enterprises",
-          "credit_in_account_currency": 40000.0,
-          "debit_in_account_currency": 0.0,
-          "doctype": "Journal Entry Account",
-          "parentfield": "accounts",
-         },
-         {
-          "credit_in_account_currency": 0.0,
-          "debit_in_account_currency": 40000.0,
-          "doctype": "Journal Entry Account",
-          "parentfield": "accounts",
-         }
+            {
+                "party_type": "Customer",
+                "party": "ABC Enterprises",
+                "credit_in_account_currency": 40000.0,
+                "debit_in_account_currency": 0.0,
+                "doctype": "Journal Entry Account",
+                "parentfield": "accounts"
+            },
+            {
+                "credit_in_account_currency": 0.0,
+                "debit_in_account_currency": 40000.0,
+                "doctype": "Journal Entry Account",
+                "parentfield": "accounts"
+            }
         ],
         "user_remark": "test",
         "voucher_type": "Bank Entry"
     }
-]
\ No newline at end of file
+]
diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py
index 0e8ee2d..9897847 100644
--- a/erpnext/setup/doctype/company/company.py
+++ b/erpnext/setup/doctype/company/company.py
@@ -274,7 +274,7 @@
 						"parent_warehouse": "{0} - {1}".format(_("All Warehouses"), self.abbr)
 						if not wh_detail["is_group"]
 						else "",
-						"warehouse_type": wh_detail["warehouse_type"] if "warehouse_type" in wh_detail else None,
+						"warehouse_type": wh_detail.get("warehouse_type"),
 					}
 				)
 				warehouse.flags.ignore_permissions = True
diff --git a/erpnext/setup/doctype/customer_group/customer_group.js b/erpnext/setup/doctype/customer_group/customer_group.js
index 3c81b02..e352818 100644
--- a/erpnext/setup/doctype/customer_group/customer_group.js
+++ b/erpnext/setup/doctype/customer_group/customer_group.js
@@ -1,21 +1,6 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-
-cur_frm.cscript.refresh = function(doc, cdt, cdn) {
-	cur_frm.cscript.set_root_readonly(doc);
-}
-
-cur_frm.cscript.set_root_readonly = function(doc) {
-	// read-only for root customer group
-	if(!doc.parent_customer_group && !doc.__islocal) {
-		cur_frm.set_read_only();
-		cur_frm.set_intro(__("This is a root customer group and cannot be edited."));
-	} else {
-		cur_frm.set_intro(null);
-	}
-}
-
 frappe.ui.form.on("Customer Group", {
 	setup: function(frm){
 		frm.set_query('parent_customer_group', function (doc) {
@@ -48,5 +33,17 @@
 				}
 			}
 		});
-	}
+	},
+	refresh: function(frm) {
+		frm.trigger("set_root_readonly");
+	},
+	set_root_readonly: function(frm) {
+		// read-only for root customer group
+		if(!frm.doc.parent_customer_group && !frm.doc.__islocal) {
+			frm.set_read_only();
+			frm.set_intro(__("This is a root customer group and cannot be edited."));
+		} else {
+			frm.set_intro(null);
+		}
+	},
 });
diff --git a/erpnext/setup/doctype/sales_person/sales_person.js b/erpnext/setup/doctype/sales_person/sales_person.js
index d86a8f3..f0d9aa8 100644
--- a/erpnext/setup/doctype/sales_person/sales_person.js
+++ b/erpnext/setup/doctype/sales_person/sales_person.js
@@ -11,6 +11,7 @@
 			frm.dashboard.add_indicator(__('Total Contribution Amount Against Invoices: {0}',
 				[format_currency(info.allocated_amount_against_invoice, info.currency)]), 'blue');
 		}
+		frm.trigger("set_root_readonly");
 	},
 
 	setup: function(frm) {
@@ -27,22 +28,18 @@
 			'Sales Order': () => frappe.new_doc("Sales Order")
 				.then(() => frm.add_child("sales_team", {"sales_person": frm.doc.name}))
 		}
+	},
+	set_root_readonly: function(frm) {
+		// read-only for root
+		if(!frm.doc.parent_sales_person && !frm.doc.__islocal) {
+			frm.set_read_only();
+			frm.set_intro(__("This is a root sales person and cannot be edited."));
+		} else {
+			frm.set_intro(null);
+		}
 	}
 });
 
-cur_frm.cscript.refresh = function(doc, cdt, cdn) {
-	cur_frm.cscript.set_root_readonly(doc);
-}
-
-cur_frm.cscript.set_root_readonly = function(doc) {
-	// read-only for root
-	if(!doc.parent_sales_person && !doc.__islocal) {
-		cur_frm.set_read_only();
-		cur_frm.set_intro(__("This is a root sales person and cannot be edited."));
-	} else {
-		cur_frm.set_intro(null);
-	}
-}
 
 //get query select sales person
 cur_frm.fields_dict['parent_sales_person'].get_query = function(doc, cdt, cdn) {
diff --git a/erpnext/setup/doctype/supplier_group/supplier_group.js b/erpnext/setup/doctype/supplier_group/supplier_group.js
index 3362929..c697a99 100644
--- a/erpnext/setup/doctype/supplier_group/supplier_group.js
+++ b/erpnext/setup/doctype/supplier_group/supplier_group.js
@@ -1,21 +1,6 @@
 // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
 // For license information, please see license.txt
 
-cur_frm.cscript.refresh = function(doc) {
-	cur_frm.set_intro(doc.__islocal ? "" : __("There is nothing to edit."));
-	cur_frm.cscript.set_root_readonly(doc);
-};
-
-cur_frm.cscript.set_root_readonly = function(doc) {
-	// read-only for root customer group
-	if(!doc.parent_supplier_group && !doc.__islocal) {
-		cur_frm.set_read_only();
-		cur_frm.set_intro(__("This is a root supplier group and cannot be edited."));
-	} else {
-		cur_frm.set_intro(null);
-	}
-};
-
 frappe.ui.form.on("Supplier Group", {
 	setup: function(frm){
 		frm.set_query('parent_supplier_group', function (doc) {
@@ -48,5 +33,17 @@
 				}
 			}
 		});
+	},
+	refresh: function(frm) {
+		frm.set_intro(frm.doc.__islocal ? "" : __("There is nothing to edit."));
+		frm.trigger("set_root_readonly");
+	},
+	set_root_readonly: function(frm) {
+		if(!frm.doc.parent_supplier_group && !frm.doc.__islocal) {
+			frm.trigger("set_read_only");
+			frm.set_intro(__("This is a root supplier group and cannot be edited."));
+		} else {
+			frm.set_intro(null);
+		}
 	}
 });
diff --git a/erpnext/setup/doctype/territory/territory.js b/erpnext/setup/doctype/territory/territory.js
index 3caf814..e11d20b 100644
--- a/erpnext/setup/doctype/territory/territory.js
+++ b/erpnext/setup/doctype/territory/territory.js
@@ -11,23 +11,22 @@
 				}
 			}
 		};
+	},
+	refresh: function(frm) {
+		frm.trigger("set_root_readonly");
+	},
+	set_root_readonly: function(frm) {
+		// read-only for root territory
+		if(!frm.doc.parent_territory && !frm.doc.__islocal) {
+			frm.set_read_only();
+			frm.set_intro(__("This is a root territory and cannot be edited."));
+		} else {
+			frm.set_intro(null);
+		}
 	}
+
 });
 
-cur_frm.cscript.refresh = function(doc, cdt, cdn) {
-	cur_frm.cscript.set_root_readonly(doc);
-}
-
-cur_frm.cscript.set_root_readonly = function(doc) {
-	// read-only for root territory
-	if(!doc.parent_territory && !doc.__islocal) {
-		cur_frm.set_read_only();
-		cur_frm.set_intro(__("This is a root territory and cannot be edited."));
-	} else {
-		cur_frm.set_intro(null);
-	}
-}
-
 //get query select territory
 cur_frm.fields_dict['parent_territory'].get_query = function(doc,cdt,cdn) {
 	return{
diff --git a/erpnext/stock/doctype/bin/bin.json b/erpnext/stock/doctype/bin/bin.json
index 312470d..10d9511 100644
--- a/erpnext/stock/doctype/bin/bin.json
+++ b/erpnext/stock/doctype/bin/bin.json
@@ -52,8 +52,7 @@
    "oldfieldtype": "Link",
    "options": "Item",
    "read_only": 1,
-   "reqd": 1,
-   "search_index": 1
+   "reqd": 1
   },
   {
    "default": "0.00",
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json
index b85f296..7873d3e 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.json
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.json
@@ -301,7 +301,8 @@
    "no_copy": 1,
    "options": "Delivery Note",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "search_index": 1
   },
   {
    "collapsible": 1,
@@ -1401,7 +1402,7 @@
  "idx": 146,
  "is_submittable": 1,
  "links": [],
- "modified": "2023-09-04 14:15:28.363184",
+ "modified": "2023-12-18 17:19:39.368239",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Delivery Note",
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index a101bdf..675f8e9 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -1294,7 +1294,3 @@
 	)
 
 	return doclist
-
-
-def on_doctype_update():
-	frappe.db.add_index("Delivery Note", ["customer", "is_return", "return_against"])
diff --git a/erpnext/stock/doctype/delivery_note/patches/__init__.py b/erpnext/stock/doctype/delivery_note/patches/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/stock/doctype/delivery_note/patches/__init__.py
diff --git a/erpnext/stock/doctype/delivery_note/patches/drop_unused_return_against_index.py b/erpnext/stock/doctype/delivery_note/patches/drop_unused_return_against_index.py
new file mode 100644
index 0000000..cc29e67
--- /dev/null
+++ b/erpnext/stock/doctype/delivery_note/patches/drop_unused_return_against_index.py
@@ -0,0 +1,27 @@
+import click
+import frappe
+
+UNUSED_INDEXES = [
+	("Delivery Note", ["customer", "is_return", "return_against"]),
+	("Sales Invoice", ["customer", "is_return", "return_against"]),
+	("Purchase Invoice", ["supplier", "is_return", "return_against"]),
+	("Purchase Receipt", ["supplier", "is_return", "return_against"]),
+]
+
+
+def execute():
+	for doctype, index_fields in UNUSED_INDEXES:
+		table = f"tab{doctype}"
+		index_name = frappe.db.get_index_name(index_fields)
+		drop_index_if_exists(table, index_name)
+
+
+def drop_index_if_exists(table: str, index: str):
+	if not frappe.db.has_index(table, index):
+		return
+
+	try:
+		frappe.db.sql_ddl(f"ALTER TABLE `{table}` DROP INDEX `{index}`")
+		click.echo(f"✓ dropped {index} index from {table}")
+	except Exception:
+		frappe.log_error("Failed to drop index")
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index 9465574..933be53 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -174,6 +174,115 @@
 		for field, value in field_values.items():
 			self.assertEqual(cstr(serial_no.get(field)), value)
 
+	def test_delivery_note_return_against_denormalized_serial_no(self):
+		from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_return
+		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+
+		frappe.flags.ignore_serial_batch_bundle_validation = True
+		sn_item = "Old Serial NO Item Return Test - 1"
+		make_item(
+			sn_item,
+			{
+				"has_serial_no": 1,
+				"serial_no_series": "OSN-.####",
+				"is_stock_item": 1,
+			},
+		)
+
+		frappe.flags.ignore_serial_batch_bundle_validation = True
+		serial_nos = [
+			"OSN-1",
+			"OSN-2",
+			"OSN-3",
+			"OSN-4",
+			"OSN-5",
+			"OSN-6",
+			"OSN-7",
+			"OSN-8",
+			"OSN-9",
+			"OSN-10",
+			"OSN-11",
+			"OSN-12",
+		]
+
+		for sn in serial_nos:
+			if not frappe.db.exists("Serial No", sn):
+				sn_doc = frappe.get_doc(
+					{
+						"doctype": "Serial No",
+						"item_code": sn_item,
+						"serial_no": sn,
+					}
+				)
+				sn_doc.insert()
+
+		warehouse = "_Test Warehouse - _TC"
+		company = frappe.db.get_value("Warehouse", warehouse, "company")
+		se_doc = make_stock_entry(
+			item_code=sn_item,
+			company=company,
+			target="_Test Warehouse - _TC",
+			qty=12,
+			basic_rate=100,
+			do_not_submit=1,
+		)
+
+		se_doc.items[0].serial_no = "\n".join(serial_nos)
+		se_doc.submit()
+
+		self.assertEqual(sorted(get_serial_nos(se_doc.items[0].serial_no)), sorted(serial_nos))
+
+		dn = create_delivery_note(
+			item_code=sn_item,
+			qty=12,
+			rate=500,
+			warehouse=warehouse,
+			company=company,
+			expense_account="Cost of Goods Sold - _TC",
+			cost_center="Main - _TC",
+			do_not_submit=1,
+		)
+
+		dn.items[0].serial_no = "\n".join(serial_nos)
+		dn.submit()
+		dn.reload()
+
+		self.assertTrue(dn.items[0].serial_no)
+
+		frappe.flags.ignore_serial_batch_bundle_validation = False
+
+		# return entry
+		dn1 = make_sales_return(dn.name)
+
+		dn1.items[0].qty = -2
+
+		bundle_doc = frappe.get_doc("Serial and Batch Bundle", dn1.items[0].serial_and_batch_bundle)
+		bundle_doc.set("entries", bundle_doc.entries[:2])
+		bundle_doc.save()
+
+		dn1.save()
+		dn1.submit()
+
+		returned_serial_nos1 = get_serial_nos_from_bundle(dn1.items[0].serial_and_batch_bundle)
+		for serial_no in returned_serial_nos1:
+			self.assertTrue(serial_no in serial_nos)
+
+		dn2 = make_sales_return(dn.name)
+
+		dn2.items[0].qty = -2
+
+		bundle_doc = frappe.get_doc("Serial and Batch Bundle", dn2.items[0].serial_and_batch_bundle)
+		bundle_doc.set("entries", bundle_doc.entries[:2])
+		bundle_doc.save()
+
+		dn2.save()
+		dn2.submit()
+
+		returned_serial_nos2 = get_serial_nos_from_bundle(dn2.items[0].serial_and_batch_bundle)
+		for serial_no in returned_serial_nos2:
+			self.assertTrue(serial_no in serial_nos)
+			self.assertFalse(serial_no in returned_serial_nos1)
+
 	def test_sales_return_for_non_bundled_items_partial(self):
 		company = frappe.db.get_value("Warehouse", "Stores - TCP1", "company")
 
@@ -1266,6 +1375,109 @@
 		dn.reload()
 		self.assertEqual(frappe.db.get_value("Serial No", serial_no, "status"), "Delivered")
 
+	def test_sales_return_valuation_for_moving_average(self):
+		item_code = make_item(
+			"_Test Item Sales Return with MA", {"is_stock_item": 1, "valuation_method": "Moving Average"}
+		).name
+
+		make_stock_entry(
+			item_code=item_code,
+			target="_Test Warehouse - _TC",
+			qty=5,
+			basic_rate=100.0,
+			posting_date=add_days(nowdate(), -5),
+		)
+		dn = create_delivery_note(
+			item_code=item_code, qty=5, rate=500, posting_date=add_days(nowdate(), -4)
+		)
+		self.assertEqual(dn.items[0].incoming_rate, 100.0)
+
+		make_stock_entry(
+			item_code=item_code,
+			target="_Test Warehouse - _TC",
+			qty=5,
+			basic_rate=200.0,
+			posting_date=add_days(nowdate(), -3),
+		)
+		make_stock_entry(
+			item_code=item_code,
+			target="_Test Warehouse - _TC",
+			qty=5,
+			basic_rate=300.0,
+			posting_date=add_days(nowdate(), -2),
+		)
+
+		dn1 = create_delivery_note(
+			is_return=1,
+			item_code=item_code,
+			return_against=dn.name,
+			qty=-5,
+			rate=500,
+			company=dn.company,
+			expense_account="Cost of Goods Sold - _TC",
+			cost_center="Main - _TC",
+			do_not_submit=1,
+			posting_date=add_days(nowdate(), -1),
+		)
+
+		# (300 * 5) + (200 * 5) = 2500
+		# 2500 / 10 = 250
+
+		self.assertAlmostEqual(dn1.items[0].incoming_rate, 250.0)
+
+	def test_sales_return_valuation_for_moving_average_case2(self):
+		# Make DN return
+		# Make Bakcdated Purchase Receipt and check DN return valuation rate
+		# The rate should be recalculate based on the backdated purchase receipt
+		frappe.flags.print_debug_messages = False
+		item_code = make_item(
+			"_Test Item Sales Return with MA Case2",
+			{"is_stock_item": 1, "valuation_method": "Moving Average", "stock_uom": "Nos"},
+		).name
+
+		make_stock_entry(
+			item_code=item_code,
+			target="_Test Warehouse - _TC",
+			qty=5,
+			basic_rate=100.0,
+			posting_date=add_days(nowdate(), -5),
+		)
+
+		dn = create_delivery_note(
+			item_code=item_code,
+			warehouse="_Test Warehouse - _TC",
+			qty=5,
+			rate=500,
+			posting_date=add_days(nowdate(), -4),
+		)
+
+		returned_dn = create_delivery_note(
+			is_return=1,
+			item_code=item_code,
+			return_against=dn.name,
+			qty=-5,
+			rate=500,
+			company=dn.company,
+			warehouse="_Test Warehouse - _TC",
+			expense_account="Cost of Goods Sold - _TC",
+			cost_center="Main - _TC",
+			posting_date=add_days(nowdate(), -1),
+		)
+
+		self.assertAlmostEqual(returned_dn.items[0].incoming_rate, 100.0)
+
+		# Make backdated purchase receipt
+		make_stock_entry(
+			item_code=item_code,
+			target="_Test Warehouse - _TC",
+			qty=5,
+			basic_rate=200.0,
+			posting_date=add_days(nowdate(), -3),
+		)
+
+		returned_dn.reload()
+		self.assertAlmostEqual(returned_dn.items[0].incoming_rate, 200.0)
+
 
 def create_delivery_note(**args):
 	dn = frappe.new_doc("Delivery Note")
diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py
index a942f58..b237f73 100644
--- a/erpnext/stock/doctype/item/test_item.py
+++ b/erpnext/stock/doctype/item/test_item.py
@@ -522,39 +522,25 @@
 		self.assertEqual(factor, 1.0)
 
 	def test_item_variant_by_manufacturer(self):
-		fields = [{"field_name": "description"}, {"field_name": "variant_based_on"}]
-		set_item_variant_settings(fields)
+		template = make_item(
+			"_Test Item Variant By Manufacturer", {"has_variants": 1, "variant_based_on": "Manufacturer"}
+		).name
 
-		if frappe.db.exists("Item", "_Test Variant Mfg"):
-			frappe.delete_doc("Item", "_Test Variant Mfg")
-		if frappe.db.exists("Item", "_Test Variant Mfg-1"):
-			frappe.delete_doc("Item", "_Test Variant Mfg-1")
-		if frappe.db.exists("Manufacturer", "MSG1"):
-			frappe.delete_doc("Manufacturer", "MSG1")
+		for manufacturer in ["DFSS", "DASA", "ASAAS"]:
+			if not frappe.db.exists("Manufacturer", manufacturer):
+				m_doc = frappe.new_doc("Manufacturer")
+				m_doc.short_name = manufacturer
+				m_doc.insert()
 
-		template = frappe.get_doc(
-			dict(
-				doctype="Item",
-				item_code="_Test Variant Mfg",
-				has_variant=1,
-				item_group="Products",
-				variant_based_on="Manufacturer",
-			)
-		).insert()
+		self.assertFalse(frappe.db.exists("Item Manufacturer", {"manufacturer": "DFSS"}))
+		variant = get_variant(template, manufacturer="DFSS", manufacturer_part_no="DFSS-123")
 
-		manufacturer = frappe.get_doc(dict(doctype="Manufacturer", short_name="MSG1")).insert()
+		item_manufacturer = frappe.db.exists(
+			"Item Manufacturer", {"manufacturer": "DFSS", "item_code": variant.name}
+		)
+		self.assertTrue(item_manufacturer)
 
-		variant = get_variant(template.name, manufacturer=manufacturer.name)
-		self.assertEqual(variant.item_code, "_Test Variant Mfg-1")
-		self.assertEqual(variant.description, "_Test Variant Mfg")
-		self.assertEqual(variant.manufacturer, "MSG1")
-		variant.insert()
-
-		variant = get_variant(template.name, manufacturer=manufacturer.name, manufacturer_part_no="007")
-		self.assertEqual(variant.item_code, "_Test Variant Mfg-2")
-		self.assertEqual(variant.description, "_Test Variant Mfg")
-		self.assertEqual(variant.manufacturer, "MSG1")
-		self.assertEqual(variant.manufacturer_part_no, "007")
+		frappe.delete_doc("Item Manufacturer", item_manufacturer)
 
 	def test_stock_exists_against_template_item(self):
 		stock_item = frappe.get_all("Stock Ledger Entry", fields=["item_code"], limit=1)
diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py
index 3e90ed5..ad9b34c 100644
--- a/erpnext/stock/doctype/material_request/material_request.py
+++ b/erpnext/stock/doctype/material_request/material_request.py
@@ -169,7 +169,9 @@
 	def on_submit(self):
 		self.update_requested_qty_in_production_plan()
 		self.update_requested_qty()
-		if self.material_request_type == "Purchase":
+		if self.material_request_type == "Purchase" and frappe.db.exists(
+			"Budget", {"applicable_on_material_request": 1, "docstatus": 1}
+		):
 			self.validate_budget()
 
 	def before_save(self):
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
index 6c9d339..2cbccb0 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
@@ -88,6 +88,20 @@
 			}, __('Create'));
 		}
 
+		if (frm.doc.docstatus === 0) {
+			if (!frm.doc.is_return) {
+				frappe.db.get_single_value("Buying Settings", "maintain_same_rate").then((value) => {
+					if (value) {
+						frm.doc.items.forEach((item) => {
+							frm.fields_dict.items.grid.update_docfield_property(
+								"rate", "read_only", (item.purchase_order && item.purchase_order_item)
+							);
+						});
+					}
+				});
+			}
+		}
+
 		frm.events.add_custom_buttons(frm);
 	},
 
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
index c7ad660..a181022 100755
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
@@ -289,7 +289,8 @@
    "no_copy": 1,
    "options": "Purchase Receipt",
    "print_hide": 1,
-   "read_only": 1
+   "read_only": 1,
+   "search_index": 1
   },
   {
    "fieldname": "section_addresses",
@@ -1251,7 +1252,7 @@
  "idx": 261,
  "is_submittable": 1,
  "links": [],
- "modified": "2023-11-28 13:14:15.243474",
+ "modified": "2023-12-18 17:26:41.279663",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Purchase Receipt",
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index ab07271..10d9eaa 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -518,6 +518,7 @@
 							debit=0.0,
 							credit=discrepancy_caused_by_exchange_rate_difference,
 							remarks=remarks,
+							against_type="Supplier",
 							against_account=self.supplier,
 							debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference,
 							account_currency=account_currency,
@@ -531,6 +532,7 @@
 							debit=discrepancy_caused_by_exchange_rate_difference,
 							credit=0.0,
 							remarks=remarks,
+							against_type="Supplier",
 							against_account=self.supplier,
 							debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference,
 							account_currency=account_currency,
@@ -796,7 +798,7 @@
 			# Backward compatibility:
 			# and charges added via Landed Cost Voucher,
 			# post valuation related charges on "Stock Received But Not Billed"
-			against_account = ", ".join([d.account for d in gl_entries if flt(d.debit) > 0])
+			against_accounts = [d.account for d in gl_entries if flt(d.debit) > 0]
 			total_valuation_amount = sum(valuation_tax.values())
 			amount_including_divisional_loss = negative_expense_to_be_booked
 			stock_rbnb = (
@@ -828,16 +830,17 @@
 						)
 						amount_including_divisional_loss -= applicable_amount
 
-					self.add_gl_entry(
-						gl_entries=gl_entries,
-						account=account,
-						cost_center=tax.cost_center,
-						debit=0.0,
-						credit=applicable_amount,
-						remarks=self.remarks or _("Accounting Entry for Stock"),
-						against_account=against_account,
-						item=tax,
-					)
+					for against in against_accounts:
+						self.add_gl_entry(
+							gl_entries=gl_entries,
+							account=account,
+							cost_center=tax.cost_center,
+							debit=0.0,
+							credit=flt(applicable_amount) / len(against_accounts),
+							remarks=self.remarks or _("Accounting Entry for Stock"),
+							against_account=against,
+							item=tax,
+						)
 
 					i += 1
 
@@ -1357,10 +1360,6 @@
 	return item_account_wise_cost
 
 
-def on_doctype_update():
-	frappe.db.add_index("Purchase Receipt", ["supplier", "is_return", "return_against"])
-
-
 @erpnext.allow_regional
 def update_regional_gl_entries(gl_list, doc):
 	return
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 146cbff..d274069 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -4,6 +4,7 @@
 import frappe
 from frappe.tests.utils import FrappeTestCase, change_settings
 from frappe.utils import add_days, cint, cstr, flt, nowtime, today
+from pypika import Order
 from pypika import functions as fn
 
 import erpnext
@@ -990,7 +991,7 @@
 		gl_entries = get_gl_entries("Purchase Receipt", pr.name)
 		sl_entries = get_sl_entries("Purchase Receipt", pr.name)
 
-		self.assertFalse(gl_entries)
+		self.assertEqual(len(gl_entries), 2)
 
 		expected_sle = {"Work In Progress - TCP1": -5, "Stores - TCP1": 5}
 
@@ -2235,13 +2236,13 @@
 
 
 def get_gl_entries(voucher_type, voucher_no):
-	return frappe.db.sql(
-		"""select account, debit, credit, cost_center, is_cancelled
-		from `tabGL Entry` where voucher_type=%s and voucher_no=%s
-		order by account desc""",
-		(voucher_type, voucher_no),
-		as_dict=1,
-	)
+	gle = frappe.qb.DocType("GL Entry")
+	return (
+		frappe.qb.from_(gle)
+		.select(gle.account, gle.debit, gle.credit, gle.cost_center, gle.is_cancelled)
+		.where((gle.voucher_type == voucher_type) & (gle.voucher_no == voucher_no))
+		.orderby(gle.account, gle.debit, order=Order.desc)
+	).run(as_dict=True)
 
 
 def get_taxes(**args):
diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
index 7344d2a..9bd692a 100644
--- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
+++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
@@ -359,7 +359,6 @@
    "oldfieldtype": "Currency",
    "options": "currency",
    "print_width": "100px",
-   "read_only_depends_on": "eval: (!parent.is_return && doc.purchase_order && doc.purchase_order_item)",
    "width": "100px"
   },
   {
@@ -1104,7 +1103,7 @@
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-11-30 16:12:02.364608",
+ "modified": "2023-12-25 22:32:09.801965",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Purchase Receipt Item",
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 ecb9314..afb53fb 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
@@ -23,7 +23,11 @@
 )
 from frappe.utils.csvutils import build_csv_response
 
-from erpnext.stock.serial_batch_bundle import BatchNoValuation, SerialNoValuation
+from erpnext.stock.serial_batch_bundle import (
+	BatchNoValuation,
+	SerialNoValuation,
+	get_batches_from_bundle,
+)
 from erpnext.stock.serial_batch_bundle import get_serial_nos as get_serial_nos_from_bundle
 
 
@@ -81,6 +85,7 @@
 	# end: auto-generated types
 
 	def validate(self):
+		self.set_batch_no()
 		self.validate_serial_and_batch_no()
 		self.validate_duplicate_serial_and_batch_no()
 		self.validate_voucher_no()
@@ -95,6 +100,26 @@
 		self.set_incoming_rate()
 		self.calculate_qty_and_amount()
 
+	def set_batch_no(self):
+		if self.has_serial_no and self.has_batch_no:
+			serial_nos = [d.serial_no for d in self.entries if d.serial_no]
+			has_no_batch = any(not d.batch_no for d in self.entries)
+			if not has_no_batch:
+				return
+
+			serial_no_batch = frappe._dict(
+				frappe.get_all(
+					"Serial No",
+					filters={"name": ("in", serial_nos)},
+					fields=["name", "batch_no"],
+					as_list=True,
+				)
+			)
+
+			for row in self.entries:
+				if not row.batch_no:
+					row.batch_no = serial_no_batch.get(row.serial_no)
+
 	def validate_serial_nos_inventory(self):
 		if not (self.has_serial_no and self.type_of_transaction == "Outward"):
 			return
@@ -123,6 +148,11 @@
 				)
 
 	def validate_serial_nos_duplicate(self):
+		# Don't inward same serial number multiple times
+
+		if not self.warehouse:
+			return
+
 		if self.voucher_type in ["Stock Reconciliation", "Stock Entry"] and self.docstatus != 1:
 			return
 
@@ -146,7 +176,6 @@
 			kwargs["voucher_no"] = self.voucher_no
 
 		available_serial_nos = get_available_serial_nos(kwargs)
-
 		for data in available_serial_nos:
 			if data.serial_no in serial_nos:
 				self.throw_error_message(
@@ -327,6 +356,19 @@
 		):
 			values_to_set["posting_time"] = parent.posting_time
 
+		if parent.doctype in [
+			"Delivery Note",
+			"Purchase Receipt",
+			"Purchase Invoice",
+			"Sales Invoice",
+		] and parent.get("is_return"):
+			return_ref_field = frappe.scrub(parent.doctype) + "_item"
+			if parent.doctype == "Delivery Note":
+				return_ref_field = "dn_detail"
+
+			if row.get(return_ref_field):
+				values_to_set["returned_against"] = row.get(return_ref_field)
+
 		if values_to_set:
 			self.db_set(values_to_set)
 
@@ -438,7 +480,7 @@
 			qty_field = "qty"
 
 		precision = row.precision
-		if self.voucher_type in ["Subcontracting Receipt"]:
+		if row.get("doctype") in ["Subcontracting Receipt Supplied Item"]:
 			qty_field = "consumed_qty"
 
 		if abs(abs(flt(self.total_qty, precision)) - abs(flt(row.get(qty_field), precision))) > 0.01:
@@ -509,7 +551,6 @@
 		batch_nos = []
 
 		serial_batches = {}
-
 		for row in self.entries:
 			if self.has_serial_no and not row.serial_no:
 				frappe.throw(
@@ -590,6 +631,67 @@
 				f"Batch Nos {bold(incorrect_batch_nos)} does not belong to Item {bold(self.item_code)}"
 			)
 
+	def validate_serial_and_batch_no_for_returned(self):
+		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+
+		if not self.returned_against:
+			return
+
+		if self.voucher_type not in [
+			"Purchase Receipt",
+			"Purchase Invoice",
+			"Sales Invoice",
+			"Delivery Note",
+		]:
+			return
+
+		data = self.get_orignal_document_data()
+		if not data:
+			return
+
+		serial_nos, batches = [], []
+		current_serial_nos = [d.serial_no for d in self.entries if d.serial_no]
+		current_batches = [d.batch_no for d in self.entries if d.batch_no]
+
+		for d in data:
+			if self.has_serial_no:
+				if d.serial_and_batch_bundle:
+					serial_nos = get_serial_nos_from_bundle(d.serial_and_batch_bundle)
+				else:
+					serial_nos = get_serial_nos(d.serial_no)
+
+			elif self.has_batch_no:
+				if d.serial_and_batch_bundle:
+					batches = get_batches_from_bundle(d.serial_and_batch_bundle)
+				else:
+					batches = frappe._dict({d.batch_no: d.stock_qty})
+
+				if batches:
+					batches = [d for d in batches if batches[d] > 0]
+
+			if serial_nos:
+				if not set(current_serial_nos).issubset(set(serial_nos)):
+					self.throw_error_message(
+						f"Serial Nos {bold(', '.join(serial_nos))} are not part of the original document."
+					)
+
+			if batches:
+				if not set(current_batches).issubset(set(batches)):
+					self.throw_error_message(
+						f"Batch Nos {bold(', '.join(batches))} are not part of the original document."
+					)
+
+	def get_orignal_document_data(self):
+		fields = ["serial_and_batch_bundle", "stock_qty"]
+		if self.has_serial_no:
+			fields.append("serial_no")
+
+		elif self.has_batch_no:
+			fields.append("batch_no")
+
+		child_doc = self.voucher_type + " Item"
+		return frappe.get_all(child_doc, fields=fields, filters={"name": self.returned_against})
+
 	def validate_duplicate_serial_and_batch_no(self):
 		serial_nos = []
 		batch_nos = []
@@ -688,9 +790,29 @@
 		for batch in batches:
 			frappe.db.set_value("Batch", batch.name, {"reference_name": None, "reference_doctype": None})
 
+	def before_submit(self):
+		self.validate_serial_and_batch_no_for_returned()
+		self.set_purchase_document_no()
+
 	def on_submit(self):
 		self.validate_serial_nos_inventory()
 
+	def set_purchase_document_no(self):
+		if not self.has_serial_no:
+			return
+
+		if self.total_qty > 0:
+			serial_nos = [d.serial_no for d in self.entries if d.serial_no]
+			sn_table = frappe.qb.DocType("Serial No")
+			(
+				frappe.qb.update(sn_table)
+				.set(
+					sn_table.purchase_document_no,
+					self.voucher_no if not sn_table.purchase_document_no else self.voucher_no,
+				)
+				.where(sn_table.name.isin(serial_nos))
+			).run()
+
 	def validate_serial_and_batch_inventory(self):
 		self.check_future_entries_exists()
 		self.validate_batch_inventory()
@@ -1063,7 +1185,7 @@
 		doc.append(
 			"entries",
 			{
-				"qty": (row.qty or 1.0) * (1 if type_of_transaction == "Inward" else -1),
+				"qty": (flt(row.qty) or 1.0) * (1 if type_of_transaction == "Inward" else -1),
 				"warehouse": warehouse,
 				"batch_no": row.batch_no,
 				"serial_no": row.serial_no,
@@ -1091,7 +1213,7 @@
 		doc.append(
 			"entries",
 			{
-				"qty": (d.get("qty") or 1.0) * (1 if doc.type_of_transaction == "Inward" else -1),
+				"qty": (flt(d.get("qty")) or 1.0) * (1 if doc.type_of_transaction == "Inward" else -1),
 				"warehouse": warehouse or d.get("warehouse"),
 				"batch_no": d.get("batch_no"),
 				"serial_no": d.get("serial_no"),
diff --git a/erpnext/stock/doctype/serial_no/serial_no.json b/erpnext/stock/doctype/serial_no/serial_no.json
index b4ece00..2d7fcac 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.json
+++ b/erpnext/stock/doctype/serial_no/serial_no.json
@@ -27,8 +27,6 @@
   "column_break_24",
   "location",
   "employee",
-  "delivery_details",
-  "delivery_document_type",
   "warranty_amc_details",
   "column_break6",
   "warranty_expiry_date",
@@ -39,7 +37,8 @@
   "more_info",
   "company",
   "column_break_2cmm",
-  "work_order"
+  "work_order",
+  "purchase_document_no"
  ],
  "fields": [
   {
@@ -154,20 +153,6 @@
    "read_only": 1
   },
   {
-   "fieldname": "delivery_details",
-   "fieldtype": "Section Break",
-   "label": "Delivery Details",
-   "oldfieldtype": "Column Break"
-  },
-  {
-   "fieldname": "delivery_document_type",
-   "fieldtype": "Link",
-   "label": "Delivery Document Type",
-   "no_copy": 1,
-   "options": "DocType",
-   "read_only": 1
-  },
-  {
    "fieldname": "warranty_amc_details",
    "fieldtype": "Section Break",
    "label": "Warranty / AMC Details"
@@ -275,12 +260,19 @@
   {
    "fieldname": "column_break_2cmm",
    "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "purchase_document_no",
+   "fieldtype": "Data",
+   "label": "Creation Document No",
+   "no_copy": 1,
+   "read_only": 1
   }
  ],
  "icon": "fa fa-barcode",
  "idx": 1,
  "links": [],
- "modified": "2023-11-28 15:37:59.489945",
+ "modified": "2023-12-17 10:52:55.767839",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Serial No",
diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py
index d562560..122664c 100644
--- a/erpnext/stock/doctype/serial_no/serial_no.py
+++ b/erpnext/stock/doctype/serial_no/serial_no.py
@@ -41,7 +41,6 @@
 		batch_no: DF.Link | None
 		brand: DF.Link | None
 		company: DF.Link
-		delivery_document_type: DF.Link | None
 		description: DF.Text | None
 		employee: DF.Link | None
 		item_code: DF.Link
@@ -51,6 +50,7 @@
 		maintenance_status: DF.Literal[
 			"", "Under Warranty", "Out of Warranty", "Under AMC", "Out of AMC"
 		]
+		purchase_document_no: DF.Data | None
 		purchase_rate: DF.Float
 		serial_no: DF.Data
 		status: DF.Literal["", "Active", "Inactive", "Delivered", "Expired"]
@@ -231,26 +231,6 @@
 	return sorted([d.get("name") for d in serial_numbers])
 
 
-def get_delivered_serial_nos(serial_nos):
-	"""
-	Returns serial numbers that delivered from the list of serial numbers
-	"""
-	from frappe.query_builder.functions import Coalesce
-
-	SerialNo = frappe.qb.DocType("Serial No")
-	serial_nos = get_serial_nos(serial_nos)
-	query = (
-		frappe.qb.select(SerialNo.name)
-		.from_(SerialNo)
-		.where((SerialNo.name.isin(serial_nos)) & (Coalesce(SerialNo.delivery_document_type, "") != ""))
-	)
-
-	result = query.run()
-	if result and len(result) > 0:
-		delivered_serial_nos = [row[0] for row in result]
-		return delivered_serial_nos
-
-
 @frappe.whitelist()
 def get_pos_reserved_serial_nos(filters):
 	if isinstance(filters, str):
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 7af5d1a..8da3e8f 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -512,7 +512,12 @@
 				},
 				callback: function(r) {
 					if (!r.exc) {
-						["actual_qty", "basic_rate"].forEach((field) => {
+						let fields = ["actual_qty", "basic_rate"];
+						if (frm.doc.purpose == "Material Receipt") {
+							fields = ["actual_qty"];
+						}
+
+						fields.forEach((field) => {
 							frappe.model.set_value(cdt, cdn, field, (r.message[field] || 0.0));
 						});
 						frm.events.calculate_basic_amount(frm, child);
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 84e99c5..6521394 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -1463,7 +1463,9 @@
 						self.get_gl_dict(
 							{
 								"account": account,
+								"against_type": "Account",
 								"against": d.expense_account,
+								"against_link": d.expense_account,
 								"cost_center": d.cost_center,
 								"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
 								"credit_in_account_currency": flt(amount["amount"]),
@@ -1477,7 +1479,9 @@
 						self.get_gl_dict(
 							{
 								"account": d.expense_account,
+								"against_type": "Account",
 								"against": account,
+								"against_link": account,
 								"cost_center": d.cost_center,
 								"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
 								"credit": -1
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index e0e364f..d400312 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -504,7 +504,14 @@
 		self.check_gl_entries(
 			"Stock Entry",
 			repack.name,
-			sorted([[stock_in_hand_account, 1200, 0.0], ["Cost of Goods Sold - TCP1", 0.0, 1200.0]]),
+			sorted(
+				[
+					["Cost of Goods Sold - TCP1", 0.0, 1200.0],
+					["Stock Adjustment - TCP1", 0.0, 1200.0],
+					["Stock Adjustment - TCP1", 1200.0, 0.0],
+					[stock_in_hand_account, 1200.0, 0.0],
+				]
+			),
 		)
 
 	def check_stock_ledger_entries(self, voucher_type, voucher_no, expected_sle):
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
index 23788cf..6e7af68 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -181,6 +181,9 @@
 			frappe.throw(_("Actual Qty is mandatory"))
 
 	def validate_serial_batch_no_bundle(self):
+		if self.is_cancelled == 1:
+			return
+
 		item_detail = frappe.get_cached_value(
 			"Item",
 			self.item_code,
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index e8d652e..6819968 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -171,7 +171,7 @@
 						},
 					)
 
-			if item_details.has_batch_no:
+			elif item_details.has_batch_no:
 				batch_nos_details = get_available_batches(
 					frappe._dict(
 						{
@@ -228,6 +228,9 @@
 
 	def set_new_serial_and_batch_bundle(self):
 		for item in self.items:
+			if not item.qty:
+				continue
+
 			if item.current_serial_and_batch_bundle and not item.serial_and_batch_bundle:
 				current_doc = frappe.get_doc("Serial and Batch Bundle", item.current_serial_and_batch_bundle)
 
diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
index 1ec99bf..70e9fb2 100644
--- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
@@ -865,6 +865,66 @@
 		sr1.load_from_db()
 		self.assertEqual(sr1.difference_amount, 10000)
 
+	def test_make_stock_zero_for_serial_batch_item(self):
+		from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
+
+		serial_item = self.make_item(
+			properties={"is_stock_item": 1, "has_serial_no": 1, "serial_no_series": "DJJ.####"}
+		).name
+		batch_item = self.make_item(
+			properties={
+				"is_stock_item": 1,
+				"has_batch_no": 1,
+				"batch_number_series": "BDJJ.####",
+				"create_new_batch": 1,
+			}
+		).name
+
+		serial_batch_item = self.make_item(
+			properties={
+				"is_stock_item": 1,
+				"has_batch_no": 1,
+				"batch_number_series": "ADJJ.####",
+				"create_new_batch": 1,
+				"has_serial_no": 1,
+				"serial_no_series": "SN-ADJJ.####",
+			}
+		).name
+
+		warehouse = "_Test Warehouse - _TC"
+
+		for item_code in [serial_item, batch_item, serial_batch_item]:
+			make_stock_entry(
+				item_code=item_code,
+				target=warehouse,
+				qty=10,
+				basic_rate=100,
+			)
+
+			_reco = create_stock_reconciliation(
+				item_code=item_code,
+				warehouse=warehouse,
+				qty=0.0,
+			)
+
+			serial_batch_bundle = frappe.get_all(
+				"Stock Ledger Entry",
+				{"item_code": item_code, "warehouse": warehouse, "is_cancelled": 0, "voucher_no": _reco.name},
+				"serial_and_batch_bundle",
+			)
+
+			self.assertEqual(len(serial_batch_bundle), 1)
+
+			_reco.cancel()
+
+			serial_batch_bundle = frappe.get_all(
+				"Stock Ledger Entry",
+				{"item_code": item_code, "warehouse": warehouse, "is_cancelled": 0, "voucher_no": _reco.name},
+				"serial_and_batch_bundle",
+			)
+
+			self.assertEqual(len(serial_batch_bundle), 0)
+
 
 def create_batch_item_with_batch(item_name, batch_id):
 	batch_item_doc = create_item(item_name, is_stock_item=1)
diff --git a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py
index 24650fd..7e03ac3 100644
--- a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py
+++ b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py
@@ -9,7 +9,7 @@
 from frappe.query_builder.functions import Sum
 from frappe.utils import cint, flt
 
-from erpnext.stock.utils import get_or_make_bin
+from erpnext.stock.utils import get_or_make_bin, get_stock_balance
 
 
 class StockReservationEntry(Document):
@@ -151,7 +151,7 @@
 		"""Validates `Reserved Qty` when `Reservation Based On` is `Qty`."""
 
 		if self.reservation_based_on == "Qty":
-			self.validate_with_max_reserved_qty(self.reserved_qty)
+			self.validate_with_allowed_qty(self.reserved_qty)
 
 	def auto_reserve_serial_and_batch(self, based_on: str = None) -> None:
 		"""Auto pick Serial and Batch Nos to reserve when `Reservation Based On` is `Serial and Batch`."""
@@ -324,7 +324,7 @@
 				frappe.throw(msg)
 
 			# Should be called after validating Serial and Batch Nos.
-			self.validate_with_max_reserved_qty(qty_to_be_reserved)
+			self.validate_with_allowed_qty(qty_to_be_reserved)
 			self.db_set("reserved_qty", qty_to_be_reserved)
 
 	def update_reserved_qty_in_voucher(
@@ -429,7 +429,7 @@
 			msg = _("Stock Reservation Entry cannot be updated as it has been delivered.")
 			frappe.throw(msg)
 
-	def validate_with_max_reserved_qty(self, qty_to_be_reserved: float) -> None:
+	def validate_with_allowed_qty(self, qty_to_be_reserved: float) -> None:
 		"""Validates `Reserved Qty` with `Max Reserved Qty`."""
 
 		self.db_set(
@@ -448,12 +448,12 @@
 			)
 			voucher_delivered_qty = flt(delivered_qty) * flt(conversion_factor)
 
-		max_reserved_qty = min(
+		allowed_qty = min(
 			self.available_qty, (self.voucher_qty - voucher_delivered_qty - total_reserved_qty)
 		)
 
-		if max_reserved_qty <= 0 and self.voucher_type == "Sales Order":
-			msg = _("Item {0} is already delivered for Sales Order {1}.").format(
+		if self.get("_action") != "submit" and self.voucher_type == "Sales Order" and allowed_qty <= 0:
+			msg = _("Item {0} is already reserved/delivered against Sales Order {1}.").format(
 				frappe.bold(self.item_code), frappe.bold(self.voucher_no)
 			)
 
@@ -463,19 +463,33 @@
 			else:
 				frappe.throw(msg)
 
-		if qty_to_be_reserved > max_reserved_qty:
+		if qty_to_be_reserved > allowed_qty:
+			actual_qty = get_stock_balance(self.item_code, self.warehouse)
 			msg = """
-				Cannot reserve more than Max Reserved Qty {0} {1}.<br /><br />
-				The <b>Max Reserved Qty</b> is calculated as follows:<br />
+				Cannot reserve more than Allowed Qty {0} {1} for Item {2} against {3} {4}.<br /><br />
+				The <b>Allowed Qty</b> is calculated as follows:<br />
 				<ul>
-					<li><b>Available Qty To Reserve</b> = (Actual Stock Qty - Reserved Stock Qty)</li>
-					<li><b>Voucher Qty</b> = Voucher Item Qty</li>
-					<li><b>Delivered Qty</b> = Qty delivered against the Voucher Item</li>
-					<li><b>Total Reserved Qty</b> = Qty reserved against the Voucher Item</li>
-					<li><b>Max Reserved Qty</b> = Minimum of (Available Qty To Reserve, (Voucher Qty - Delivered Qty - Total Reserved Qty))</li>
+					<li>Actual Qty [Available Qty at Warehouse] = {5}</li>
+					<li>Reserved Stock [Ignore current SRE] = {6}</li>
+					<li>Available Qty To Reserve [Actual Qty - Reserved Stock] = {7}</li>
+					<li>Voucher Qty [Voucher Item Qty] = {8}</li>
+					<li>Delivered Qty [Qty delivered against the Voucher Item] = {9}</li>
+					<li>Total Reserved Qty [Qty reserved against the Voucher Item] = {10}</li>
+					<li>Allowed Qty [Minimum of (Available Qty To Reserve, (Voucher Qty - Delivered Qty - Total Reserved Qty))] = {11}</li>
 				</ul>
 			""".format(
-				frappe.bold(max_reserved_qty), self.stock_uom
+				frappe.bold(allowed_qty),
+				self.stock_uom,
+				frappe.bold(self.item_code),
+				self.voucher_type,
+				frappe.bold(self.voucher_no),
+				actual_qty,
+				actual_qty - self.available_qty,
+				self.available_qty,
+				self.voucher_qty,
+				voucher_delivered_qty,
+				total_reserved_qty,
+				allowed_qty,
 			)
 			frappe.throw(msg)
 
@@ -509,7 +523,6 @@
 	"""Returns `Available Qty to Reserve (Actual Qty - Reserved Qty)` for Item, Warehouse and Batch combination."""
 
 	from erpnext.stock.doctype.batch.batch import get_batch_qty
-	from erpnext.stock.utils import get_stock_balance
 
 	if batch_no:
 		return get_batch_qty(
diff --git a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py
index 176a215..7f2608e 100644
--- a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py
+++ b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py
@@ -4,7 +4,7 @@
 
 import frappe
 from frappe import _
-from frappe.utils import cint, flt, getdate
+from frappe.utils import cint, flt, get_table_name, getdate
 from frappe.utils.deprecations import deprecated
 from pypika import functions as fn
 
@@ -13,11 +13,22 @@
 SLE_COUNT_LIMIT = 10_000
 
 
+def _estimate_table_row_count(doctype: str):
+	table = get_table_name(doctype)
+	return cint(
+		frappe.db.sql(
+			f"""select table_rows
+			   from  information_schema.tables
+			   where table_name = '{table}' ;"""
+		)[0][0]
+	)
+
+
 def execute(filters=None):
 	if not filters:
 		filters = {}
 
-	sle_count = frappe.db.count("Stock Ledger Entry")
+	sle_count = _estimate_table_row_count("Stock Ledger Entry")
 
 	if sle_count > SLE_COUNT_LIMIT and not filters.get("item_code") and not filters.get("warehouse"):
 		frappe.throw(_("Please select either the Item or Warehouse filter to generate the report."))
diff --git a/erpnext/stock/report/reserved_stock/reserved_stock.js b/erpnext/stock/report/reserved_stock/reserved_stock.js
index 6872741..2b075e2 100644
--- a/erpnext/stock/report/reserved_stock/reserved_stock.js
+++ b/erpnext/stock/report/reserved_stock/reserved_stock.js
@@ -149,34 +149,36 @@
 	formatter: (value, row, column, data, default_formatter) => {
 		value = default_formatter(value, row, column, data);
 
-		if (column.fieldname == "status") {
-			switch (data.status) {
-				case "Partially Reserved":
-					value = "<span style='color:orange'>" + value + "</span>";
-					break;
-				case "Reserved":
-					value = "<span style='color:blue'>" + value + "</span>";
-					break;
-				case "Partially Delivered":
-					value = "<span style='color:purple'>" + value + "</span>";
-					break;
-				case "Delivered":
-					value = "<span style='color:green'>" + value + "</span>";
-					break;
+		if (data) {
+			if (column.fieldname == "status") {
+				switch (data.status) {
+					case "Partially Reserved":
+						value = "<span style='color:orange'>" + value + "</span>";
+						break;
+					case "Reserved":
+						value = "<span style='color:blue'>" + value + "</span>";
+						break;
+					case "Partially Delivered":
+						value = "<span style='color:purple'>" + value + "</span>";
+						break;
+					case "Delivered":
+						value = "<span style='color:green'>" + value + "</span>";
+						break;
+				}
 			}
-		}
-		else if (column.fieldname == "delivered_qty") {
-			if (data.delivered_qty > 0) {
-				if (data.reserved_qty > data.delivered_qty) {
-					value = "<span style='color:blue'>" + value + "</span>";
+			else if (column.fieldname == "delivered_qty") {
+				if (data.delivered_qty > 0) {
+					if (data.reserved_qty > data.delivered_qty) {
+						value = "<span style='color:blue'>" + value + "</span>";
+					}
+					else {
+						value = "<span style='color:green'>" + value + "</span>";
+					}
 				}
 				else {
-					value = "<span style='color:green'>" + value + "</span>";
+					value = "<span style='color:red'>" + value + "</span>";
 				}
 			}
-			else {
-				value = "<span style='color:red'>" + value + "</span>";
-			}
 		}
 
 		return value;
diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py
index 0c18792..a1874b8 100644
--- a/erpnext/stock/serial_batch_bundle.py
+++ b/erpnext/stock/serial_batch_bundle.py
@@ -218,15 +218,16 @@
 			).validate_serial_and_batch_inventory()
 
 	def post_process(self):
-		if not self.sle.serial_and_batch_bundle:
+		if not self.sle.serial_and_batch_bundle and not self.sle.serial_no and not self.sle.batch_no:
 			return
 
-		docstatus = frappe.get_cached_value(
-			"Serial and Batch Bundle", self.sle.serial_and_batch_bundle, "docstatus"
-		)
+		if self.sle.serial_and_batch_bundle:
+			docstatus = frappe.get_cached_value(
+				"Serial and Batch Bundle", self.sle.serial_and_batch_bundle, "docstatus"
+			)
 
-		if docstatus != 1:
-			self.submit_serial_and_batch_bundle()
+			if docstatus != 1:
+				self.submit_serial_and_batch_bundle()
 
 		if self.item_details.has_serial_no == 1:
 			self.set_warehouse_and_status_in_serial_nos()
@@ -249,7 +250,12 @@
 		doc.submit()
 
 	def set_warehouse_and_status_in_serial_nos(self):
+		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos as get_parsed_serial_nos
+
 		serial_nos = get_serial_nos(self.sle.serial_and_batch_bundle)
+		if not self.sle.serial_and_batch_bundle and self.sle.serial_no:
+			serial_nos = get_parsed_serial_nos(self.sle.serial_no)
+
 		warehouse = self.warehouse if self.sle.actual_qty > 0 else None
 
 		if not serial_nos:
@@ -263,7 +269,14 @@
 		(
 			frappe.qb.update(sn_table)
 			.set(sn_table.warehouse, warehouse)
-			.set(sn_table.status, "Active" if warehouse else status)
+			.set(
+				sn_table.status,
+				"Active"
+				if warehouse
+				else status
+				if (sn_table.purchase_document_no != self.sle.voucher_no and self.sle.is_cancelled != 1)
+				else "Inactive",
+			)
 			.where(sn_table.name.isin(serial_nos))
 		).run()
 
@@ -290,6 +303,8 @@
 		from erpnext.stock.doctype.batch.batch import get_available_batches
 
 		batches = get_batch_nos(self.sle.serial_and_batch_bundle)
+		if not self.sle.serial_and_batch_bundle and self.sle.batch_no:
+			batches = frappe._dict({self.sle.batch_no: self.sle.actual_qty})
 
 		batches_qty = get_available_batches(
 			frappe._dict(
@@ -312,13 +327,35 @@
 	if serial_nos:
 		filters["serial_no"] = ("in", serial_nos)
 
-	entries = frappe.get_all("Serial and Batch Entry", fields=["serial_no"], filters=filters)
+	entries = frappe.get_all(
+		"Serial and Batch Entry", fields=["serial_no"], filters=filters, order_by="idx"
+	)
 	if not entries:
 		return []
 
 	return [d.serial_no for d in entries if d.serial_no]
 
 
+def get_batches_from_bundle(serial_and_batch_bundle, batches=None):
+	if not serial_and_batch_bundle:
+		return []
+
+	filters = {"parent": serial_and_batch_bundle, "batch_no": ("is", "set")}
+	if isinstance(serial_and_batch_bundle, list):
+		filters = {"parent": ("in", serial_and_batch_bundle)}
+
+	if batches:
+		filters["batch_no"] = ("in", batches)
+
+	entries = frappe.get_all(
+		"Serial and Batch Entry", fields=["batch_no", "qty"], filters=filters, order_by="idx", as_list=1
+	)
+	if not entries:
+		return frappe._dict({})
+
+	return frappe._dict(entries)
+
+
 def get_serial_nos_from_bundle(serial_and_batch_bundle, serial_nos=None):
 	return get_serial_nos(serial_and_batch_bundle, serial_nos=serial_nos)
 
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 9203f45..a6206ac 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -25,6 +25,7 @@
 )
 from erpnext.stock.utils import (
 	get_incoming_outgoing_rate_for_cancel,
+	get_incoming_rate,
 	get_or_make_bin,
 	get_stock_balance,
 	get_valuation_method,
@@ -841,14 +842,33 @@
 					get_rate_for_return,  # don't move this import to top
 				)
 
-				rate = get_rate_for_return(
-					sle.voucher_type,
-					sle.voucher_no,
-					sle.item_code,
-					voucher_detail_no=sle.voucher_detail_no,
-					sle=sle,
-				)
+				if self.valuation_method == "Moving Average":
+					rate = get_incoming_rate(
+						{
+							"item_code": sle.item_code,
+							"warehouse": sle.warehouse,
+							"posting_date": sle.posting_date,
+							"posting_time": sle.posting_time,
+							"qty": sle.actual_qty,
+							"serial_no": sle.get("serial_no"),
+							"batch_no": sle.get("batch_no"),
+							"serial_and_batch_bundle": sle.get("serial_and_batch_bundle"),
+							"company": sle.company,
+							"voucher_type": sle.voucher_type,
+							"voucher_no": sle.voucher_no,
+							"allow_zero_valuation": self.allow_zero_rate,
+							"sle": sle.name,
+						}
+					)
 
+				else:
+					rate = get_rate_for_return(
+						sle.voucher_type,
+						sle.voucher_no,
+						sle.item_code,
+						voucher_detail_no=sle.voucher_detail_no,
+						sle=sle,
+					)
 			elif (
 				sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"]
 				and sle.voucher_detail_no
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
index 1a5deb6..52bf13c 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
@@ -167,13 +167,13 @@
 		)
 		self.update_status_updater_args()
 		self.update_prevdoc_status()
-		self.delete_auto_created_batches()
 		self.set_consumed_qty_in_subcontract_order()
 		self.set_subcontracting_order_status()
 		self.update_stock_ledger()
 		self.make_gl_entries_on_cancel()
 		self.repost_future_sle_and_gle()
 		self.update_status()
+		self.delete_auto_created_batches()
 
 	def validate_items_qty(self):
 		for item in self.items:
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py
index 1d007fe..9d7be36 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py
@@ -365,24 +365,17 @@
 		fg_warehouse_ac = get_inventory_account(scr.company, scr.items[0].warehouse)
 		supplier_warehouse_ac = get_inventory_account(scr.company, scr.supplier_warehouse)
 		expense_account = scr.items[0].expense_account
+		expected_values = [
+			[fg_warehouse_ac, 2100.0, 0.0],  # FG Amount (D)
+			[supplier_warehouse_ac, 0.0, 1000.0],  # RM Cost (C)
+			[additional_costs_expense_account, 0.0, 100.0],  # Additional Cost (C)
+			[expense_account, 0.0, 1000.0],  # Service Cost (C)
+		]
 
-		if fg_warehouse_ac == supplier_warehouse_ac:
-			expected_values = {
-				fg_warehouse_ac: [2100.0, 1000.0],  # FG Amount (D), RM Cost (C)
-				expense_account: [0.0, 1000.0],  # Service Cost (C)
-				additional_costs_expense_account: [0.0, 100.0],  # Additional Cost (C)
-			}
-		else:
-			expected_values = {
-				fg_warehouse_ac: [2100.0, 0.0],  # FG Amount (D)
-				supplier_warehouse_ac: [0.0, 1000.0],  # RM Cost (C)
-				expense_account: [0.0, 1000.0],  # Service Cost (C)
-				additional_costs_expense_account: [0.0, 100.0],  # Additional Cost (C)
-			}
-
-		for gle in gl_entries:
-			self.assertEqual(expected_values[gle.account][0], gle.debit)
-			self.assertEqual(expected_values[gle.account][1], gle.credit)
+		for i in range(len(expected_values)):
+			self.assertEqual(expected_values[i][0], gl_entries[i]["account"])
+			self.assertEqual(expected_values[i][1], gl_entries[i]["debit"])
+			self.assertEqual(expected_values[i][2], gl_entries[i]["credit"])
 
 		scr.reload()
 		scr.cancel()
@@ -953,6 +946,91 @@
 
 		scr.submit()
 
+	def test_subcontracting_receipt_cancel_with_batch(self):
+		from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
+
+		# Step - 1: Set Backflush Based On as "BOM"
+		set_backflush_based_on("BOM")
+
+		# Step - 2: Create FG and RM Items
+		fg_item = make_item(
+			properties={"is_stock_item": 1, "is_sub_contracted_item": 1, "has_batch_no": 1}
+		).name
+		rm_item1 = make_item(properties={"is_stock_item": 1}).name
+		rm_item2 = make_item(properties={"is_stock_item": 1}).name
+		make_item("Subcontracted Service Item Test For Batch 1", {"is_stock_item": 0})
+
+		# Step - 3: Create BOM for FG Item
+		bom = make_bom(item=fg_item, raw_materials=[rm_item1, rm_item2])
+		for rm_item in bom.items:
+			self.assertEqual(rm_item.rate, 0)
+			self.assertEqual(rm_item.amount, 0)
+		bom = bom.name
+
+		# Step - 4: Create PO and SCO
+		service_items = [
+			{
+				"warehouse": "_Test Warehouse - _TC",
+				"item_code": "Subcontracted Service Item Test For Batch 1",
+				"qty": 100,
+				"rate": 100,
+				"fg_item": fg_item,
+				"fg_item_qty": 100,
+			},
+		]
+		sco = get_subcontracting_order(service_items=service_items)
+		for rm_item in sco.supplied_items:
+			self.assertEqual(rm_item.rate, 0)
+			self.assertEqual(rm_item.amount, 0)
+
+		# Step - 5: Inward Raw Materials
+		rm_items = get_rm_items(sco.supplied_items)
+		for rm_item in rm_items:
+			rm_item["rate"] = 100
+		itemwise_details = make_stock_in_entry(rm_items=rm_items)
+
+		# Step - 6: Transfer RM's to Subcontractor
+		se = make_stock_transfer_entry(
+			sco_no=sco.name,
+			rm_items=rm_items,
+			itemwise_details=copy.deepcopy(itemwise_details),
+		)
+		for item in se.items:
+			self.assertEqual(item.qty, 100)
+			self.assertEqual(item.basic_rate, 100)
+			self.assertEqual(item.amount, item.qty * item.basic_rate)
+
+		batch_doc = frappe.get_doc(
+			{
+				"doctype": "Batch",
+				"item": fg_item,
+				"batch_id": frappe.generate_hash(length=10),
+			}
+		).insert(ignore_permissions=True)
+
+		serial_batch_bundle = frappe.get_doc(
+			{
+				"doctype": "Serial and Batch Bundle",
+				"item_code": fg_item,
+				"warehouse": sco.items[0].warehouse,
+				"has_batch_no": 1,
+				"type_of_transaction": "Inward",
+				"voucher_type": "Subcontracting Receipt",
+				"entries": [{"batch_no": batch_doc.name, "qty": 100}],
+			}
+		).insert(ignore_permissions=True)
+
+		# Step - 7: Create Subcontracting Receipt
+		scr = make_subcontracting_receipt(sco.name)
+		scr.items[0].serial_and_batch_bundle = serial_batch_bundle.name
+		scr.save()
+		scr.submit()
+		scr.load_from_db()
+
+		# Step - 8: Cancel Subcontracting Receipt
+		scr.cancel()
+		self.assertTrue(scr.docstatus == 2)
+
 	@change_settings("Buying Settings", {"auto_create_purchase_receipt": 1})
 	def test_auto_create_purchase_receipt(self):
 		fg_item = "Subcontracted Item SA1"
diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js
index f96823b..9f91dc1 100644
--- a/erpnext/support/doctype/issue/issue.js
+++ b/erpnext/support/doctype/issue/issue.js
@@ -58,7 +58,9 @@
 
 				frappe.call("erpnext.support.doctype.service_level_agreement.service_level_agreement.reset_service_level_agreement", {
 					reason: values.reason,
-					user: frappe.session.user_email
+					user: frappe.session.user_email,
+					doctype: frm.doc.doctype,
+					docname: frm.doc.name,
 				}, () => {
 					reset_sla.enable_primary_action();
 					frm.refresh();
diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
index 8b37c94..f6b3a13 100644
--- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
+++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py
@@ -774,10 +774,12 @@
 	return priority
 
 
-def reset_service_level_agreement(doc, reason, user):
+@frappe.whitelist()
+def reset_service_level_agreement(doctype: str, docname: str, reason, user):
 	if not frappe.db.get_single_value("Support Settings", "allow_resetting_service_level_agreement"):
 		frappe.throw(_("Allow Resetting Service Level Agreement from Support Settings."))
 
+	doc = frappe.get_doc(doctype, docname)
 	frappe.get_doc(
 		{
 			"doctype": "Comment",
diff --git a/erpnext/tests/test_perf.py b/erpnext/tests/test_perf.py
new file mode 100644
index 0000000..fc17b1d
--- /dev/null
+++ b/erpnext/tests/test_perf.py
@@ -0,0 +1,24 @@
+import frappe
+from frappe.tests.utils import FrappeTestCase
+
+INDEXED_FIELDS = {
+	"Bin": ["item_code"],
+	"GL Entry": ["voucher_type", "against_voucher_type"],
+	"Purchase Order Item": ["item_code"],
+	"Stock Ledger Entry": ["warehouse"],
+}
+
+
+class TestPerformance(FrappeTestCase):
+	def test_ensure_indexes(self):
+		# These fields are not explicitly indexed BUT they are prefix in some
+		# other composite index. If those are removed this test should be
+		# updated accordingly.
+		for doctype, fields in INDEXED_FIELDS.items():
+			for field in fields:
+				self.assertTrue(
+					frappe.db.sql(
+						f"""SHOW INDEX FROM `tab{doctype}`
+						WHERE Column_name = "{field}" AND Seq_in_index = 1"""
+					)
+				)
diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv
index 2745d4d..d05d0d9 100644
--- a/erpnext/translations/de.csv
+++ b/erpnext/translations/de.csv
@@ -103,6 +103,7 @@
 Actual Qty {0} / Waiting Qty {1},Tatsächliche Menge {0} / Wartezeit {1},
 Actual Qty: Quantity available in the warehouse.,Tatsächliche Menge: Menge verfügbar im Lager.,
 Actual qty in stock,Tatsächliche Menge auf Lager,
+Actual Time (in Hours via Time Sheet), IST Zeit (in Stunden aus Zeiterfassung),
 Actual type tax cannot be included in Item rate in row {0},Tatsächliche Steuerart kann nicht im Artikelpreis in Zeile {0} beinhaltet sein,
 Add,Hinzufügen,
 Add / Edit Prices,Preise hinzufügen / bearbeiten,