From 58db5960271285e20cde25304c45c0f4b98e8319 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Fri, 30 May 2025 14:44:27 +0530 Subject: [PATCH 01/22] feat: move frozen account settings to Company for company-specific configuration --- erpnext/setup/doctype/company/company.json | 33 ++++++++++++++++++++++ erpnext/setup/doctype/company/company.py | 2 ++ 2 files changed, 35 insertions(+) diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 4fe721f5628..c7d6b4b7481 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -95,6 +95,11 @@ "depreciation_cost_center", "capital_work_in_progress_account", "asset_received_but_not_billed", + "accounts_closing_tab", + "accounts_closing_section", + "accounts_frozen_till_date", + "column_break_tawz", + "role_allowed_for_frozen_entries", "buying_and_selling_tab", "sales_settings", "default_buying_terms", @@ -930,8 +935,36 @@ "fieldtype": "Link", "label": "Default Sales Contact", "options": "Contact" + }, + { + "description": "Accounting entries are frozen up to this date. Only users with the specified role can create or modify entries before this date.", + "fieldname": "accounts_frozen_till_date", + "fieldtype": "Date", + "in_list_view": 1, + "label": "Accounts Frozen Till Date" + }, + { + "fieldname": "accounts_closing_tab", + "fieldtype": "Tab Break", + "label": "Accounts Closing" + }, + { + "fieldname": "column_break_tawz", + "fieldtype": "Column Break" + }, + { + "fieldname": "role_allowed_for_frozen_entries", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Roles Allowed to Set and Edit Frozen Account Entries", + "options": "Role" + }, + { + "fieldname": "accounts_closing_section", + "fieldtype": "Section Break" } ], + "grid_page_length": 50, "icon": "fa fa-building", "idx": 1, "image_field": "company_logo", diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index abd6bac0ca4..a94a62bfba7 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -31,6 +31,7 @@ class Company(NestedSet): from frappe.types import DF abbr: DF.Data + accounts_frozen_till_date: DF.Date | None accumulated_depreciation_account: DF.Link | None allow_account_creation_against_child_company: DF.Check asset_received_but_not_billed: DF.Link | None @@ -103,6 +104,7 @@ class Company(NestedSet): registration_details: DF.Code | None reporting_currency: DF.Link | None rgt: DF.Int + role_allowed_for_frozen_entries: DF.Link | None round_off_account: DF.Link | None round_off_cost_center: DF.Link | None round_off_for_opening: DF.Link | None From 17a6392407228432002ea6443976932d7fd71fba Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Tue, 10 Jun 2025 16:01:51 +0530 Subject: [PATCH 02/22] refactor: updated logic in depreciation and gl to validate acc frozen date company wise --- erpnext/accounts/general_ledger.py | 16 ++++----- erpnext/assets/doctype/asset/depreciation.py | 37 ++++++++++---------- 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index a74b982754f..73c45f31ad1 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -406,7 +406,7 @@ def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False): dimension_filter_map = get_dimension_filter_map() if gl_map: - check_freezing_date(gl_map[0]["posting_date"], adv_adj) + check_freezing_date(gl_map[0]["posting_date"], gl_map[0]["company"], adv_adj) is_opening = any(d.get("is_opening") == "Yes" for d in gl_map) if gl_map[0]["voucher_type"] != "Period Closing Voucher": validate_against_pcv(is_opening, gl_map[0]["posting_date"], gl_map[0]["company"]) @@ -767,7 +767,7 @@ def make_reverse_gl_entries( make_entry(new_gle, adv_adj, "Yes") -def check_freezing_date(posting_date, adv_adj=False): +def check_freezing_date(posting_date, company, adv_adj=False): """ Nobody can do GL Entries where posting date is before freezing date except authorized person @@ -776,17 +776,17 @@ def check_freezing_date(posting_date, adv_adj=False): Hence stop admin to bypass if accounts are freezed """ if not adv_adj: - acc_frozen_upto = frappe.get_single_value("Accounts Settings", "acc_frozen_upto") - if acc_frozen_upto: - frozen_accounts_modifier = frappe.get_single_value( - "Accounts Settings", "frozen_accounts_modifier" + acc_frozen_till_date = frappe.db.get_value("Company", company, "accounts_frozen_till_date") + if acc_frozen_till_date: + frozen_accounts_modifier = frappe.db.get_value( + "Company", company, "role_allowed_for_frozen_entries" ) - if getdate(posting_date) <= getdate(acc_frozen_upto) and ( + if getdate(posting_date) <= getdate(acc_frozen_till_date) and ( frozen_accounts_modifier not in frappe.get_roles() or frappe.session.user == "Administrator" ): frappe.throw( _("You are not authorized to add or update entries before {0}").format( - formatdate(acc_frozen_upto) + formatdate(acc_frozen_till_date) ) ) diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index 25148dc0a18..1de2762fa0e 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -96,13 +96,26 @@ def get_depreciable_assets_data(date): .orderby(a.creation, order=Order.desc) ) - acc_frozen_upto = get_acc_frozen_upto() - if acc_frozen_upto: - res = res.where(ds.schedule_date > acc_frozen_upto) + companies_with_frozen_limits = get_companies_with_frozen_limits() - res = res.run() + for company, frozen_upto in companies_with_frozen_limits.items(): + res = res.where((a.company != company) | (ds.schedule_date > frozen_upto)) - return res + return res.run() + + +def get_companies_with_frozen_limits(): + companies_with_frozen_limits = {} + for d in frappe.get_all( + "Company", fields=["name", "accounts_frozen_till_date", "role_allowed_for_frozen_entries"] + ): + if not d.accounts_frozen_till_date: + continue + + if d.role_allowed_for_frozen_entries in frappe.get_roles() or frappe.session.user == "Administrator": + continue + companies_with_frozen_limits[d.name] = getdate(d.accounts_frozen_till_date) + return companies_with_frozen_limits def make_depreciation_entry_on_disposal(asset_doc, disposal_date=None): @@ -111,20 +124,6 @@ def make_depreciation_entry_on_disposal(asset_doc, disposal_date=None): make_depreciation_entry(depr_schedule_name, disposal_date) -def get_acc_frozen_upto(): - acc_frozen_upto = frappe.get_single_value("Accounts Settings", "acc_frozen_upto") - - if not acc_frozen_upto: - return - - frozen_accounts_modifier = frappe.get_single_value("Accounts Settings", "frozen_accounts_modifier") - - if frozen_accounts_modifier not in frappe.get_roles() or frappe.session.user == "Administrator": - return getdate(acc_frozen_upto) - - return - - def get_credit_debit_accounts_for_asset(asset_category, company): # Returns credit and debit accounts for the given asset category and company. (_, accumulated_depr_account, depr_expense_account) = get_depreciation_accounts(asset_category, company) From dc85babb4d29046104c69eef53a0d386094dfd33 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Thu, 12 Jun 2025 16:21:50 +0530 Subject: [PATCH 03/22] refactor: get frozen accounts settings from Company in Deferred Revenue --- erpnext/accounts/deferred_revenue.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py index cb34b172ab4..be723915951 100644 --- a/erpnext/accounts/deferred_revenue.py +++ b/erpnext/accounts/deferred_revenue.py @@ -7,6 +7,7 @@ from frappe.utils import ( cint, date_diff, flt, + formatdate, get_first_day, get_last_day, get_link_to_form, @@ -318,7 +319,7 @@ def get_already_booked_amount(doc, item): def book_deferred_income_or_expense(doc, deferred_process, posting_date=None): enable_check = "enable_deferred_revenue" if doc.doctype == "Sales Invoice" else "enable_deferred_expense" - accounts_frozen_upto = frappe.get_single_value("Accounts Settings", "acc_frozen_upto") + accounts_frozen_upto = frappe.db.get_value("Company", doc.company, "accounts_frozen_till_date") def _book_deferred_revenue_or_expense( item, From d330700f3958485e5aa8e0867c199345e533cf32 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Fri, 13 Jun 2025 14:35:20 +0530 Subject: [PATCH 04/22] refactor: get frozen accounts settings from Company in patches --- .../modify_invalid_gain_loss_gl_entries.py | 49 ++++++++++++------- .../patches/v14_0/single_to_multi_dunning.py | 2 +- .../recalculate_amount_difference_field.py | 6 +-- .../repost_item_valuation.py | 15 +++--- 4 files changed, 45 insertions(+), 27 deletions(-) diff --git a/erpnext/patches/v13_0/modify_invalid_gain_loss_gl_entries.py b/erpnext/patches/v13_0/modify_invalid_gain_loss_gl_entries.py index 8e58d79b928..37dbd728636 100644 --- a/erpnext/patches/v13_0/modify_invalid_gain_loss_gl_entries.py +++ b/erpnext/patches/v13_0/modify_invalid_gain_loss_gl_entries.py @@ -10,15 +10,17 @@ def execute(): purchase_invoices = frappe.db.sql( """ select - parenttype as type, parent as name + PI.company, PI_ADV.parenttype as type, PI_ADV.parent as name from - `tabPurchase Invoice Advance` + `tabPurchase Invoice Advance` as PI_ADV join `tabPurchase Invoice` as PI + on + PI_ADV.parent = PI.name where - ref_exchange_rate = 1 - and docstatus = 1 - and ifnull(exchange_gain_loss, 0) != 0 + PI_ADV.ref_exchange_rate = 1 + and PI_ADV.docstatus = 1 + and ifnull(PI_ADV.exchange_gain_loss, 0) != 0 group by - parent + PI_ADV.parent """, as_dict=1, ) @@ -26,15 +28,17 @@ def execute(): sales_invoices = frappe.db.sql( """ select - parenttype as type, parent as name + SI.company, SI_ADV.parenttype as type, SI_ADV.parent as name from - `tabSales Invoice Advance` + `tabSales Invoice Advance` as SI_ADV join `tabSales Invoice` as SI + on + SI_ADV.parent = SI.name where - ref_exchange_rate = 1 - and docstatus = 1 - and ifnull(exchange_gain_loss, 0) != 0 + SI_ADV.ref_exchange_rate = 1 + and SI_ADV.docstatus = 1 + and ifnull(SI_ADV.exchange_gain_loss, 0) != 0 group by - parent + SI_ADV.parent """, as_dict=1, ) @@ -45,11 +49,21 @@ def execute(): message=json.dumps(purchase_invoices + sales_invoices, indent=2), ) - acc_frozen_upto = frappe.db.get_single_value("Accounts Settings", "acc_frozen_upto") - if acc_frozen_upto: - frappe.db.set_single_value("Accounts Settings", "acc_frozen_upto", None) + original_frozen_dates = {} for invoice in purchase_invoices + sales_invoices: + company = invoice.company + + # Unfreeze only once per company + if company not in original_frozen_dates: + accounts_frozen_till_date = frappe.get_cached_value( + "Company", company, "accounts_frozen_till_date" + ) + original_frozen_dates[company] = accounts_frozen_till_date + + if accounts_frozen_till_date: + frappe.db.set_value("Company", company, "accounts_frozen_till_date", None) + try: doc = frappe.get_doc(invoice.type, invoice.name) doc.docstatus = 2 @@ -64,5 +78,6 @@ def execute(): frappe.db.rollback() print(f"Failed to correct gl entries of {invoice.name}") - if acc_frozen_upto: - frappe.db.set_single_value("Accounts Settings", "acc_frozen_upto", acc_frozen_upto) + for company, frozen_date in original_frozen_dates.items(): + if frozen_date: + frappe.db.set_value("Company", company, "accounts_frozen_till_date", frozen_date) diff --git a/erpnext/patches/v14_0/single_to_multi_dunning.py b/erpnext/patches/v14_0/single_to_multi_dunning.py index 47d13a0e383..108f9c96554 100644 --- a/erpnext/patches/v14_0/single_to_multi_dunning.py +++ b/erpnext/patches/v14_0/single_to_multi_dunning.py @@ -77,4 +77,4 @@ def get_accounts_closing_date(): else: can_edit_accounts_after = accounts_frozen_till or period_closing_date - return can_edit_accounts_after + return can_edit_accounts_after \ No newline at end of file diff --git a/erpnext/patches/v15_0/recalculate_amount_difference_field.py b/erpnext/patches/v15_0/recalculate_amount_difference_field.py index d7b56c206d2..f3cece4c416 100644 --- a/erpnext/patches/v15_0/recalculate_amount_difference_field.py +++ b/erpnext/patches/v15_0/recalculate_amount_difference_field.py @@ -58,9 +58,9 @@ def execute(): ): posting_date = period_closing_voucher[0].period_end_date - acc_frozen_upto = frappe.db.get_single_value("Accounts Settings", "acc_frozen_upto") - if acc_frozen_upto and getdate(acc_frozen_upto) > getdate(posting_date): - posting_date = acc_frozen_upto + acc_frozen_till_date = frappe.db.get_value("Company", company, "accounts_frozen_till_date") + if acc_frozen_till_date and getdate(acc_frozen_till_date) > getdate(posting_date): + posting_date = acc_frozen_till_date stock_frozen_upto = frappe.db.get_single_value("Stock Settings", "stock_frozen_upto") if stock_frozen_upto and getdate(stock_frozen_upto) > getdate(posting_date): diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py index 9ab616c94ca..99f1d3539ec 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py @@ -168,16 +168,19 @@ class RepostItemValuation(Document): return query[0][0] if query and query[0][0] else None def validate_accounts_freeze(self): - acc_settings = frappe.get_cached_doc("Accounts Settings") - if not acc_settings.acc_frozen_upto: + acc_frozen_till_date = frappe.db.get_value("Company", self.company, "accounts_frozen_till_date") + frozen_accounts_modifier = frappe.db.get_value( + "Company", self.company, "role_allowed_for_frozen_entries" + ) + if not acc_frozen_till_date: return - if getdate(self.posting_date) <= getdate(acc_settings.acc_frozen_upto): - if acc_settings.frozen_accounts_modifier and frappe.session.user in get_users_with_role( - acc_settings.frozen_accounts_modifier + if getdate(self.posting_date) <= getdate(acc_frozen_till_date): + if frozen_accounts_modifier and frappe.session.user in get_users_with_role( + frozen_accounts_modifier ): frappe.msgprint(_("Caution: This might alter frozen accounts.")) return - frappe.throw(_("You cannot repost item valuation before {}").format(acc_settings.acc_frozen_upto)) + frappe.throw(_("You cannot repost item valuation before {}").format(acc_frozen_till_date)) def reset_field_values(self): if self.based_on == "Transaction": From 479e412a447d3d96778ca3cfa89ada4844c8caca Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Fri, 13 Jun 2025 16:39:46 +0530 Subject: [PATCH 05/22] refactor: get frozen accounts settings from Company in tests --- erpnext/accounts/doctype/account/account.py | 12 +++++++----- erpnext/accounts/doctype/gl_entry/gl_entry.py | 14 +++++++------- .../payment_ledger_entry/payment_ledger_entry.py | 2 +- .../test_process_deferred_accounting.py | 8 +++++--- .../doctype/sales_invoice/test_sales_invoice.py | 8 +++++--- .../doctype/subscription/test_subscription.py | 2 +- erpnext/accounts/party.py | 8 ++++---- erpnext/controllers/accounts_controller.py | 2 +- 8 files changed, 31 insertions(+), 25 deletions(-) diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index 1e6467e502f..3e32f6b8817 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -93,8 +93,10 @@ class Account(NestedSet): super().on_update() def onload(self): - frozen_accounts_modifier = frappe.get_single_value("Accounts Settings", "frozen_accounts_modifier") - if not frozen_accounts_modifier or frozen_accounts_modifier in frappe.get_roles(): + role_allowed_for_frozen_entries = frappe.db.get_value( + "Company", self.company, "role_allowed_for_frozen_entries" + ) + if not role_allowed_for_frozen_entries or role_allowed_for_frozen_entries in frappe.get_roles(): self.set_onload("can_freeze_account", True) def autoname(self): @@ -303,10 +305,10 @@ class Account(NestedSet): if not doc_before_save or doc_before_save.freeze_account == self.freeze_account: return - frozen_accounts_modifier = frappe.get_cached_value( - "Accounts Settings", "Accounts Settings", "frozen_accounts_modifier" + role_allowed_for_frozen_entries = frappe.get_cached_value( + "Company", self.company, "role_allowed_for_frozen_entries" ) - if not frozen_accounts_modifier or frozen_accounts_modifier not in frappe.get_roles(): + if not role_allowed_for_frozen_entries or role_allowed_for_frozen_entries not in frappe.get_roles(): throw(_("You are not authorized to set Frozen value")) def validate_balance_must_be_debit_or_credit(self): diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index aefe293157b..68ef84a1365 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -100,7 +100,7 @@ class GLEntry(Document): self.validate_account_details(adv_adj) self.validate_dimensions_for_pl_and_bs() validate_balance_type(self.account, adv_adj) - validate_frozen_account(self.account, adv_adj) + validate_frozen_account(self.company, self.account, adv_adj) if ( self.voucher_type == "Journal Entry" @@ -276,7 +276,7 @@ class GLEntry(Document): ) def validate_party(self): - validate_party_frozen_disabled(self.party_type, self.party) + validate_party_frozen_disabled(self.company, self.party_type, self.party) validate_account_party_type(self) def validate_currency(self): @@ -419,16 +419,16 @@ def update_outstanding_amt( ref_doc.set_status(update=True) -def validate_frozen_account(account, adv_adj=None): +def validate_frozen_account(company, account, adv_adj=None): frozen_account = frappe.get_cached_value("Account", account, "freeze_account") if frozen_account == "Yes" and not adv_adj: - frozen_accounts_modifier = frappe.get_cached_value( - "Accounts Settings", None, "frozen_accounts_modifier" + role_allowed_for_frozen_entries = frappe.db_get_cached_value( + "Company", company, "role_allowed_for_frozen_entries" ) - if not frozen_accounts_modifier: + if not role_allowed_for_frozen_entries: frappe.throw(_("Account {0} is frozen").format(account)) - elif frozen_accounts_modifier not in frappe.get_roles(): + elif role_allowed_for_frozen_entries not in frappe.get_roles(): frappe.throw(_("Not authorized to edit frozen Account {0}").format(account)) diff --git a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.py b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.py index 30e885adacc..c1a28d12350 100644 --- a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.py +++ b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.py @@ -159,7 +159,7 @@ class PaymentLedgerEntry(Document): def on_update(self): adv_adj = self.flags.adv_adj if not self.flags.from_repost: - validate_frozen_account(self.account, adv_adj) + validate_frozen_account(self.company, self.account, adv_adj) if not self.delinked: self.validate_account_details() self.validate_dimensions_for_pl_and_bs() diff --git a/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py b/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py index 97fc47b01b9..59764442a4e 100644 --- a/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py +++ b/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py @@ -16,7 +16,7 @@ from erpnext.stock.doctype.item.test_item import create_item class TestProcessDeferredAccounting(IntegrationTestCase): def test_creation_of_ledger_entry_on_submit(self): """test creation of gl entries on submission of document""" - change_acc_settings(acc_frozen_upto="2023-05-31", book_deferred_entries_based_on="Months") + change_acc_settings(acc_frozen_till_date="2023-05-31", book_deferred_entries_based_on="Months") deferred_account = create_account( account_name="Deferred Revenue for Accounts Frozen", @@ -92,8 +92,10 @@ class TestProcessDeferredAccounting(IntegrationTestCase): pda.cancel() -def change_acc_settings(acc_frozen_upto="", book_deferred_entries_based_on="Days"): +def change_acc_settings( + company="_Test Company", acc_frozen_till_date="", book_deferred_entries_based_on="Days" +): acc_settings = frappe.get_doc("Accounts Settings", "Accounts Settings") - acc_settings.acc_frozen_upto = acc_frozen_upto acc_settings.book_deferred_entries_based_on = book_deferred_entries_based_on + frappe.db.set_value("Company", company, "accounts_frozen_till_date", acc_frozen_till_date) acc_settings.save() diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index a2983e4555b..bae0de3f94c 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -63,7 +63,8 @@ class TestSalesInvoice(ERPNextTestSuite): set_default_account_for_mode_of_payment( mode_of_payment, "_Test Company with perpetual inventory", "_Test Bank - TCP1" ) - frappe.db.set_single_value("Accounts Settings", "acc_frozen_upto", None) + for company in frappe.get_all("Company", pluck="name"): + frappe.db.set_value("Company", company, "acc_frozen_till_date", None) @change_settings( "Accounts Settings", @@ -3398,8 +3399,8 @@ class TestSalesInvoice(ERPNextTestSuite): si.commission_rate = commission_rate self.assertRaises(frappe.ValidationError, si.save) - @IntegrationTestCase.change_settings("Accounts Settings", {"acc_frozen_upto": add_days(getdate(), 1)}) def test_sales_invoice_submission_post_account_freezing_date(self): + frappe.db.set_value("Company", "_Test Company", "accounts_frozen_till_date", add_days(getdate(), 1)) si = create_sales_invoice(do_not_save=True) si.posting_date = add_days(getdate(), 1) si.save() @@ -3407,6 +3408,7 @@ class TestSalesInvoice(ERPNextTestSuite): self.assertRaises(frappe.ValidationError, si.submit) si.posting_date = getdate() si.submit() + frappe.db.set_value("Company", "_Test Company", "accounts_frozen_till_date", None) @IntegrationTestCase.change_settings("Accounts Settings", {"over_billing_allowance": 0}) def test_over_billing_case_against_delivery_note(self): @@ -3473,7 +3475,7 @@ class TestSalesInvoice(ERPNextTestSuite): si.save() si.submit() - frappe.db.set_single_value("Accounts Settings", "acc_frozen_upto", getdate("2019-01-31")) + frappe.db.set_value("Company", "_Test Company", "accounts_frozen_till_date", getdate("2019-01-31")) pda1 = frappe.get_doc( dict( diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py index a121db20634..7bb72a1dcd7 100644 --- a/erpnext/accounts/doctype/subscription/test_subscription.py +++ b/erpnext/accounts/doctype/subscription/test_subscription.py @@ -27,7 +27,7 @@ class TestSubscription(IntegrationTestCase): make_plans() create_parties() reset_settings() - frappe.db.set_single_value("Accounts Settings", "acc_frozen_upto", None) + frappe.db.set_value("Company", "_Test Company", "accounts_frozen_till_date", None) def tearDown(self): frappe.db.rollback() diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index eef82a398b9..39f25f5fcab 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -795,7 +795,7 @@ def get_payment_terms_template(party_name, party_type, company=None): return template -def validate_party_frozen_disabled(party_type, party_name): +def validate_party_frozen_disabled(company, party_type, party_name): if frappe.flags.ignore_party_validation: return @@ -805,10 +805,10 @@ def validate_party_frozen_disabled(party_type, party_name): if party.disabled: frappe.throw(_("{0} {1} is disabled").format(party_type, party_name), PartyDisabled) elif party.get("is_frozen"): - frozen_accounts_modifier = frappe.get_single_value( - "Accounts Settings", "frozen_accounts_modifier" + role_allowed_for_frozen_entries = frappe.db_get_cached_value( + "Company", company, "role_allowed_for_frozen_entries" ) - if frozen_accounts_modifier not in frappe.get_roles(): + if role_allowed_for_frozen_entries not in frappe.get_roles(): frappe.throw(_("{0} {1} is frozen").format(party_type, party_name), PartyFrozen) elif party_type == "Employee": diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index e2042c59ad7..1f11eabe458 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2332,7 +2332,7 @@ class AccountsController(TransactionBase): def validate_party(self): party_type, party = self.get_party() - validate_party_frozen_disabled(party_type, party) + validate_party_frozen_disabled(self.company, party_type, party) def get_party(self): party_type = None From b2e4e76b97f66cb5e635eeae58c0d528166c30d8 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Fri, 13 Jun 2025 20:12:19 +0530 Subject: [PATCH 06/22] refactor: remove accounts freezing settings from accounts settings --- .../accounts_settings/accounts_settings.json | 17 ----------------- .../accounts_settings/accounts_settings.py | 2 -- 2 files changed, 19 deletions(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index 64b8b27cd76..37d6b6cd71e 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -75,11 +75,9 @@ "book_asset_depreciation_entry_automatically", "closing_settings_tab", "period_closing_settings_section", - "acc_frozen_upto", "ignore_account_closing_balance", "use_legacy_controller_for_pcv", "column_break_25", - "frozen_accounts_modifier", "tab_break_dpet", "show_balance_in_coa", "banking_tab", @@ -102,21 +100,6 @@ "use_legacy_budget_controller" ], "fields": [ - { - "description": "Accounting entries are frozen up to this date. Nobody can create or modify entries except users with the role specified below", - "fieldname": "acc_frozen_upto", - "fieldtype": "Date", - "in_list_view": 1, - "label": "Accounts Frozen Till Date" - }, - { - "description": "Users with this role are allowed to set frozen accounts and create / modify accounting entries against frozen accounts", - "fieldname": "frozen_accounts_modifier", - "fieldtype": "Link", - "in_list_view": 1, - "label": "Role Allowed to Set Frozen Accounts and Edit Frozen Entries", - "options": "Role" - }, { "default": "Billing Address", "description": "Address used to determine Tax Category in transactions", diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py index bef862d85e0..5765d626a59 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -23,7 +23,6 @@ class AccountsSettings(Document): if TYPE_CHECKING: from frappe.types import DF - acc_frozen_upto: DF.Date | None add_taxes_from_item_tax_template: DF.Check add_taxes_from_taxes_and_charges_template: DF.Check allow_multi_currency_invoices_against_single_party_account: DF.Check @@ -50,7 +49,6 @@ class AccountsSettings(Document): enable_party_matching: DF.Check exchange_gain_loss_posting_date: DF.Literal["Invoice", "Payment", "Reconciliation Date"] fetch_valuation_rate_for_internal_transaction: DF.Check - frozen_accounts_modifier: DF.Link | None general_ledger_remarks_length: DF.Int ignore_account_closing_balance: DF.Check ignore_is_opening_check_for_reporting: DF.Check From cd540ab4cc88c9e58bed9393de263d6406f9ee9e Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Fri, 13 Jun 2025 20:20:54 +0530 Subject: [PATCH 07/22] chore: migration patch for account freezing fields --- erpnext/patches.txt | 1 + ...ate_account_freezing_settings_to_company.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 erpnext/patches/v15_0/migrate_account_freezing_settings_to_company.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 1fda2b6f518..f46d8987d08 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -265,6 +265,7 @@ erpnext.patches.v16_0.create_company_custom_fields [post_model_sync] erpnext.patches.v15_0.rename_gross_purchase_amount_to_net_purchase_amount +erpnext.patches.v15_0.migrate_account_freezing_settings_to_company erpnext.patches.v15_0.create_asset_depreciation_schedules_from_assets execute:frappe.delete_doc_if_exists('Workspace', 'ERPNext Integrations Settings') erpnext.patches.v14_0.update_posting_datetime_and_dropped_indexes #22-02-2024 diff --git a/erpnext/patches/v15_0/migrate_account_freezing_settings_to_company.py b/erpnext/patches/v15_0/migrate_account_freezing_settings_to_company.py new file mode 100644 index 00000000000..0ae7764f07c --- /dev/null +++ b/erpnext/patches/v15_0/migrate_account_freezing_settings_to_company.py @@ -0,0 +1,18 @@ +import frappe + + +def execute(): + accounts_settings = frappe.get_doc("Accounts Settings", "Accounts Settings") + + accounts_frozen_till_date = accounts_settings.acc_frozen_upto + frozen_accounts_modifier = accounts_settings.frozen_accounts_modifier + + for company in frappe.get_all("Company", pluck="name"): + frappe.db.set_value( + "Company", + company, + { + "accounts_frozen_till_date": accounts_frozen_till_date, + "role_allowed_for_frozen_entries": frozen_accounts_modifier, + }, + ) From 6da10b9f97555103e5e81e4ec1ee662ebccf9e99 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Wed, 18 Jun 2025 01:04:51 +0530 Subject: [PATCH 08/22] fix: update validation and test cases --- .../accounts_settings/accounts_settings.py | 9 +-- .../regional/united_states.js | 17 ++-- .../fixed_asset_register.py | 77 +++++++++++++++++++ erpnext/setup/doctype/company/company.py | 8 ++ .../test_repost_item_valuation.py | 13 ++-- 5 files changed, 105 insertions(+), 19 deletions(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py index 5765d626a59..bf4c78b0c1f 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -11,7 +11,6 @@ from frappe.model.document import Document from frappe.utils import cint from erpnext.accounts.utils import sync_auto_reconcile_config -from erpnext.stock.utils import check_pending_reposting class AccountsSettings(Document): @@ -98,8 +97,8 @@ class AccountsSettings(Document): if old_doc.show_payment_schedule_in_print != self.show_payment_schedule_in_print: self.enable_payment_schedule_in_print() - if old_doc.acc_frozen_upto != self.acc_frozen_upto: - self.validate_pending_reposts() + if old_doc.use_sales_invoice_in_pos != self.use_sales_invoice_in_pos: + self.validate_invoice_mode_switch_in_pos() if clear_cache: frappe.clear_cache() @@ -127,10 +126,6 @@ class AccountsSettings(Document): validate_fields_for_doctype=False, ) - def validate_pending_reposts(self): - if self.acc_frozen_upto: - check_pending_reposting(self.acc_frozen_upto) - def validate_and_sync_auto_reconcile_config(self): if self.has_value_changed("auto_reconciliation_job_trigger"): if ( diff --git a/erpnext/accounts/doctype/accounts_settings/regional/united_states.js b/erpnext/accounts/doctype/accounts_settings/regional/united_states.js index a522de9da75..06e1e3ba3b8 100644 --- a/erpnext/accounts/doctype/accounts_settings/regional/united_states.js +++ b/erpnext/accounts/doctype/accounts_settings/regional/united_states.js @@ -1,11 +1,16 @@ frappe.ui.form.on("Accounts Settings", { refresh: function (frm) { - frm.set_df_property("acc_frozen_upto", "label", "Books Closed Through"); - frm.set_df_property( - "frozen_accounts_modifier", - "label", - "Role Allowed to Close Books & Make Changes to Closed Periods" - ); frm.set_df_property("credit_controller", "label", "Credit Manager"); }, }); + +frappe.ui.form.on("Company", { + refresh: function (frm) { + frm.set_df_property("accounts_frozen_till_date", "label", "Books Closed Through"); + frm.set_df_property( + "role_allowed_for_frozen_entries", + "label", + "Role Allowed to Close Books & Make Changes to Closed Periods" + ); + }, +}); diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index 2103379df93..f92ba266c74 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -161,6 +161,83 @@ def get_conditions(filters): return conditions +def get_data(filters): + data = [] + + conditions = get_conditions(filters) + pr_supplier_map = get_purchase_receipt_supplier_map() + pi_supplier_map = get_purchase_invoice_supplier_map() + + assets_linked_to_fb = get_assets_linked_to_fb(filters) + + company_fb = frappe.get_cached_value("Company", filters.company, "default_finance_book") + + if filters.include_default_book_assets and company_fb: + finance_book = company_fb + elif filters.finance_book: + finance_book = filters.finance_book + else: + finance_book = None + + depreciation_amount_map = get_asset_depreciation_amount_map(filters, finance_book) + + group_by = frappe.scrub(filters.get("group_by")) + + if group_by in ("asset_category", "location"): + data = get_group_by_data(group_by, conditions, assets_linked_to_fb, depreciation_amount_map) + return data + + fields = [ + "name as asset_id", + "asset_name", + "status", + "department", + "company", + "cost_center", + "calculate_depreciation", + "purchase_receipt", + "asset_category", + "purchase_date", + "gross_purchase_amount", + "location", + "available_for_use_date", + "purchase_invoice", + "opening_accumulated_depreciation", + ] + assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields, debug=1) + + for asset in assets_record: + if assets_linked_to_fb and asset.calculate_depreciation and asset.asset_id not in assets_linked_to_fb: + continue + + depreciation_amount = depreciation_amount_map.get(asset.asset_id) or 0.0 + asset_value = ( + asset.gross_purchase_amount - asset.opening_accumulated_depreciation - depreciation_amount + ) + + row = { + "asset_id": asset.asset_id, + "asset_name": asset.asset_name, + "status": asset.status, + "department": asset.department, + "cost_center": asset.cost_center, + "vendor_name": pr_supplier_map.get(asset.purchase_receipt) + or pi_supplier_map.get(asset.purchase_invoice), + "gross_purchase_amount": asset.gross_purchase_amount, + "opening_accumulated_depreciation": asset.opening_accumulated_depreciation, + "depreciated_amount": depreciation_amount, + "available_for_use_date": asset.available_for_use_date, + "location": asset.location, + "asset_category": asset.asset_category, + "purchase_date": asset.purchase_date, + "asset_value": asset_value, + "company": asset.company, + } + data.append(row) + + return data + + def prepare_chart_data(data, filters): if not data: return diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index a94a62bfba7..53759839c79 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -19,6 +19,7 @@ from erpnext.accounts.doctype.financial_report_template.financial_report_templat sync_financial_report_templates, ) from erpnext.setup.setup_wizard.operations.taxes_setup import setup_taxes_and_charges +from erpnext.stock.utils import check_pending_reposting class Company(NestedSet): @@ -153,6 +154,7 @@ class Company(NestedSet): return exists def validate(self): + old_doc = self.get_doc_before_save() self.update_default_account = False if self.is_new(): self.update_default_account = True @@ -171,6 +173,7 @@ class Company(NestedSet): self.set_reporting_currency() self.validate_inventory_account_settings() self.cant_change_valuation_method() + self.validate_pending_reposts(old_doc) def cant_change_valuation_method(self): doc_before_save = self.get_doc_before_save() @@ -599,6 +602,11 @@ class Company(NestedSet): ) self.reporting_currency = parent_reporting_currency + def validate_pending_reposts(self, old_doc): + if old_doc.accounts_frozen_till_date != self.accounts_frozen_till_date: + if self.accounts_frozen_till_date: + check_pending_reposting(self.accounts_frozen_till_date) + def set_default_accounts(self): default_accounts = { "default_cash_account": "Cash", diff --git a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py index 285e9648fdd..d4a8dc24c52 100644 --- a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py @@ -356,6 +356,7 @@ class TestRepostItemValuation(IntegrationTestCase, StockTestMixin): riv = frappe.get_doc( doctype="Repost Item Valuation", item_code="_Test Item", + company="_Test Company", warehouse="_Test Warehouse - _TC", based_on="Item and Warehouse", posting_date=today, @@ -363,15 +364,15 @@ class TestRepostItemValuation(IntegrationTestCase, StockTestMixin): ) riv.flags.dont_run_in_test = True # keep it queued - accounts_settings = frappe.get_doc("Accounts Settings") - accounts_settings.acc_frozen_upto = today - accounts_settings.frozen_accounts_modifier = "" - accounts_settings.save() + company = frappe.get_doc("Company", "_Test Company") + company.accounts_frozen_till_date = today + company.role_allowed_for_frozen_entries = "" + company.save() self.assertRaises(frappe.ValidationError, riv.save) - accounts_settings.acc_frozen_upto = "" - accounts_settings.save() + company.accounts_frozen_till_date = "" + company.save() @IntegrationTestCase.change_settings("Stock Reposting Settings", {"item_based_reposting": 0}) def test_create_repost_entry_for_cancelled_document(self): From 826c74eb717abd44a0bd508bc846dc66e0f03246 Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Wed, 18 Jun 2025 19:11:49 +0530 Subject: [PATCH 09/22] chore: remove debug flag accidentally left in code --- .../assets/report/fixed_asset_register/fixed_asset_register.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index f92ba266c74..728d1014efe 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -204,7 +204,7 @@ def get_data(filters): "purchase_invoice", "opening_accumulated_depreciation", ] - assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields, debug=1) + assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields) for asset in assets_record: if assets_linked_to_fb and asset.calculate_depreciation and asset.asset_id not in assets_linked_to_fb: From eee78766cd4ef0586d6ef76d55dfc627bacf801d Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Thu, 19 Jun 2025 11:53:04 +0530 Subject: [PATCH 10/22] chore: resolved conflicts --- .../accounts/doctype/accounts_settings/accounts_settings.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py index bf4c78b0c1f..3c2582361d8 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.py +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.py @@ -97,9 +97,6 @@ class AccountsSettings(Document): if old_doc.show_payment_schedule_in_print != self.show_payment_schedule_in_print: self.enable_payment_schedule_in_print() - if old_doc.use_sales_invoice_in_pos != self.use_sales_invoice_in_pos: - self.validate_invoice_mode_switch_in_pos() - if clear_cache: frappe.clear_cache() From 28febc69e858446ee2cd483a316e10677d71a440 Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Thu, 26 Jun 2025 11:51:57 +0530 Subject: [PATCH 11/22] fix: validate pending reposting till acc frozen date --- erpnext/stock/utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index b891e0744fc..5367d67004c 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -560,7 +560,7 @@ def is_reposting_item_valuation_in_progress(): ) -def check_pending_reposting(posting_date: str, throw_error: bool = True) -> bool: +def check_pending_reposting(posting_date: str, company: str | None = None, throw_error: bool = True) -> bool: """Check if there are pending reposting job till the specified posting date.""" filters = { @@ -568,6 +568,8 @@ def check_pending_reposting(posting_date: str, throw_error: bool = True) -> bool "status": ["in", ["Queued", "In Progress"]], "posting_date": ["<=", posting_date], } + if company: + filters["company"] = company reposting_pending = frappe.db.exists("Repost Item Valuation", filters) if reposting_pending and throw_error: From 9cc8a42074144a81f90fb865e954551f79d4b54c Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Thu, 26 Jun 2025 14:30:32 +0530 Subject: [PATCH 12/22] fix: use correct field name --- erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index bae0de3f94c..1b98556f0c6 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -64,7 +64,7 @@ class TestSalesInvoice(ERPNextTestSuite): mode_of_payment, "_Test Company with perpetual inventory", "_Test Bank - TCP1" ) for company in frappe.get_all("Company", pluck="name"): - frappe.db.set_value("Company", company, "acc_frozen_till_date", None) + frappe.db.set_value("Company", company, "accounts_frozen_till_date", None) @change_settings( "Accounts Settings", From 16f4e128540f4e18e60b0a71de10ae5ce1779f1d Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Thu, 26 Jun 2025 14:35:48 +0530 Subject: [PATCH 13/22] chore: fix typo --- erpnext/accounts/doctype/gl_entry/gl_entry.py | 2 +- erpnext/accounts/party.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index 68ef84a1365..92312f3a630 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -422,7 +422,7 @@ def update_outstanding_amt( def validate_frozen_account(company, account, adv_adj=None): frozen_account = frappe.get_cached_value("Account", account, "freeze_account") if frozen_account == "Yes" and not adv_adj: - role_allowed_for_frozen_entries = frappe.db_get_cached_value( + role_allowed_for_frozen_entries = frappe.db.get_cached_value( "Company", company, "role_allowed_for_frozen_entries" ) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 39f25f5fcab..702120408c8 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -805,7 +805,7 @@ def validate_party_frozen_disabled(company, party_type, party_name): if party.disabled: frappe.throw(_("{0} {1} is disabled").format(party_type, party_name), PartyDisabled) elif party.get("is_frozen"): - role_allowed_for_frozen_entries = frappe.db_get_cached_value( + role_allowed_for_frozen_entries = frappe.db.get_cached_value( "Company", company, "role_allowed_for_frozen_entries" ) if role_allowed_for_frozen_entries not in frappe.get_roles(): From 95877e73f03ec71dd9e2c014113e6e69d2e028f3 Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Thu, 26 Jun 2025 15:44:41 +0530 Subject: [PATCH 14/22] fix: use correct date value --- erpnext/accounts/doctype/gl_entry/gl_entry.py | 2 +- .../test_process_deferred_accounting.py | 2 +- erpnext/accounts/party.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index 92312f3a630..c52c86528fd 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -422,7 +422,7 @@ def update_outstanding_amt( def validate_frozen_account(company, account, adv_adj=None): frozen_account = frappe.get_cached_value("Account", account, "freeze_account") if frozen_account == "Yes" and not adv_adj: - role_allowed_for_frozen_entries = frappe.db.get_cached_value( + role_allowed_for_frozen_entries = frappe.get_cached_value( "Company", company, "role_allowed_for_frozen_entries" ) diff --git a/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py b/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py index 59764442a4e..b987d1579e8 100644 --- a/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py +++ b/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py @@ -93,7 +93,7 @@ class TestProcessDeferredAccounting(IntegrationTestCase): def change_acc_settings( - company="_Test Company", acc_frozen_till_date="", book_deferred_entries_based_on="Days" + company="_Test Company", acc_frozen_till_date=None, book_deferred_entries_based_on="Days" ): acc_settings = frappe.get_doc("Accounts Settings", "Accounts Settings") acc_settings.book_deferred_entries_based_on = book_deferred_entries_based_on diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 702120408c8..86efdefcc3d 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -805,7 +805,7 @@ def validate_party_frozen_disabled(company, party_type, party_name): if party.disabled: frappe.throw(_("{0} {1} is disabled").format(party_type, party_name), PartyDisabled) elif party.get("is_frozen"): - role_allowed_for_frozen_entries = frappe.db.get_cached_value( + role_allowed_for_frozen_entries = frappe.get_cached_value( "Company", company, "role_allowed_for_frozen_entries" ) if role_allowed_for_frozen_entries not in frappe.get_roles(): From 0373f7f33fa856831c7df531e6cc08cc508d40ae Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Wed, 1 Oct 2025 16:30:06 +0530 Subject: [PATCH 15/22] chore: validation for none type object --- erpnext/setup/doctype/company/company.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index 53759839c79..52de3542c61 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -603,7 +603,7 @@ class Company(NestedSet): self.reporting_currency = parent_reporting_currency def validate_pending_reposts(self, old_doc): - if old_doc.accounts_frozen_till_date != self.accounts_frozen_till_date: + if old_doc and old_doc.accounts_frozen_till_date != self.accounts_frozen_till_date: if self.accounts_frozen_till_date: check_pending_reposting(self.accounts_frozen_till_date) From 09cdb943ec40f1ba2046352ce171f77a62a4061a Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Wed, 1 Oct 2025 17:05:51 +0530 Subject: [PATCH 16/22] fix: remove duplicate method --- .../fixed_asset_register.py | 77 ------------------- 1 file changed, 77 deletions(-) diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index 728d1014efe..2103379df93 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -161,83 +161,6 @@ def get_conditions(filters): return conditions -def get_data(filters): - data = [] - - conditions = get_conditions(filters) - pr_supplier_map = get_purchase_receipt_supplier_map() - pi_supplier_map = get_purchase_invoice_supplier_map() - - assets_linked_to_fb = get_assets_linked_to_fb(filters) - - company_fb = frappe.get_cached_value("Company", filters.company, "default_finance_book") - - if filters.include_default_book_assets and company_fb: - finance_book = company_fb - elif filters.finance_book: - finance_book = filters.finance_book - else: - finance_book = None - - depreciation_amount_map = get_asset_depreciation_amount_map(filters, finance_book) - - group_by = frappe.scrub(filters.get("group_by")) - - if group_by in ("asset_category", "location"): - data = get_group_by_data(group_by, conditions, assets_linked_to_fb, depreciation_amount_map) - return data - - fields = [ - "name as asset_id", - "asset_name", - "status", - "department", - "company", - "cost_center", - "calculate_depreciation", - "purchase_receipt", - "asset_category", - "purchase_date", - "gross_purchase_amount", - "location", - "available_for_use_date", - "purchase_invoice", - "opening_accumulated_depreciation", - ] - assets_record = frappe.db.get_all("Asset", filters=conditions, fields=fields) - - for asset in assets_record: - if assets_linked_to_fb and asset.calculate_depreciation and asset.asset_id not in assets_linked_to_fb: - continue - - depreciation_amount = depreciation_amount_map.get(asset.asset_id) or 0.0 - asset_value = ( - asset.gross_purchase_amount - asset.opening_accumulated_depreciation - depreciation_amount - ) - - row = { - "asset_id": asset.asset_id, - "asset_name": asset.asset_name, - "status": asset.status, - "department": asset.department, - "cost_center": asset.cost_center, - "vendor_name": pr_supplier_map.get(asset.purchase_receipt) - or pi_supplier_map.get(asset.purchase_invoice), - "gross_purchase_amount": asset.gross_purchase_amount, - "opening_accumulated_depreciation": asset.opening_accumulated_depreciation, - "depreciated_amount": depreciation_amount, - "available_for_use_date": asset.available_for_use_date, - "location": asset.location, - "asset_category": asset.asset_category, - "purchase_date": asset.purchase_date, - "asset_value": asset_value, - "company": asset.company, - } - data.append(row) - - return data - - def prepare_chart_data(data, filters): if not data: return From 29048c3364208608a24ce5c333112af4083595bf Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Mon, 3 Nov 2025 11:10:31 +0530 Subject: [PATCH 17/22] refactor: fix incorrect conditon --- erpnext/assets/doctype/asset/depreciation.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index 1de2762fa0e..af5e2faca07 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -112,9 +112,11 @@ def get_companies_with_frozen_limits(): if not d.accounts_frozen_till_date: continue - if d.role_allowed_for_frozen_entries in frappe.get_roles() or frappe.session.user == "Administrator": - continue - companies_with_frozen_limits[d.name] = getdate(d.accounts_frozen_till_date) + if ( + d.role_allowed_for_frozen_entries not in frappe.get_roles() + and frappe.session.user != "Administrator" + ): + companies_with_frozen_limits[d.name] = getdate(d.accounts_frozen_till_date) return companies_with_frozen_limits From 4df20a312223f9a5737f0c93a550b5295fc4e98f Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Mon, 1 Dec 2025 12:53:33 +0530 Subject: [PATCH 18/22] fix: patch to migrate setting --- .../migrate_account_freezing_settings_to_company.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/erpnext/patches/v15_0/migrate_account_freezing_settings_to_company.py b/erpnext/patches/v15_0/migrate_account_freezing_settings_to_company.py index 0ae7764f07c..2801b1c9a65 100644 --- a/erpnext/patches/v15_0/migrate_account_freezing_settings_to_company.py +++ b/erpnext/patches/v15_0/migrate_account_freezing_settings_to_company.py @@ -2,17 +2,18 @@ import frappe def execute(): - accounts_settings = frappe.get_doc("Accounts Settings", "Accounts Settings") + frozen_till = frappe.db.get_single_value("Accounts Settings", "acc_frozen_upto") + modifier = frappe.db.get_single_value("Accounts Settings", "frozen_accounts_modifier") - accounts_frozen_till_date = accounts_settings.acc_frozen_upto - frozen_accounts_modifier = accounts_settings.frozen_accounts_modifier + if not frozen_till and not modifier: + return for company in frappe.get_all("Company", pluck="name"): frappe.db.set_value( "Company", company, { - "accounts_frozen_till_date": accounts_frozen_till_date, - "role_allowed_for_frozen_entries": frozen_accounts_modifier, + "accounts_frozen_till_date": frozen_till, + "role_allowed_for_frozen_entries": modifier, }, ) From c7e7e02b5b5a43283c22502109a4fc9079393f04 Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Mon, 1 Dec 2025 13:40:51 +0530 Subject: [PATCH 19/22] refactor: use Singles table to get acc_frozen_upto and modifier during migration --- ...grate_account_freezing_settings_to_company.py | 16 ++++++++++++++-- erpnext/setup/doctype/company/company.py | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/erpnext/patches/v15_0/migrate_account_freezing_settings_to_company.py b/erpnext/patches/v15_0/migrate_account_freezing_settings_to_company.py index 2801b1c9a65..60869fc3267 100644 --- a/erpnext/patches/v15_0/migrate_account_freezing_settings_to_company.py +++ b/erpnext/patches/v15_0/migrate_account_freezing_settings_to_company.py @@ -2,8 +2,20 @@ import frappe def execute(): - frozen_till = frappe.db.get_single_value("Accounts Settings", "acc_frozen_upto") - modifier = frappe.db.get_single_value("Accounts Settings", "frozen_accounts_modifier") + rows = frappe.db.sql( + """ + SELECT field, value + FROM `tabSingles` + WHERE doctype='Accounts Settings' + AND field IN ('acc_frozen_upto', 'frozen_accounts_modifier') + """, + as_dict=True, + ) + + values = {row["field"]: row["value"] for row in rows} + + frozen_till = values.get("acc_frozen_upto") + modifier = values.get("frozen_accounts_modifier") if not frozen_till and not modifier: return diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index 52de3542c61..def469e4fa3 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -605,7 +605,7 @@ class Company(NestedSet): def validate_pending_reposts(self, old_doc): if old_doc and old_doc.accounts_frozen_till_date != self.accounts_frozen_till_date: if self.accounts_frozen_till_date: - check_pending_reposting(self.accounts_frozen_till_date) + check_pending_reposting(self.accounts_frozen_till_date, self.name) def set_default_accounts(self): default_accounts = { From 4f33ee01cf81bb1e4f4628e0538b9450c86d91e6 Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Tue, 2 Dec 2025 14:24:27 +0530 Subject: [PATCH 20/22] fix: undo incorrect patch modification --- .../modify_invalid_gain_loss_gl_entries.py | 49 +++++++------------ 1 file changed, 17 insertions(+), 32 deletions(-) diff --git a/erpnext/patches/v13_0/modify_invalid_gain_loss_gl_entries.py b/erpnext/patches/v13_0/modify_invalid_gain_loss_gl_entries.py index 37dbd728636..8e58d79b928 100644 --- a/erpnext/patches/v13_0/modify_invalid_gain_loss_gl_entries.py +++ b/erpnext/patches/v13_0/modify_invalid_gain_loss_gl_entries.py @@ -10,17 +10,15 @@ def execute(): purchase_invoices = frappe.db.sql( """ select - PI.company, PI_ADV.parenttype as type, PI_ADV.parent as name + parenttype as type, parent as name from - `tabPurchase Invoice Advance` as PI_ADV join `tabPurchase Invoice` as PI - on - PI_ADV.parent = PI.name + `tabPurchase Invoice Advance` where - PI_ADV.ref_exchange_rate = 1 - and PI_ADV.docstatus = 1 - and ifnull(PI_ADV.exchange_gain_loss, 0) != 0 + ref_exchange_rate = 1 + and docstatus = 1 + and ifnull(exchange_gain_loss, 0) != 0 group by - PI_ADV.parent + parent """, as_dict=1, ) @@ -28,17 +26,15 @@ def execute(): sales_invoices = frappe.db.sql( """ select - SI.company, SI_ADV.parenttype as type, SI_ADV.parent as name + parenttype as type, parent as name from - `tabSales Invoice Advance` as SI_ADV join `tabSales Invoice` as SI - on - SI_ADV.parent = SI.name + `tabSales Invoice Advance` where - SI_ADV.ref_exchange_rate = 1 - and SI_ADV.docstatus = 1 - and ifnull(SI_ADV.exchange_gain_loss, 0) != 0 + ref_exchange_rate = 1 + and docstatus = 1 + and ifnull(exchange_gain_loss, 0) != 0 group by - SI_ADV.parent + parent """, as_dict=1, ) @@ -49,21 +45,11 @@ def execute(): message=json.dumps(purchase_invoices + sales_invoices, indent=2), ) - original_frozen_dates = {} + acc_frozen_upto = frappe.db.get_single_value("Accounts Settings", "acc_frozen_upto") + if acc_frozen_upto: + frappe.db.set_single_value("Accounts Settings", "acc_frozen_upto", None) for invoice in purchase_invoices + sales_invoices: - company = invoice.company - - # Unfreeze only once per company - if company not in original_frozen_dates: - accounts_frozen_till_date = frappe.get_cached_value( - "Company", company, "accounts_frozen_till_date" - ) - original_frozen_dates[company] = accounts_frozen_till_date - - if accounts_frozen_till_date: - frappe.db.set_value("Company", company, "accounts_frozen_till_date", None) - try: doc = frappe.get_doc(invoice.type, invoice.name) doc.docstatus = 2 @@ -78,6 +64,5 @@ def execute(): frappe.db.rollback() print(f"Failed to correct gl entries of {invoice.name}") - for company, frozen_date in original_frozen_dates.items(): - if frozen_date: - frappe.db.set_value("Company", company, "accounts_frozen_till_date", frozen_date) + if acc_frozen_upto: + frappe.db.set_single_value("Accounts Settings", "acc_frozen_upto", acc_frozen_upto) From 404e68bdc202a6b7e2e6e115af0254238779065b Mon Sep 17 00:00:00 2001 From: khushi8112 Date: Tue, 2 Dec 2025 17:49:16 +0530 Subject: [PATCH 21/22] fix: more patch related changes --- erpnext/patches.txt | 4 ++-- .../patches/v15_0/recalculate_amount_difference_field.py | 6 +++--- .../migrate_account_freezing_settings_to_company.py | 0 3 files changed, 5 insertions(+), 5 deletions(-) rename erpnext/patches/{v15_0 => v16_0}/migrate_account_freezing_settings_to_company.py (100%) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index f46d8987d08..3e640e7bf0c 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -265,7 +265,6 @@ erpnext.patches.v16_0.create_company_custom_fields [post_model_sync] erpnext.patches.v15_0.rename_gross_purchase_amount_to_net_purchase_amount -erpnext.patches.v15_0.migrate_account_freezing_settings_to_company erpnext.patches.v15_0.create_asset_depreciation_schedules_from_assets execute:frappe.delete_doc_if_exists('Workspace', 'ERPNext Integrations Settings') erpnext.patches.v14_0.update_posting_datetime_and_dropped_indexes #22-02-2024 @@ -450,4 +449,5 @@ erpnext.patches.v16_0.set_company_wise_warehouses erpnext.patches.v16_0.set_valuation_method_on_companies erpnext.patches.v15_0.migrate_old_item_wise_tax_detail_data_to_table erpnext.patches.v16_0.migrate_budget_records_to_new_structure -erpnext.patches.v16_0.update_currency_exchange_settings_for_frankfurter \ No newline at end of file +erpnext.patches.v16_0.update_currency_exchange_settings_for_frankfurter +erpnext.patches.v16_0.migrate_account_freezing_settings_to_company \ No newline at end of file diff --git a/erpnext/patches/v15_0/recalculate_amount_difference_field.py b/erpnext/patches/v15_0/recalculate_amount_difference_field.py index f3cece4c416..d7b56c206d2 100644 --- a/erpnext/patches/v15_0/recalculate_amount_difference_field.py +++ b/erpnext/patches/v15_0/recalculate_amount_difference_field.py @@ -58,9 +58,9 @@ def execute(): ): posting_date = period_closing_voucher[0].period_end_date - acc_frozen_till_date = frappe.db.get_value("Company", company, "accounts_frozen_till_date") - if acc_frozen_till_date and getdate(acc_frozen_till_date) > getdate(posting_date): - posting_date = acc_frozen_till_date + acc_frozen_upto = frappe.db.get_single_value("Accounts Settings", "acc_frozen_upto") + if acc_frozen_upto and getdate(acc_frozen_upto) > getdate(posting_date): + posting_date = acc_frozen_upto stock_frozen_upto = frappe.db.get_single_value("Stock Settings", "stock_frozen_upto") if stock_frozen_upto and getdate(stock_frozen_upto) > getdate(posting_date): diff --git a/erpnext/patches/v15_0/migrate_account_freezing_settings_to_company.py b/erpnext/patches/v16_0/migrate_account_freezing_settings_to_company.py similarity index 100% rename from erpnext/patches/v15_0/migrate_account_freezing_settings_to_company.py rename to erpnext/patches/v16_0/migrate_account_freezing_settings_to_company.py From 4e578c4f83cfc0267736b4a4ed6bb4c2c1da40db Mon Sep 17 00:00:00 2001 From: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> Date: Wed, 3 Dec 2025 11:47:04 +0530 Subject: [PATCH 22/22] fix: conflicts --- erpnext/patches/v14_0/single_to_multi_dunning.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches/v14_0/single_to_multi_dunning.py b/erpnext/patches/v14_0/single_to_multi_dunning.py index 108f9c96554..47d13a0e383 100644 --- a/erpnext/patches/v14_0/single_to_multi_dunning.py +++ b/erpnext/patches/v14_0/single_to_multi_dunning.py @@ -77,4 +77,4 @@ def get_accounts_closing_date(): else: can_edit_accounts_after = accounts_frozen_till or period_closing_date - return can_edit_accounts_after \ No newline at end of file + return can_edit_accounts_after