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)
)