From 6d6a7bcb5054ade439bb86e1c767778ebd46b98a Mon Sep 17 00:00:00 2001 From: Anand Baburajan Date: Tue, 21 Mar 2023 20:07:27 +0530 Subject: [PATCH] fix: incorrect depr schedules after asset repair [develop] (#34544) * fix: backport missing changes from #30838 * fix: incorrect schedule after repair --- erpnext/assets/doctype/asset/asset.py | 30 +++-- .../asset_depreciation_schedule.py | 17 ++- .../doctype/asset_repair/asset_repair.py | 117 ++++++++++++------ 3 files changed, 107 insertions(+), 57 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index e1d58a0264..1614c28986 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -79,6 +79,7 @@ class Asset(AccountsController): def after_insert(self): if not self.split_from: make_draft_asset_depr_schedules(self) + self.validate_expected_value_after_useful_life() def validate_asset_and_reference(self): if self.purchase_invoice or self.purchase_receipt: @@ -325,13 +326,13 @@ class Asset(AccountsController): def validate_expected_value_after_useful_life(self): for row in self.get("finance_books"): - depr_schedule = get_depr_schedule(self.name, "Draft", row.finance_book) + asset_depr_schedule_doc = get_asset_depr_schedule_doc(self.name, "Draft", row.finance_book) - if not depr_schedule: + if not asset_depr_schedule_doc: continue accumulated_depreciation_after_full_schedule = [ - d.accumulated_depreciation_amount for d in depr_schedule + d.accumulated_depreciation_amount for d in asset_depr_schedule_doc.get("depreciation_schedule") ] if accumulated_depreciation_after_full_schedule: @@ -355,6 +356,9 @@ class Asset(AccountsController): ) elif not row.expected_value_after_useful_life: row.expected_value_after_useful_life = asset_value_after_full_schedule + asset_depr_schedule_doc.db_set( + "expected_value_after_useful_life", asset_value_after_full_schedule + ) def validate_cancellation(self): if self.status in ("In Maintenance", "Out of Order"): @@ -474,17 +478,21 @@ class Asset(AccountsController): @erpnext.allow_regional def get_depreciation_amount(self, depreciable_value, fb_row): if fb_row.depreciation_method in ("Straight Line", "Manual"): - # if the Depreciation Schedule is being prepared for the first time - if not self.flags.increase_in_asset_life: - depreciation_amount = ( - flt(self.gross_purchase_amount) - flt(fb_row.expected_value_after_useful_life) - ) / flt(fb_row.total_number_of_depreciations) - - # if the Depreciation Schedule is being modified after Asset Repair - else: + # if the Depreciation Schedule is being modified after Asset Repair due to increase in asset life and value + if self.flags.increase_in_asset_life: depreciation_amount = ( flt(fb_row.value_after_depreciation) - flt(fb_row.expected_value_after_useful_life) ) / (date_diff(self.to_date, self.available_for_use_date) / 365) + # if the Depreciation Schedule is being modified after Asset Repair due to increase in asset value + elif self.flags.increase_in_asset_value_due_to_repair: + depreciation_amount = ( + flt(fb_row.value_after_depreciation) - flt(fb_row.expected_value_after_useful_life) + ) / flt(fb_row.total_number_of_depreciations) + # if the Depreciation Schedule is being prepared for the first time + else: + depreciation_amount = ( + flt(self.gross_purchase_amount) - flt(fb_row.expected_value_after_useful_life) + ) / flt(fb_row.total_number_of_depreciations) else: depreciation_amount = flt(depreciable_value * (flt(fb_row.rate_of_depreciation) / 100)) diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py index 5912329846..1d90baab35 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -283,12 +283,19 @@ class AssetDepreciationSchedule(Document): ) # Adjust depreciation amount in the last period based on the expected value after useful life - if row.expected_value_after_useful_life and ( - ( - n == cint(number_of_pending_depreciations) - 1 - and value_after_depreciation != row.expected_value_after_useful_life + if ( + row.expected_value_after_useful_life + and ( + ( + n == cint(number_of_pending_depreciations) - 1 + and value_after_depreciation != row.expected_value_after_useful_life + ) + or value_after_depreciation < row.expected_value_after_useful_life + ) + and ( + not asset_doc.flags.increase_in_asset_value_due_to_repair + or not row.depreciation_method in ("Written Down Value", "Double Declining Balance") ) - or value_after_depreciation < row.expected_value_after_useful_life ): depreciation_amount += value_after_depreciation - row.expected_value_after_useful_life skip_row = True diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index a7172a72c6..3ef8d5db65 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -9,6 +9,7 @@ import erpnext from erpnext.accounts.general_ledger import make_gl_entries from erpnext.assets.doctype.asset.asset import get_asset_account from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule import ( + get_asset_depr_schedule_doc, get_depr_schedule, make_new_active_asset_depr_schedules_and_cancel_current_ones, ) @@ -43,53 +44,61 @@ class AssetRepair(AccountsController): def before_submit(self): self.check_repair_status() - if self.get("stock_consumption") or self.get("capitalize_repair_cost"): - self.increase_asset_value() - if self.get("stock_consumption"): - self.check_for_stock_items_and_warehouse() - self.decrease_stock_quantity() - if self.get("capitalize_repair_cost"): - self.make_gl_entries() - if ( - frappe.db.get_value("Asset", self.asset, "calculate_depreciation") - and self.increase_in_asset_life - ): - self.modify_depreciation_schedule() + self.asset_doc.flags.increase_in_asset_value_due_to_repair = False - notes = _( - "This schedule was created when Asset {0} was repaired through Asset Repair {1}." - ).format( - get_link_to_form(self.asset_doc.doctype, self.asset_doc.name), - get_link_to_form(self.doctype, self.name), - ) - self.asset_doc.flags.ignore_validate_update_after_submit = True - make_new_active_asset_depr_schedules_and_cancel_current_ones(self.asset_doc, notes) - self.asset_doc.save() + if self.get("stock_consumption") or self.get("capitalize_repair_cost"): + self.asset_doc.flags.increase_in_asset_value_due_to_repair = True + + self.increase_asset_value() + + if self.get("stock_consumption"): + self.check_for_stock_items_and_warehouse() + self.decrease_stock_quantity() + if self.get("capitalize_repair_cost"): + self.make_gl_entries() + if self.asset_doc.calculate_depreciation and self.increase_in_asset_life: + self.modify_depreciation_schedule() + + notes = _( + "This schedule was created when Asset {0} was repaired through Asset Repair {1}." + ).format( + get_link_to_form(self.asset_doc.doctype, self.asset_doc.name), + get_link_to_form(self.doctype, self.name), + ) + self.asset_doc.flags.ignore_validate_update_after_submit = True + make_new_active_asset_depr_schedules_and_cancel_current_ones(self.asset_doc, notes) + if self.asset_doc.calculate_depreciation: + self.update_asset_expected_value_after_useful_life() + self.asset_doc.save() def before_cancel(self): self.asset_doc = frappe.get_doc("Asset", self.asset) - if self.get("stock_consumption") or self.get("capitalize_repair_cost"): - self.decrease_asset_value() - if self.get("stock_consumption"): - self.increase_stock_quantity() - if self.get("capitalize_repair_cost"): - self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry") - self.make_gl_entries(cancel=True) - self.db_set("stock_entry", None) - if ( - frappe.db.get_value("Asset", self.asset, "calculate_depreciation") - and self.increase_in_asset_life - ): - self.revert_depreciation_schedule_on_cancellation() + self.asset_doc.flags.increase_in_asset_value_due_to_repair = False - notes = _("This schedule was created when Asset {0}'s Asset Repair {1} was cancelled.").format( - get_link_to_form(self.asset_doc.doctype, self.asset_doc.name), - get_link_to_form(self.doctype, self.name), - ) - self.asset_doc.flags.ignore_validate_update_after_submit = True - make_new_active_asset_depr_schedules_and_cancel_current_ones(self.asset_doc, notes) - self.asset_doc.save() + if self.get("stock_consumption") or self.get("capitalize_repair_cost"): + self.asset_doc.flags.increase_in_asset_value_due_to_repair = True + + self.decrease_asset_value() + + if self.get("stock_consumption"): + self.increase_stock_quantity() + if self.get("capitalize_repair_cost"): + self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry") + self.make_gl_entries(cancel=True) + self.db_set("stock_entry", None) + if self.asset_doc.calculate_depreciation and self.increase_in_asset_life: + self.revert_depreciation_schedule_on_cancellation() + + notes = _("This schedule was created when Asset {0}'s Asset Repair {1} was cancelled.").format( + get_link_to_form(self.asset_doc.doctype, self.asset_doc.name), + get_link_to_form(self.doctype, self.name), + ) + self.asset_doc.flags.ignore_validate_update_after_submit = True + make_new_active_asset_depr_schedules_and_cancel_current_ones(self.asset_doc, notes) + if self.asset_doc.calculate_depreciation: + self.update_asset_expected_value_after_useful_life() + self.asset_doc.save() def after_delete(self): frappe.get_doc("Asset", self.asset).set_status() @@ -109,6 +118,32 @@ class AssetRepair(AccountsController): title=_("Missing Warehouse"), ) + def update_asset_expected_value_after_useful_life(self): + for row in self.asset_doc.get("finance_books"): + if row.depreciation_method in ("Written Down Value", "Double Declining Balance"): + asset_depr_schedule_doc = get_asset_depr_schedule_doc( + self.asset_doc.name, "Active", row.finance_book + ) + + accumulated_depreciation_after_full_schedule = [ + d.accumulated_depreciation_amount + for d in asset_depr_schedule_doc.get("depreciation_schedule") + ] + + accumulated_depreciation_after_full_schedule = max( + accumulated_depreciation_after_full_schedule + ) + + asset_value_after_full_schedule = flt( + flt(row.value_after_depreciation) - flt(accumulated_depreciation_after_full_schedule), + row.precision("expected_value_after_useful_life"), + ) + + row.expected_value_after_useful_life = asset_value_after_full_schedule + asset_depr_schedule_doc.db_set( + "expected_value_after_useful_life", asset_value_after_full_schedule + ) + def increase_asset_value(self): total_value_of_stock_consumed = self.get_total_value_of_stock_consumed()