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, 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/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index 551005902f6..dc657945544 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -76,11 +76,9 @@ "role_to_notify_on_depreciation_failure", "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", @@ -103,21 +101,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 f56bc7533fb..894a2cb3646 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): @@ -23,7 +22,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 +48,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 @@ -101,9 +98,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.acc_frozen_upto != self.acc_frozen_upto: - self.validate_pending_reposts() - if clear_cache: frappe.clear_cache() @@ -130,10 +124,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/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index aefe293157b..c52c86528fd 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.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..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 @@ -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=None, 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..1b98556f0c6 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, "accounts_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/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/accounts/party.py b/erpnext/accounts/party.py index eef82a398b9..86efdefcc3d 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.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/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index 9cad62470ce..7a1b4b70af7 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -96,13 +96,28 @@ 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 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 def make_depreciation_entry_on_disposal(asset_doc, disposal_date=None): @@ -111,20 +126,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) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index cefe589cf1d..58c0b8a5dd1 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2357,7 +2357,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 diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 1fda2b6f518..3e640e7bf0c 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -449,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/v16_0/migrate_account_freezing_settings_to_company.py b/erpnext/patches/v16_0/migrate_account_freezing_settings_to_company.py new file mode 100644 index 00000000000..60869fc3267 --- /dev/null +++ b/erpnext/patches/v16_0/migrate_account_freezing_settings_to_company.py @@ -0,0 +1,31 @@ +import frappe + + +def execute(): + 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 + + for company in frappe.get_all("Company", pluck="name"): + frappe.db.set_value( + "Company", + company, + { + "accounts_frozen_till_date": frozen_till, + "role_allowed_for_frozen_entries": modifier, + }, + ) 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..def469e4fa3 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): @@ -31,6 +32,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 +105,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 @@ -151,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 @@ -169,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() @@ -597,6 +602,11 @@ class Company(NestedSet): ) self.reporting_currency = parent_reporting_currency + 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, self.name) + def set_default_accounts(self): default_accounts = { "default_cash_account": "Cash", 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": 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): 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: