From dc3c27fd1b6382c1f1e7a6cdbe4af3826859b247 Mon Sep 17 00:00:00 2001 From: Saif Ur Rehman Date: Thu, 4 Nov 2021 13:47:33 +0500 Subject: [PATCH] fix(Asset Capitalization): update code for changes in depreciation logic --- .../doctype/sales_invoice/sales_invoice.py | 86 +------------------ erpnext/assets/doctype/asset/asset.py | 14 +-- .../asset_capitalization.py | 2 +- .../test_asset_capitalization.py | 10 +-- erpnext/controllers/accounts_controller.py | 41 +++++---- 5 files changed, 37 insertions(+), 116 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 2b3850e513..7ed45ce26c 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -27,7 +27,6 @@ from erpnext.assets.doctype.asset.depreciation import ( get_disposal_account_and_cost_center, get_gl_entries_on_asset_disposal, get_gl_entries_on_asset_regain, - make_depreciation_entry, ) from erpnext.controllers.selling_controller import SellingController from erpnext.projects.doctype.timesheet.timesheet import get_projectwise_timesheet_data @@ -924,7 +923,7 @@ class SalesInvoice(SellingController): asset.db_set("disposal_date", None) if asset.calculate_depreciation: - self.reverse_depreciation_entry_made_after_sale(asset) + self.reverse_depreciation_entry_made_after_disposal(asset) self.reset_depreciation_schedule(asset) else: @@ -980,89 +979,6 @@ class SalesInvoice(SellingController): self.check_finance_books(item, asset) return asset - def check_finance_books(self, item, asset): - if (len(asset.finance_books) > 1 and not item.finance_book - and asset.finance_books[0].finance_book): - frappe.throw(_("Select finance book for the item {0} at row {1}") - .format(item.item_code, item.idx)) - - def depreciate_asset(self, asset): - asset.flags.ignore_validate_update_after_submit = True - asset.prepare_depreciation_data(date_of_sale=self.posting_date) - asset.save() - - make_depreciation_entry(asset.name, self.posting_date) - - def reset_depreciation_schedule(self, asset): - asset.flags.ignore_validate_update_after_submit = True - - # recreate original depreciation schedule of the asset - asset.prepare_depreciation_data(date_of_return=self.posting_date) - - self.modify_depreciation_schedule_for_asset_repairs(asset) - asset.save() - - def modify_depreciation_schedule_for_asset_repairs(self, asset): - asset_repairs = frappe.get_all( - 'Asset Repair', - filters = {'asset': asset.name}, - fields = ['name', 'increase_in_asset_life'] - ) - - for repair in asset_repairs: - if repair.increase_in_asset_life: - asset_repair = frappe.get_doc('Asset Repair', repair.name) - asset_repair.modify_depreciation_schedule() - asset.prepare_depreciation_data() - - def reverse_depreciation_entry_made_after_sale(self, asset): - from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry - - posting_date_of_original_invoice = self.get_posting_date_of_sales_invoice() - - row = -1 - finance_book = asset.get('schedules')[0].get('finance_book') - for schedule in asset.get('schedules'): - if schedule.finance_book != finance_book: - row = 0 - finance_book = schedule.finance_book - else: - row += 1 - - if schedule.schedule_date == posting_date_of_original_invoice: - if not self.sale_was_made_on_original_schedule_date(asset, schedule, row, posting_date_of_original_invoice) \ - or self.sale_happens_in_the_future(posting_date_of_original_invoice): - - reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry) - reverse_journal_entry.posting_date = nowdate() - frappe.flags.is_reverse_depr_entry = True - reverse_journal_entry.submit() - - frappe.flags.is_reverse_depr_entry = False - asset.flags.ignore_validate_update_after_submit = True - schedule.journal_entry = None - asset.save() - - def get_posting_date_of_sales_invoice(self): - return frappe.db.get_value('Sales Invoice', self.return_against, 'posting_date') - - # if the invoice had been posted on the date the depreciation was initially supposed to happen, the depreciation shouldn't be undone - def sale_was_made_on_original_schedule_date(self, asset, schedule, row, posting_date_of_original_invoice): - for finance_book in asset.get('finance_books'): - if schedule.finance_book == finance_book.finance_book: - orginal_schedule_date = add_months(finance_book.depreciation_start_date, - row * cint(finance_book.frequency_of_depreciation)) - - if orginal_schedule_date == posting_date_of_original_invoice: - return True - return False - - def sale_happens_in_the_future(self, posting_date_of_original_invoice): - if posting_date_of_original_invoice > getdate(): - return True - - return False - @property def enable_discount_accounting(self): if not hasattr(self, "_enable_discount_accounting"): diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index cf62f496ea..7c05488db5 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -75,12 +75,12 @@ class Asset(AccountsController): if self.is_existing_asset and self.purchase_invoice: frappe.throw(_("Purchase Invoice cannot be made against an existing asset {0}").format(self.name)) - def prepare_depreciation_data(self, date_of_sale=None, date_of_return=None): + def prepare_depreciation_data(self, date_of_disposal=None, date_of_return=None): if self.calculate_depreciation: self.value_after_depreciation = 0 self.set_depreciation_rate() - self.make_depreciation_schedule(date_of_sale) - self.set_accumulated_depreciation(date_of_sale, date_of_return) + self.make_depreciation_schedule(date_of_disposal) + self.set_accumulated_depreciation(date_of_disposal, date_of_return) else: self.finance_books = [] self.value_after_depreciation = (flt(self.gross_purchase_amount) - @@ -181,7 +181,7 @@ class Asset(AccountsController): d.rate_of_depreciation = flt(self.get_depreciation_rate(d, on_validate=True), d.precision("rate_of_depreciation")) - def make_depreciation_schedule(self, date_of_sale): + def make_depreciation_schedule(self, date_of_disposal): if 'Manual' not in [d.depreciation_method for d in self.finance_books] and not self.get('schedules'): self.schedules = [] @@ -227,14 +227,14 @@ class Asset(AccountsController): monthly_schedule_date = add_months(schedule_date, - d.frequency_of_depreciation + 1) # if asset is being sold - if date_of_sale: + if date_of_disposal: from_date = self.get_from_date(d.finance_book) depreciation_amount, days, months = self.get_pro_rata_amt(d, depreciation_amount, - from_date, date_of_sale) + from_date, date_of_disposal) if depreciation_amount > 0: self.append("schedules", { - "schedule_date": date_of_sale, + "schedule_date": date_of_disposal, "depreciation_amount": depreciation_amount, "depreciation_method": d.depreciation_method, "finance_book": d.finance_book, diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index 5a2398650b..7d08581cbe 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -375,8 +375,8 @@ class AssetCapitalization(StockController): else: if asset.calculate_depreciation: self.depreciate_asset(asset) + asset.reload() - asset.reload() fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(asset, item.asset_value, item.get('finance_book') or self.get('finance_book')) asset.db_set("disposal_date", self.posting_date) diff --git a/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py index 9bfc88b28c..5a342f7d2f 100644 --- a/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py @@ -118,11 +118,11 @@ class TestAssetCapitalization(unittest.TestCase): depreciation_before_disposal_amount = 15_000 accumulated_depreciation = 45_000 - # to accomodate for depreciation on disposal calculation bugs TODO remove this when bug is fixed - consumed_asset_value_before_disposal = 60_082.19 - target_incoming_rate = 6008.219 - depreciation_before_disposal_amount = 9917.81 - accumulated_depreciation = 39_917.81 + # to accomodate for depreciation on disposal calculation minor difference + consumed_asset_value_before_disposal = 55_123.29 + target_incoming_rate = 5512.329 + depreciation_before_disposal_amount = 14_876.71 + accumulated_depreciation = 44_876.71 # Create assets consumed_asset = create_depreciation_asset( diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 9d19639020..6b681ffee5 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -38,7 +38,7 @@ from erpnext.accounts.party import ( validate_party_frozen_disabled, ) from erpnext.accounts.utils import get_account_currency, get_fiscal_years, validate_fiscal_year -from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries +from erpnext.assets.doctype.asset.depreciation import make_depreciation_entry from erpnext.buying.utils import update_last_purchase_rate from erpnext.controllers.print_settings import ( set_print_templates_for_item_table, @@ -1516,17 +1516,16 @@ class AccountsController(TransactionBase): def depreciate_asset(self, asset): asset.flags.ignore_validate_update_after_submit = True - asset.prepare_depreciation_data(self.posting_date) + asset.prepare_depreciation_data(date_of_disposal=self.posting_date) asset.save() - post_depreciation_entries(self.posting_date, commit=False) + make_depreciation_entry(asset.name, self.posting_date) def reset_depreciation_schedule(self, asset): asset.flags.ignore_validate_update_after_submit = True # recreate original depreciation schedule of the asset - self.delete_depreciation_entry_made_after_disposal(asset) - asset.prepare_depreciation_data() + asset.prepare_depreciation_data(date_of_return=self.posting_date) self.modify_depreciation_schedule_for_asset_repairs(asset) asset.save() @@ -1544,10 +1543,10 @@ class AccountsController(TransactionBase): asset_repair.modify_depreciation_schedule() asset.prepare_depreciation_data() - def delete_depreciation_entry_made_after_disposal(self, asset): + def reverse_depreciation_entry_made_after_disposal(self, asset): from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry - posting_date_of_original_invoice = self.get_posting_date_of_disposal_entry() + posting_date_of_original_disposal = self.get_posting_date_of_disposal_entry() row = -1 finance_book = asset.get('schedules')[0].get('finance_book') @@ -1558,19 +1557,19 @@ class AccountsController(TransactionBase): else: row += 1 - if schedule.schedule_date == posting_date_of_original_invoice: - if not self.disposal_was_made_on_original_schedule_date(asset, schedule, row, - posting_date_of_original_invoice) or getdate(schedule.schedule_date) > getdate(today()): + if schedule.schedule_date == posting_date_of_original_disposal: + if not self.disposal_was_made_on_original_schedule_date(asset, schedule, row, posting_date_of_original_disposal) \ + or self.disposal_happens_in_the_future(posting_date_of_original_disposal): + reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry) reverse_journal_entry.posting_date = nowdate() - - for d in reverse_journal_entry.accounts: - d.reference_type = "Asset" - d.reference_name = asset.name - + frappe.flags.is_reverse_depr_entry = True reverse_journal_entry.submit() - schedule.db_set('journal_entry', None) + frappe.flags.is_reverse_depr_entry = False + asset.flags.ignore_validate_update_after_submit = True + schedule.journal_entry = None + asset.save() def get_posting_date_of_disposal_entry(self): if self.doctype == "Sales Invoice" and self.return_against: @@ -1579,16 +1578,22 @@ class AccountsController(TransactionBase): return self.posting_date # if the invoice had been posted on the date the depreciation was initially supposed to happen, the depreciation shouldn't be undone - def disposal_was_made_on_original_schedule_date(self, asset, schedule, row, posting_date_of_original_disposal): + def disposal_was_made_on_original_schedule_date(self, asset, schedule, row, posting_date_of_disposal): for finance_book in asset.get('finance_books'): if schedule.finance_book == finance_book.finance_book: orginal_schedule_date = add_months(finance_book.depreciation_start_date, row * cint(finance_book.frequency_of_depreciation)) - if orginal_schedule_date == posting_date_of_original_disposal: + if orginal_schedule_date == posting_date_of_disposal: return True return False + def disposal_happens_in_the_future(self, posting_date_of_disposal): + if posting_date_of_disposal > getdate(): + return True + + return False + @frappe.whitelist() def get_tax_rate(account_head): return frappe.db.get_value("Account", account_head, ["tax_rate", "account_name"], as_dict=True)