fix: company wise deferred accounting fields in item (#37023)

* fix: move deferred accounts in accounting section

* fix: move deferred check boxes in item accounting

* fix: show company wise acc in filters

* fix: fetch item deferred account from child table

* fix: tests using deferred acc

* refactor: use cached value

* fix: cached value call

* feat: patch to migrate deferred acc

* fix: hardcode education module doctypes in patch

* chore: resolve conflicts

---------

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index ce7ada3..b4dd75a 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -1164,7 +1164,7 @@
 
 		item = create_item("_Test Item for Deferred Accounting", is_purchase_item=True)
 		item.enable_deferred_expense = 1
-		item.deferred_expense_account = deferred_account
+		item.item_defaults[0].deferred_expense_account = deferred_account
 		item.save()
 
 		pi = make_purchase_invoice(item=item.name, qty=1, rate=100, do_not_save=True)
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 21b39d7..9ffdaf6 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -2322,7 +2322,7 @@
 
 		item = create_item("_Test Item for Deferred Accounting")
 		item.enable_deferred_revenue = 1
-		item.deferred_revenue_account = deferred_account
+		item.item_defaults[0].deferred_revenue_account = deferred_account
 		item.no_of_months = 12
 		item.save()
 
@@ -3102,7 +3102,7 @@
 
 		item = create_item("_Test Item for Deferred Accounting")
 		item.enable_deferred_expense = 1
-		item.deferred_revenue_account = deferred_account
+		item.item_defaults[0].deferred_revenue_account = deferred_account
 		item.save()
 
 		si = create_sales_invoice(
diff --git a/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py b/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py
index 28d0c20..7b1a902 100644
--- a/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py
+++ b/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py
@@ -81,7 +81,7 @@
 		self.create_item("_Test Internet Subscription", 0, self.warehouse, self.company)
 		item = frappe.get_doc("Item", self.item)
 		item.enable_deferred_revenue = 1
-		item.deferred_revenue_account = self.deferred_revenue_account
+		item.item_defaults[0].deferred_revenue_account = self.deferred_revenue_account
 		item.no_of_months = 3
 		item.save()
 
@@ -150,7 +150,7 @@
 		self.create_item("_Test Office Desk", 0, self.warehouse, self.company)
 		item = frappe.get_doc("Item", self.item)
 		item.enable_deferred_expense = 1
-		item.deferred_expense_account = self.deferred_expense_account
+		item.item_defaults[0].deferred_expense_account = self.deferred_expense_account
 		item.no_of_months_exp = 3
 		item.save()
 
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index d0ee2e4..dda4d36 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -341,5 +341,6 @@
 execute:frappe.db.set_single_value('Selling Settings', 'allow_negative_rates_for_items', 0)
 erpnext.patches.v15_0.correct_asset_value_if_je_with_workflow
 erpnext.patches.v15_0.delete_woocommerce_settings_doctype
+erpnext.patches.v14_0.migrate_deferred_accounts_to_item_defaults
 # below migration patch should always run last
 erpnext.patches.v14_0.migrate_gl_to_payment_ledger
diff --git a/erpnext/patches/v14_0/delete_education_doctypes.py b/erpnext/patches/v14_0/delete_education_doctypes.py
index 56a596a..aeeda70 100644
--- a/erpnext/patches/v14_0/delete_education_doctypes.py
+++ b/erpnext/patches/v14_0/delete_education_doctypes.py
@@ -47,13 +47,16 @@
 	for doctype in doctypes:
 		frappe.delete_doc("DocType", doctype, ignore_missing=True)
 
-	portal_settings = frappe.get_doc("Portal Settings")
-
-	for row in portal_settings.get("menu"):
-		if row.reference_doctype in doctypes:
-			row.delete()
-
-	portal_settings.save()
+	titles = [
+		"Fees",
+		"Student Admission",
+		"Grant Application",
+		"Chapter",
+		"Certification Application",
+	]
+	items = frappe.get_all("Portal Menu Item", filters=[["title", "in", titles]], pluck="name")
+	for item in items:
+		frappe.delete_doc("Portal Menu Item", item, ignore_missing=True, force=True)
 
 	frappe.delete_doc("Module Def", "Education", ignore_missing=True, force=True)
 
diff --git a/erpnext/patches/v14_0/delete_healthcare_doctypes.py b/erpnext/patches/v14_0/delete_healthcare_doctypes.py
index 2c699e4..896a440 100644
--- a/erpnext/patches/v14_0/delete_healthcare_doctypes.py
+++ b/erpnext/patches/v14_0/delete_healthcare_doctypes.py
@@ -41,7 +41,7 @@
 	for card in cards:
 		frappe.delete_doc("Number Card", card, ignore_missing=True, force=True)
 
-	titles = ["Lab Test", "Prescription", "Patient Appointment"]
+	titles = ["Lab Test", "Prescription", "Patient Appointment", "Patient"]
 	items = frappe.get_all("Portal Menu Item", filters=[["title", "in", titles]], pluck="name")
 	for item in items:
 		frappe.delete_doc("Portal Menu Item", item, ignore_missing=True, force=True)
diff --git a/erpnext/patches/v14_0/migrate_deferred_accounts_to_item_defaults.py b/erpnext/patches/v14_0/migrate_deferred_accounts_to_item_defaults.py
new file mode 100644
index 0000000..44b830b
--- /dev/null
+++ b/erpnext/patches/v14_0/migrate_deferred_accounts_to_item_defaults.py
@@ -0,0 +1,39 @@
+import frappe
+
+
+def execute():
+	try:
+		item_dict = get_deferred_accounts()
+		add_to_item_defaults(item_dict)
+	except Exception:
+		frappe.db.rollback()
+		frappe.log_error("Failed to migrate deferred accounts in Item Defaults.")
+
+
+def get_deferred_accounts():
+	item = frappe.qb.DocType("Item")
+	return (
+		frappe.qb.from_(item)
+		.select(item.name, item.deferred_expense_account, item.deferred_revenue_account)
+		.where((item.enable_deferred_expense == 1) | (item.enable_deferred_revenue == 1))
+		.run(as_dict=True)
+	)
+
+
+def add_to_item_defaults(item_dict):
+	for item in item_dict:
+		add_company_wise_item_default(item, "deferred_expense_account")
+		add_company_wise_item_default(item, "deferred_revenue_account")
+
+
+def add_company_wise_item_default(item, account_type):
+	company = frappe.get_cached_value("Account", item[account_type], "company")
+	if company and item[account_type]:
+		item_defaults = frappe.get_cached_value("Item", item["name"], "item_defaults")
+		for item_row in item_defaults:
+			if item_row.company == company:
+				frappe.set_value("Item Default", item_row.name, account_type, item[account_type])
+				break
+		else:
+			item_defaults.append({"company": company, account_type: item[account_type]})
+			frappe.set_value("Item", item["name"], "item_defaults", item_defaults)
diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js
index 76e8866..4ae9bf5 100644
--- a/erpnext/stock/doctype/item/item.js
+++ b/erpnext/stock/doctype/item/item.js
@@ -350,18 +350,20 @@
 			}
 		}
 
-		frm.fields_dict['deferred_revenue_account'].get_query = function() {
+		frm.fields_dict["item_defaults"].grid.get_field("deferred_revenue_account").get_query = function(doc, cdt, cdn) {
 			return {
 				filters: {
+					"company": locals[cdt][cdn].company,
 					'root_type': 'Liability',
 					"is_group": 0
 				}
 			}
 		}
 
-		frm.fields_dict['deferred_expense_account'].get_query = function() {
+		frm.fields_dict["item_defaults"].grid.get_field("deferred_expense_account").get_query = function(doc, cdt, cdn) {
 			return {
 				filters: {
+					"company": locals[cdt][cdn].company,
 					'root_type': 'Asset',
 					"is_group": 0
 				}
diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json
index 756d004..1bcddfa 100644
--- a/erpnext/stock/doctype/item/item.json
+++ b/erpnext/stock/doctype/item/item.json
@@ -69,6 +69,13 @@
   "variant_based_on",
   "attributes",
   "accounting",
+  "deferred_accounting_section",
+  "enable_deferred_expense",
+  "no_of_months_exp",
+  "column_break_9s9o",
+  "enable_deferred_revenue",
+  "no_of_months",
+  "section_break_avcp",
   "item_defaults",
   "purchasing_tab",
   "purchase_uom",
@@ -84,10 +91,6 @@
   "delivered_by_supplier",
   "column_break2",
   "supplier_items",
-  "deferred_expense_section",
-  "enable_deferred_expense",
-  "deferred_expense_account",
-  "no_of_months_exp",
   "foreign_trade_details",
   "country_of_origin",
   "column_break_59",
@@ -98,10 +101,6 @@
   "is_sales_item",
   "column_break3",
   "max_discount",
-  "deferred_revenue",
-  "enable_deferred_revenue",
-  "deferred_revenue_account",
-  "no_of_months",
   "customer_details",
   "customer_items",
   "item_tax_section_break",
@@ -658,20 +657,6 @@
    "oldfieldtype": "Currency"
   },
   {
-   "collapsible": 1,
-   "fieldname": "deferred_revenue",
-   "fieldtype": "Section Break",
-   "label": "Deferred Revenue"
-  },
-  {
-   "depends_on": "enable_deferred_revenue",
-   "fieldname": "deferred_revenue_account",
-   "fieldtype": "Link",
-   "ignore_user_permissions": 1,
-   "label": "Deferred Revenue Account",
-   "options": "Account"
-  },
-  {
    "default": "0",
    "fieldname": "enable_deferred_revenue",
    "fieldtype": "Check",
@@ -681,21 +666,7 @@
    "depends_on": "enable_deferred_revenue",
    "fieldname": "no_of_months",
    "fieldtype": "Int",
-   "label": "No of Months"
-  },
-  {
-   "collapsible": 1,
-   "fieldname": "deferred_expense_section",
-   "fieldtype": "Section Break",
-   "label": "Deferred Expense"
-  },
-  {
-   "depends_on": "enable_deferred_expense",
-   "fieldname": "deferred_expense_account",
-   "fieldtype": "Link",
-   "ignore_user_permissions": 1,
-   "label": "Deferred Expense Account",
-   "options": "Account"
+   "label": "No of Months (Revenue)"
   },
   {
    "default": "0",
@@ -904,6 +875,20 @@
    "fieldname": "accounting",
    "fieldtype": "Tab Break",
    "label": "Accounting"
+  },
+  {
+   "fieldname": "column_break_9s9o",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "section_break_avcp",
+   "fieldtype": "Section Break"
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "deferred_accounting_section",
+   "fieldtype": "Section Break",
+   "label": "Deferred Accounting"
   }
  ],
  "icon": "fa fa-tag",
@@ -912,7 +897,7 @@
  "index_web_pages_for_search": 1,
  "links": [],
  "make_attachments_public": 1,
- "modified": "2023-08-28 22:16:40.305094",
+ "modified": "2023-09-11 13:46:32.688051",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Item",
diff --git a/erpnext/stock/doctype/item_default/item_default.json b/erpnext/stock/doctype/item_default/item_default.json
index 042d398..2895661 100644
--- a/erpnext/stock/doctype/item_default/item_default.json
+++ b/erpnext/stock/doctype/item_default/item_default.json
@@ -19,7 +19,11 @@
   "selling_defaults",
   "selling_cost_center",
   "column_break_12",
-  "income_account"
+  "income_account",
+  "deferred_accounting_defaults_section",
+  "deferred_expense_account",
+  "column_break_kwad",
+  "deferred_revenue_account"
  ],
  "fields": [
   {
@@ -108,11 +112,34 @@
    "fieldtype": "Link",
    "label": "Default Provisional Account",
    "options": "Account"
+  },
+  {
+   "fieldname": "deferred_accounting_defaults_section",
+   "fieldtype": "Section Break",
+   "label": "Deferred Accounting Defaults"
+  },
+  {
+   "depends_on": "eval: parent.enable_deferred_expense",
+   "fieldname": "deferred_expense_account",
+   "fieldtype": "Link",
+   "label": "Deferred Expense Account",
+   "options": "Account"
+  },
+  {
+   "depends_on": "eval: parent.enable_deferred_revenue",
+   "fieldname": "deferred_revenue_account",
+   "fieldtype": "Link",
+   "label": "Deferred Revenue Account",
+   "options": "Account"
+  },
+  {
+   "fieldname": "column_break_kwad",
+   "fieldtype": "Column Break"
   }
  ],
  "istable": 1,
  "links": [],
- "modified": "2022-04-10 20:18:54.148195",
+ "modified": "2023-09-04 12:33:14.607267",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Item Default",
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 4f85ac0..79e6488 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -696,7 +696,11 @@
 def get_default_deferred_account(args, item, fieldname=None):
 	if item.get("enable_deferred_revenue") or item.get("enable_deferred_expense"):
 		return (
-			item.get(fieldname)
+			frappe.get_cached_value(
+				"Item Default",
+				{"parent": args.item_code, "company": args.get("company")},
+				fieldname,
+			)
 			or args.get(fieldname)
 			or frappe.get_cached_value("Company", args.company, "default_" + fieldname)
 		)