mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-24 15:39:20 +00:00
Merge pull request #50824 from khushi8112/move-accounts-freezing-setting-to-company
refactor: Move accounts freezing setting to company
This commit is contained in:
@@ -7,6 +7,7 @@ from frappe.utils import (
|
|||||||
cint,
|
cint,
|
||||||
date_diff,
|
date_diff,
|
||||||
flt,
|
flt,
|
||||||
|
formatdate,
|
||||||
get_first_day,
|
get_first_day,
|
||||||
get_last_day,
|
get_last_day,
|
||||||
get_link_to_form,
|
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):
|
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"
|
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(
|
def _book_deferred_revenue_or_expense(
|
||||||
item,
|
item,
|
||||||
|
|||||||
@@ -93,8 +93,10 @@ class Account(NestedSet):
|
|||||||
super().on_update()
|
super().on_update()
|
||||||
|
|
||||||
def onload(self):
|
def onload(self):
|
||||||
frozen_accounts_modifier = frappe.get_single_value("Accounts Settings", "frozen_accounts_modifier")
|
role_allowed_for_frozen_entries = frappe.db.get_value(
|
||||||
if not frozen_accounts_modifier or frozen_accounts_modifier in frappe.get_roles():
|
"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)
|
self.set_onload("can_freeze_account", True)
|
||||||
|
|
||||||
def autoname(self):
|
def autoname(self):
|
||||||
@@ -303,10 +305,10 @@ class Account(NestedSet):
|
|||||||
if not doc_before_save or doc_before_save.freeze_account == self.freeze_account:
|
if not doc_before_save or doc_before_save.freeze_account == self.freeze_account:
|
||||||
return
|
return
|
||||||
|
|
||||||
frozen_accounts_modifier = frappe.get_cached_value(
|
role_allowed_for_frozen_entries = frappe.get_cached_value(
|
||||||
"Accounts Settings", "Accounts Settings", "frozen_accounts_modifier"
|
"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"))
|
throw(_("You are not authorized to set Frozen value"))
|
||||||
|
|
||||||
def validate_balance_must_be_debit_or_credit(self):
|
def validate_balance_must_be_debit_or_credit(self):
|
||||||
|
|||||||
@@ -76,11 +76,9 @@
|
|||||||
"role_to_notify_on_depreciation_failure",
|
"role_to_notify_on_depreciation_failure",
|
||||||
"closing_settings_tab",
|
"closing_settings_tab",
|
||||||
"period_closing_settings_section",
|
"period_closing_settings_section",
|
||||||
"acc_frozen_upto",
|
|
||||||
"ignore_account_closing_balance",
|
"ignore_account_closing_balance",
|
||||||
"use_legacy_controller_for_pcv",
|
"use_legacy_controller_for_pcv",
|
||||||
"column_break_25",
|
"column_break_25",
|
||||||
"frozen_accounts_modifier",
|
|
||||||
"tab_break_dpet",
|
"tab_break_dpet",
|
||||||
"show_balance_in_coa",
|
"show_balance_in_coa",
|
||||||
"banking_tab",
|
"banking_tab",
|
||||||
@@ -103,21 +101,6 @@
|
|||||||
"use_legacy_budget_controller"
|
"use_legacy_budget_controller"
|
||||||
],
|
],
|
||||||
"fields": [
|
"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",
|
"default": "Billing Address",
|
||||||
"description": "Address used to determine Tax Category in transactions",
|
"description": "Address used to determine Tax Category in transactions",
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ from frappe.model.document import Document
|
|||||||
from frappe.utils import cint
|
from frappe.utils import cint
|
||||||
|
|
||||||
from erpnext.accounts.utils import sync_auto_reconcile_config
|
from erpnext.accounts.utils import sync_auto_reconcile_config
|
||||||
from erpnext.stock.utils import check_pending_reposting
|
|
||||||
|
|
||||||
|
|
||||||
class AccountsSettings(Document):
|
class AccountsSettings(Document):
|
||||||
@@ -23,7 +22,6 @@ class AccountsSettings(Document):
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from frappe.types import DF
|
from frappe.types import DF
|
||||||
|
|
||||||
acc_frozen_upto: DF.Date | None
|
|
||||||
add_taxes_from_item_tax_template: DF.Check
|
add_taxes_from_item_tax_template: DF.Check
|
||||||
add_taxes_from_taxes_and_charges_template: DF.Check
|
add_taxes_from_taxes_and_charges_template: DF.Check
|
||||||
allow_multi_currency_invoices_against_single_party_account: DF.Check
|
allow_multi_currency_invoices_against_single_party_account: DF.Check
|
||||||
@@ -50,7 +48,6 @@ class AccountsSettings(Document):
|
|||||||
enable_party_matching: DF.Check
|
enable_party_matching: DF.Check
|
||||||
exchange_gain_loss_posting_date: DF.Literal["Invoice", "Payment", "Reconciliation Date"]
|
exchange_gain_loss_posting_date: DF.Literal["Invoice", "Payment", "Reconciliation Date"]
|
||||||
fetch_valuation_rate_for_internal_transaction: DF.Check
|
fetch_valuation_rate_for_internal_transaction: DF.Check
|
||||||
frozen_accounts_modifier: DF.Link | None
|
|
||||||
general_ledger_remarks_length: DF.Int
|
general_ledger_remarks_length: DF.Int
|
||||||
ignore_account_closing_balance: DF.Check
|
ignore_account_closing_balance: DF.Check
|
||||||
ignore_is_opening_check_for_reporting: 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:
|
if old_doc.show_payment_schedule_in_print != self.show_payment_schedule_in_print:
|
||||||
self.enable_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:
|
if clear_cache:
|
||||||
frappe.clear_cache()
|
frappe.clear_cache()
|
||||||
|
|
||||||
@@ -130,10 +124,6 @@ class AccountsSettings(Document):
|
|||||||
validate_fields_for_doctype=False,
|
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):
|
def validate_and_sync_auto_reconcile_config(self):
|
||||||
if self.has_value_changed("auto_reconciliation_job_trigger"):
|
if self.has_value_changed("auto_reconciliation_job_trigger"):
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
frappe.ui.form.on("Accounts Settings", {
|
frappe.ui.form.on("Accounts Settings", {
|
||||||
refresh: function (frm) {
|
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");
|
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"
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ class GLEntry(Document):
|
|||||||
self.validate_account_details(adv_adj)
|
self.validate_account_details(adv_adj)
|
||||||
self.validate_dimensions_for_pl_and_bs()
|
self.validate_dimensions_for_pl_and_bs()
|
||||||
validate_balance_type(self.account, adv_adj)
|
validate_balance_type(self.account, adv_adj)
|
||||||
validate_frozen_account(self.account, adv_adj)
|
validate_frozen_account(self.company, self.account, adv_adj)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
self.voucher_type == "Journal Entry"
|
self.voucher_type == "Journal Entry"
|
||||||
@@ -276,7 +276,7 @@ class GLEntry(Document):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def validate_party(self):
|
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)
|
validate_account_party_type(self)
|
||||||
|
|
||||||
def validate_currency(self):
|
def validate_currency(self):
|
||||||
@@ -419,16 +419,16 @@ def update_outstanding_amt(
|
|||||||
ref_doc.set_status(update=True)
|
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")
|
frozen_account = frappe.get_cached_value("Account", account, "freeze_account")
|
||||||
if frozen_account == "Yes" and not adv_adj:
|
if frozen_account == "Yes" and not adv_adj:
|
||||||
frozen_accounts_modifier = frappe.get_cached_value(
|
role_allowed_for_frozen_entries = frappe.get_cached_value(
|
||||||
"Accounts Settings", None, "frozen_accounts_modifier"
|
"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))
|
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))
|
frappe.throw(_("Not authorized to edit frozen Account {0}").format(account))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ class PaymentLedgerEntry(Document):
|
|||||||
def on_update(self):
|
def on_update(self):
|
||||||
adv_adj = self.flags.adv_adj
|
adv_adj = self.flags.adv_adj
|
||||||
if not self.flags.from_repost:
|
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:
|
if not self.delinked:
|
||||||
self.validate_account_details()
|
self.validate_account_details()
|
||||||
self.validate_dimensions_for_pl_and_bs()
|
self.validate_dimensions_for_pl_and_bs()
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ from erpnext.stock.doctype.item.test_item import create_item
|
|||||||
class TestProcessDeferredAccounting(IntegrationTestCase):
|
class TestProcessDeferredAccounting(IntegrationTestCase):
|
||||||
def test_creation_of_ledger_entry_on_submit(self):
|
def test_creation_of_ledger_entry_on_submit(self):
|
||||||
"""test creation of gl entries on submission of document"""
|
"""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(
|
deferred_account = create_account(
|
||||||
account_name="Deferred Revenue for Accounts Frozen",
|
account_name="Deferred Revenue for Accounts Frozen",
|
||||||
@@ -92,8 +92,10 @@ class TestProcessDeferredAccounting(IntegrationTestCase):
|
|||||||
pda.cancel()
|
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 = 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
|
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()
|
acc_settings.save()
|
||||||
|
|||||||
@@ -63,7 +63,8 @@ class TestSalesInvoice(ERPNextTestSuite):
|
|||||||
set_default_account_for_mode_of_payment(
|
set_default_account_for_mode_of_payment(
|
||||||
mode_of_payment, "_Test Company with perpetual inventory", "_Test Bank - TCP1"
|
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(
|
@change_settings(
|
||||||
"Accounts Settings",
|
"Accounts Settings",
|
||||||
@@ -3398,8 +3399,8 @@ class TestSalesInvoice(ERPNextTestSuite):
|
|||||||
si.commission_rate = commission_rate
|
si.commission_rate = commission_rate
|
||||||
self.assertRaises(frappe.ValidationError, si.save)
|
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):
|
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 = create_sales_invoice(do_not_save=True)
|
||||||
si.posting_date = add_days(getdate(), 1)
|
si.posting_date = add_days(getdate(), 1)
|
||||||
si.save()
|
si.save()
|
||||||
@@ -3407,6 +3408,7 @@ class TestSalesInvoice(ERPNextTestSuite):
|
|||||||
self.assertRaises(frappe.ValidationError, si.submit)
|
self.assertRaises(frappe.ValidationError, si.submit)
|
||||||
si.posting_date = getdate()
|
si.posting_date = getdate()
|
||||||
si.submit()
|
si.submit()
|
||||||
|
frappe.db.set_value("Company", "_Test Company", "accounts_frozen_till_date", None)
|
||||||
|
|
||||||
@IntegrationTestCase.change_settings("Accounts Settings", {"over_billing_allowance": 0})
|
@IntegrationTestCase.change_settings("Accounts Settings", {"over_billing_allowance": 0})
|
||||||
def test_over_billing_case_against_delivery_note(self):
|
def test_over_billing_case_against_delivery_note(self):
|
||||||
@@ -3473,7 +3475,7 @@ class TestSalesInvoice(ERPNextTestSuite):
|
|||||||
si.save()
|
si.save()
|
||||||
si.submit()
|
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(
|
pda1 = frappe.get_doc(
|
||||||
dict(
|
dict(
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ class TestSubscription(IntegrationTestCase):
|
|||||||
make_plans()
|
make_plans()
|
||||||
create_parties()
|
create_parties()
|
||||||
reset_settings()
|
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):
|
def tearDown(self):
|
||||||
frappe.db.rollback()
|
frappe.db.rollback()
|
||||||
|
|||||||
@@ -406,7 +406,7 @@ def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False):
|
|||||||
|
|
||||||
dimension_filter_map = get_dimension_filter_map()
|
dimension_filter_map = get_dimension_filter_map()
|
||||||
if gl_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)
|
is_opening = any(d.get("is_opening") == "Yes" for d in gl_map)
|
||||||
if gl_map[0]["voucher_type"] != "Period Closing Voucher":
|
if gl_map[0]["voucher_type"] != "Period Closing Voucher":
|
||||||
validate_against_pcv(is_opening, gl_map[0]["posting_date"], gl_map[0]["company"])
|
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")
|
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
|
Nobody can do GL Entries where posting date is before freezing date
|
||||||
except authorized person
|
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
|
Hence stop admin to bypass if accounts are freezed
|
||||||
"""
|
"""
|
||||||
if not adv_adj:
|
if not adv_adj:
|
||||||
acc_frozen_upto = frappe.get_single_value("Accounts Settings", "acc_frozen_upto")
|
acc_frozen_till_date = frappe.db.get_value("Company", company, "accounts_frozen_till_date")
|
||||||
if acc_frozen_upto:
|
if acc_frozen_till_date:
|
||||||
frozen_accounts_modifier = frappe.get_single_value(
|
frozen_accounts_modifier = frappe.db.get_value(
|
||||||
"Accounts Settings", "frozen_accounts_modifier"
|
"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"
|
frozen_accounts_modifier not in frappe.get_roles() or frappe.session.user == "Administrator"
|
||||||
):
|
):
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("You are not authorized to add or update entries before {0}").format(
|
_("You are not authorized to add or update entries before {0}").format(
|
||||||
formatdate(acc_frozen_upto)
|
formatdate(acc_frozen_till_date)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -795,7 +795,7 @@ def get_payment_terms_template(party_name, party_type, company=None):
|
|||||||
return template
|
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:
|
if frappe.flags.ignore_party_validation:
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -805,10 +805,10 @@ def validate_party_frozen_disabled(party_type, party_name):
|
|||||||
if party.disabled:
|
if party.disabled:
|
||||||
frappe.throw(_("{0} {1} is disabled").format(party_type, party_name), PartyDisabled)
|
frappe.throw(_("{0} {1} is disabled").format(party_type, party_name), PartyDisabled)
|
||||||
elif party.get("is_frozen"):
|
elif party.get("is_frozen"):
|
||||||
frozen_accounts_modifier = frappe.get_single_value(
|
role_allowed_for_frozen_entries = frappe.get_cached_value(
|
||||||
"Accounts Settings", "frozen_accounts_modifier"
|
"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)
|
frappe.throw(_("{0} {1} is frozen").format(party_type, party_name), PartyFrozen)
|
||||||
|
|
||||||
elif party_type == "Employee":
|
elif party_type == "Employee":
|
||||||
|
|||||||
@@ -96,13 +96,28 @@ def get_depreciable_assets_data(date):
|
|||||||
.orderby(a.creation, order=Order.desc)
|
.orderby(a.creation, order=Order.desc)
|
||||||
)
|
)
|
||||||
|
|
||||||
acc_frozen_upto = get_acc_frozen_upto()
|
companies_with_frozen_limits = get_companies_with_frozen_limits()
|
||||||
if acc_frozen_upto:
|
|
||||||
res = res.where(ds.schedule_date > acc_frozen_upto)
|
|
||||||
|
|
||||||
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):
|
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)
|
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):
|
def get_credit_debit_accounts_for_asset(asset_category, company):
|
||||||
# Returns credit and debit accounts for the given asset category and 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)
|
(_, accumulated_depr_account, depr_expense_account) = get_depreciation_accounts(asset_category, company)
|
||||||
|
|||||||
@@ -2357,7 +2357,7 @@ class AccountsController(TransactionBase):
|
|||||||
|
|
||||||
def validate_party(self):
|
def validate_party(self):
|
||||||
party_type, party = self.get_party()
|
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):
|
def get_party(self):
|
||||||
party_type = None
|
party_type = None
|
||||||
|
|||||||
@@ -450,3 +450,4 @@ erpnext.patches.v16_0.set_valuation_method_on_companies
|
|||||||
erpnext.patches.v15_0.migrate_old_item_wise_tax_detail_data_to_table
|
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.migrate_budget_records_to_new_structure
|
||||||
erpnext.patches.v16_0.update_currency_exchange_settings_for_frankfurter
|
erpnext.patches.v16_0.update_currency_exchange_settings_for_frankfurter
|
||||||
|
erpnext.patches.v16_0.migrate_account_freezing_settings_to_company
|
||||||
@@ -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,
|
||||||
|
},
|
||||||
|
)
|
||||||
@@ -95,6 +95,11 @@
|
|||||||
"depreciation_cost_center",
|
"depreciation_cost_center",
|
||||||
"capital_work_in_progress_account",
|
"capital_work_in_progress_account",
|
||||||
"asset_received_but_not_billed",
|
"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",
|
"buying_and_selling_tab",
|
||||||
"sales_settings",
|
"sales_settings",
|
||||||
"default_buying_terms",
|
"default_buying_terms",
|
||||||
@@ -930,8 +935,36 @@
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Default Sales Contact",
|
"label": "Default Sales Contact",
|
||||||
"options": "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",
|
"icon": "fa fa-building",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"image_field": "company_logo",
|
"image_field": "company_logo",
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ from erpnext.accounts.doctype.financial_report_template.financial_report_templat
|
|||||||
sync_financial_report_templates,
|
sync_financial_report_templates,
|
||||||
)
|
)
|
||||||
from erpnext.setup.setup_wizard.operations.taxes_setup import setup_taxes_and_charges
|
from erpnext.setup.setup_wizard.operations.taxes_setup import setup_taxes_and_charges
|
||||||
|
from erpnext.stock.utils import check_pending_reposting
|
||||||
|
|
||||||
|
|
||||||
class Company(NestedSet):
|
class Company(NestedSet):
|
||||||
@@ -31,6 +32,7 @@ class Company(NestedSet):
|
|||||||
from frappe.types import DF
|
from frappe.types import DF
|
||||||
|
|
||||||
abbr: DF.Data
|
abbr: DF.Data
|
||||||
|
accounts_frozen_till_date: DF.Date | None
|
||||||
accumulated_depreciation_account: DF.Link | None
|
accumulated_depreciation_account: DF.Link | None
|
||||||
allow_account_creation_against_child_company: DF.Check
|
allow_account_creation_against_child_company: DF.Check
|
||||||
asset_received_but_not_billed: DF.Link | None
|
asset_received_but_not_billed: DF.Link | None
|
||||||
@@ -103,6 +105,7 @@ class Company(NestedSet):
|
|||||||
registration_details: DF.Code | None
|
registration_details: DF.Code | None
|
||||||
reporting_currency: DF.Link | None
|
reporting_currency: DF.Link | None
|
||||||
rgt: DF.Int
|
rgt: DF.Int
|
||||||
|
role_allowed_for_frozen_entries: DF.Link | None
|
||||||
round_off_account: DF.Link | None
|
round_off_account: DF.Link | None
|
||||||
round_off_cost_center: DF.Link | None
|
round_off_cost_center: DF.Link | None
|
||||||
round_off_for_opening: DF.Link | None
|
round_off_for_opening: DF.Link | None
|
||||||
@@ -151,6 +154,7 @@ class Company(NestedSet):
|
|||||||
return exists
|
return exists
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
|
old_doc = self.get_doc_before_save()
|
||||||
self.update_default_account = False
|
self.update_default_account = False
|
||||||
if self.is_new():
|
if self.is_new():
|
||||||
self.update_default_account = True
|
self.update_default_account = True
|
||||||
@@ -169,6 +173,7 @@ class Company(NestedSet):
|
|||||||
self.set_reporting_currency()
|
self.set_reporting_currency()
|
||||||
self.validate_inventory_account_settings()
|
self.validate_inventory_account_settings()
|
||||||
self.cant_change_valuation_method()
|
self.cant_change_valuation_method()
|
||||||
|
self.validate_pending_reposts(old_doc)
|
||||||
|
|
||||||
def cant_change_valuation_method(self):
|
def cant_change_valuation_method(self):
|
||||||
doc_before_save = self.get_doc_before_save()
|
doc_before_save = self.get_doc_before_save()
|
||||||
@@ -597,6 +602,11 @@ class Company(NestedSet):
|
|||||||
)
|
)
|
||||||
self.reporting_currency = parent_reporting_currency
|
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):
|
def set_default_accounts(self):
|
||||||
default_accounts = {
|
default_accounts = {
|
||||||
"default_cash_account": "Cash",
|
"default_cash_account": "Cash",
|
||||||
|
|||||||
@@ -168,16 +168,19 @@ class RepostItemValuation(Document):
|
|||||||
return query[0][0] if query and query[0][0] else None
|
return query[0][0] if query and query[0][0] else None
|
||||||
|
|
||||||
def validate_accounts_freeze(self):
|
def validate_accounts_freeze(self):
|
||||||
acc_settings = frappe.get_cached_doc("Accounts Settings")
|
acc_frozen_till_date = frappe.db.get_value("Company", self.company, "accounts_frozen_till_date")
|
||||||
if not acc_settings.acc_frozen_upto:
|
frozen_accounts_modifier = frappe.db.get_value(
|
||||||
|
"Company", self.company, "role_allowed_for_frozen_entries"
|
||||||
|
)
|
||||||
|
if not acc_frozen_till_date:
|
||||||
return
|
return
|
||||||
if getdate(self.posting_date) <= getdate(acc_settings.acc_frozen_upto):
|
if getdate(self.posting_date) <= getdate(acc_frozen_till_date):
|
||||||
if acc_settings.frozen_accounts_modifier and frappe.session.user in get_users_with_role(
|
if frozen_accounts_modifier and frappe.session.user in get_users_with_role(
|
||||||
acc_settings.frozen_accounts_modifier
|
frozen_accounts_modifier
|
||||||
):
|
):
|
||||||
frappe.msgprint(_("Caution: This might alter frozen accounts."))
|
frappe.msgprint(_("Caution: This might alter frozen accounts."))
|
||||||
return
|
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):
|
def reset_field_values(self):
|
||||||
if self.based_on == "Transaction":
|
if self.based_on == "Transaction":
|
||||||
|
|||||||
@@ -356,6 +356,7 @@ class TestRepostItemValuation(IntegrationTestCase, StockTestMixin):
|
|||||||
riv = frappe.get_doc(
|
riv = frappe.get_doc(
|
||||||
doctype="Repost Item Valuation",
|
doctype="Repost Item Valuation",
|
||||||
item_code="_Test Item",
|
item_code="_Test Item",
|
||||||
|
company="_Test Company",
|
||||||
warehouse="_Test Warehouse - _TC",
|
warehouse="_Test Warehouse - _TC",
|
||||||
based_on="Item and Warehouse",
|
based_on="Item and Warehouse",
|
||||||
posting_date=today,
|
posting_date=today,
|
||||||
@@ -363,15 +364,15 @@ class TestRepostItemValuation(IntegrationTestCase, StockTestMixin):
|
|||||||
)
|
)
|
||||||
riv.flags.dont_run_in_test = True # keep it queued
|
riv.flags.dont_run_in_test = True # keep it queued
|
||||||
|
|
||||||
accounts_settings = frappe.get_doc("Accounts Settings")
|
company = frappe.get_doc("Company", "_Test Company")
|
||||||
accounts_settings.acc_frozen_upto = today
|
company.accounts_frozen_till_date = today
|
||||||
accounts_settings.frozen_accounts_modifier = ""
|
company.role_allowed_for_frozen_entries = ""
|
||||||
accounts_settings.save()
|
company.save()
|
||||||
|
|
||||||
self.assertRaises(frappe.ValidationError, riv.save)
|
self.assertRaises(frappe.ValidationError, riv.save)
|
||||||
|
|
||||||
accounts_settings.acc_frozen_upto = ""
|
company.accounts_frozen_till_date = ""
|
||||||
accounts_settings.save()
|
company.save()
|
||||||
|
|
||||||
@IntegrationTestCase.change_settings("Stock Reposting Settings", {"item_based_reposting": 0})
|
@IntegrationTestCase.change_settings("Stock Reposting Settings", {"item_based_reposting": 0})
|
||||||
def test_create_repost_entry_for_cancelled_document(self):
|
def test_create_repost_entry_for_cancelled_document(self):
|
||||||
|
|||||||
@@ -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."""
|
"""Check if there are pending reposting job till the specified posting date."""
|
||||||
|
|
||||||
filters = {
|
filters = {
|
||||||
@@ -568,6 +568,8 @@ def check_pending_reposting(posting_date: str, throw_error: bool = True) -> bool
|
|||||||
"status": ["in", ["Queued", "In Progress"]],
|
"status": ["in", ["Queued", "In Progress"]],
|
||||||
"posting_date": ["<=", posting_date],
|
"posting_date": ["<=", posting_date],
|
||||||
}
|
}
|
||||||
|
if company:
|
||||||
|
filters["company"] = company
|
||||||
|
|
||||||
reposting_pending = frappe.db.exists("Repost Item Valuation", filters)
|
reposting_pending = frappe.db.exists("Repost Item Valuation", filters)
|
||||||
if reposting_pending and throw_error:
|
if reposting_pending and throw_error:
|
||||||
|
|||||||
Reference in New Issue
Block a user