From e5da0d7a633954ec06cdeebd146e4d579330193e Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Fri, 3 Feb 2023 22:23:17 +0530 Subject: [PATCH] chore: refactor AssetDepreciationSchedule --- erpnext/assets/doctype/asset/asset.py | 35 +- erpnext/assets/doctype/asset/test_asset.py | 16 +- .../asset_depreciation_schedule.py | 576 ++++++++---------- .../asset_value_adjustment.py | 6 +- ...sset_depreciation_schedules_from_assets.py | 6 +- 5 files changed, 296 insertions(+), 343 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index e24c41d24ba..4f1cacaad50 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -36,7 +36,6 @@ from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_sched get_depr_schedule, make_draft_asset_depr_schedules, make_draft_asset_depr_schedules_if_not_present, - set_draft_asset_depr_schedule_details, update_draft_asset_depr_schedules, ) from erpnext.controllers.accounts_controller import AccountsController @@ -439,6 +438,17 @@ class Asset(AccountsController): if finance_book == row.finance_book: return row.value_after_depreciation + def _get_value_after_depreciation_for_making_schedule(self, fb_row): + # value_after_depreciation - current Asset value + if self.docstatus == 1 and fb_row.value_after_depreciation: + value_after_depreciation = flt(fb_row.value_after_depreciation) + else: + value_after_depreciation = flt(self.gross_purchase_amount) - flt( + self.opening_accumulated_depreciation + ) + + return value_after_depreciation + def get_default_finance_book_idx(self): if not self.get("default_finance_book") and self.company: self.default_finance_book = erpnext.get_default_finance_book(self.company) @@ -466,6 +476,25 @@ class Asset(AccountsController): return records + @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: + 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) + else: + depreciation_amount = flt(depreciable_value * (flt(fb_row.rate_of_depreciation) / 100)) + + return depreciation_amount + def validate_make_gl_entry(self): purchase_document = self.get_purchase_document() if not purchase_document: @@ -915,7 +944,7 @@ def update_existing_asset(asset, remaining_qty, new_asset_name): ) new_asset_depr_schedule_doc = frappe.copy_doc(current_asset_depr_schedule_doc) - set_draft_asset_depr_schedule_details(new_asset_depr_schedule_doc, asset, row) + new_asset_depr_schedule_doc.set_draft_asset_depr_schedule_details(asset, row) accumulated_depreciation = 0 @@ -967,7 +996,7 @@ def create_new_asset_after_split(asset, split_qty): ) new_asset_depr_schedule_doc = frappe.copy_doc(current_asset_depr_schedule_doc) - set_draft_asset_depr_schedule_details(new_asset_depr_schedule_doc, new_asset, row) + new_asset_depr_schedule_doc.set_draft_asset_depr_schedule_details(new_asset, row) accumulated_depreciation = 0 diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index a9af6706182..9a152638f90 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -29,7 +29,6 @@ from erpnext.assets.doctype.asset.depreciation import ( scrap_asset, ) from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule import ( - clear_depr_schedule, get_asset_depr_schedule_doc, get_depr_schedule, ) @@ -925,11 +924,6 @@ class TestDepreciationBasics(AssetSetup): def test_get_depreciation_amount(self): """Tests if get_depreciation_amount() returns the right value.""" - - from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule import ( - get_depreciation_amount, - ) - asset = create_asset(item_code="Macbook Pro", available_for_use_date="2019-12-31") asset.calculate_depreciation = 1 @@ -944,7 +938,7 @@ class TestDepreciationBasics(AssetSetup): }, ) - depreciation_amount = get_depreciation_amount(asset, 100000, asset.finance_books[0]) + depreciation_amount = asset.get_depreciation_amount(100000, asset.finance_books[0]) self.assertEqual(depreciation_amount, 30000) def test_make_depr_schedule(self): @@ -1260,7 +1254,7 @@ class TestDepreciationBasics(AssetSetup): asset_depr_schedule_doc = get_asset_depr_schedule_doc(asset.name, "Active") - clear_depr_schedule(asset_depr_schedule_doc) + asset_depr_schedule_doc.clear_depr_schedule() self.assertEqual(len(asset_depr_schedule_doc.get("depreciation_schedule")), 1) @@ -1309,19 +1303,19 @@ class TestDepreciationBasics(AssetSetup): asset_depr_schedule_doc_1 = get_asset_depr_schedule_doc( asset.name, "Active", "Test Finance Book 1" ) - clear_depr_schedule(asset_depr_schedule_doc_1) + asset_depr_schedule_doc_1.clear_depr_schedule() self.assertEqual(len(asset_depr_schedule_doc_1.get("depreciation_schedule")), 3) asset_depr_schedule_doc_2 = get_asset_depr_schedule_doc( asset.name, "Active", "Test Finance Book 2" ) - clear_depr_schedule(asset_depr_schedule_doc_2) + asset_depr_schedule_doc_2.clear_depr_schedule() self.assertEqual(len(asset_depr_schedule_doc_2.get("depreciation_schedule")), 3) asset_depr_schedule_doc_3 = get_asset_depr_schedule_doc( asset.name, "Active", "Test Finance Book 3" ) - clear_depr_schedule(asset_depr_schedule_doc_3) + asset_depr_schedule_doc_3.clear_depr_schedule() self.assertEqual(len(asset_depr_schedule_doc_3.get("depreciation_schedule")), 0) def test_depreciation_schedules_are_set_up_for_multiple_finance_books(self): 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 02e508a7fa2..7615fbc86f9 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -4,17 +4,7 @@ import frappe from frappe import _ from frappe.model.document import Document -from frappe.utils import ( - add_days, - add_months, - cint, - date_diff, - flt, - get_last_day, - is_last_day_of_the_month, -) - -import erpnext +from frappe.utils import add_days, add_months, cint, flt, get_last_day, is_last_day_of_the_month class AssetDepreciationSchedule(Document): @@ -83,7 +73,256 @@ class AssetDepreciationSchedule(Document): ) asset_finance_book_doc = frappe.get_doc("Asset Finance Book", asset_finance_book_name) - prepare_draft_asset_depr_schedule_data(self, asset_doc, asset_finance_book_doc) + self.prepare_draft_asset_depr_schedule_data(asset_doc, asset_finance_book_doc) + + def prepare_draft_asset_depr_schedule_data( + self, + asset_doc, + row, + date_of_disposal=None, + date_of_return=None, + update_asset_finance_book_row=True, + ): + self.set_draft_asset_depr_schedule_details(asset_doc, row) + self.make_depr_schedule(asset_doc, row, date_of_disposal, update_asset_finance_book_row) + self.set_accumulated_depreciation(row, date_of_disposal, date_of_return) + + def set_draft_asset_depr_schedule_details(self, asset_doc, row): + self.asset = asset_doc.name + self.finance_book = row.finance_book + self.finance_book_id = row.idx + self.opening_accumulated_depreciation = asset_doc.opening_accumulated_depreciation + self.depreciation_method = row.depreciation_method + self.total_number_of_depreciations = row.total_number_of_depreciations + self.frequency_of_depreciation = row.frequency_of_depreciation + self.rate_of_depreciation = row.rate_of_depreciation + self.expected_value_after_useful_life = row.expected_value_after_useful_life + self.status = "Draft" + + def make_depr_schedule( + self, asset_doc, row, date_of_disposal, update_asset_finance_book_row=True + ): + if row.depreciation_method != "Manual" and not self.get("depreciation_schedule"): + self.depreciation_schedule = [] + + if not asset_doc.available_for_use_date: + return + + start = self.clear_depr_schedule() + + self._make_depr_schedule(asset_doc, row, start, date_of_disposal, update_asset_finance_book_row) + + def clear_depr_schedule(self): + start = 0 + num_of_depreciations_completed = 0 + depr_schedule = [] + + for schedule in self.get("depreciation_schedule"): + if schedule.journal_entry: + num_of_depreciations_completed += 1 + depr_schedule.append(schedule) + else: + start = num_of_depreciations_completed + break + + self.depreciation_schedule = depr_schedule + + return start + + def _make_depr_schedule( + self, asset_doc, row, start, date_of_disposal, update_asset_finance_book_row + ): + asset_doc.validate_asset_finance_books(row) + + value_after_depreciation = asset_doc._get_value_after_depreciation_for_making_schedule(row) + row.value_after_depreciation = value_after_depreciation + + if update_asset_finance_book_row: + row.db_update() + + number_of_pending_depreciations = cint(row.total_number_of_depreciations) - cint( + asset_doc.number_of_depreciations_booked + ) + + has_pro_rata = asset_doc.check_is_pro_rata(row) + if has_pro_rata: + number_of_pending_depreciations += 1 + + skip_row = False + should_get_last_day = is_last_day_of_the_month(row.depreciation_start_date) + + for n in range(start, number_of_pending_depreciations): + # If depreciation is already completed (for double declining balance) + if skip_row: + continue + + depreciation_amount = asset_doc.get_depreciation_amount(value_after_depreciation, row) + + if not has_pro_rata or n < cint(number_of_pending_depreciations) - 1: + schedule_date = add_months( + row.depreciation_start_date, n * cint(row.frequency_of_depreciation) + ) + + if should_get_last_day: + schedule_date = get_last_day(schedule_date) + + # schedule date will be a year later from start date + # so monthly schedule date is calculated by removing 11 months from it + monthly_schedule_date = add_months(schedule_date, -row.frequency_of_depreciation + 1) + + # if asset is being sold or scrapped + if date_of_disposal: + from_date = asset_doc.available_for_use_date + if self.depreciation_schedule: + from_date = self.depreciation_schedule[-1].schedule_date + + depreciation_amount, days, months = asset_doc.get_pro_rata_amt( + row, depreciation_amount, from_date, date_of_disposal + ) + + if depreciation_amount > 0: + self.add_depr_schedule_row( + date_of_disposal, + depreciation_amount, + row.depreciation_method, + ) + + break + + # For first row + if has_pro_rata and not asset_doc.opening_accumulated_depreciation and n == 0: + from_date = add_days( + asset_doc.available_for_use_date, -1 + ) # needed to calc depr amount for available_for_use_date too + depreciation_amount, days, months = asset_doc.get_pro_rata_amt( + row, depreciation_amount, from_date, row.depreciation_start_date + ) + + # For first depr schedule date will be the start date + # so monthly schedule date is calculated by removing + # month difference between use date and start date + monthly_schedule_date = add_months(row.depreciation_start_date, -months + 1) + + # For last row + elif has_pro_rata and n == cint(number_of_pending_depreciations) - 1: + if not asset_doc.flags.increase_in_asset_life: + # In case of increase_in_asset_life, the asset.to_date is already set on asset_repair submission + asset_doc.to_date = add_months( + asset_doc.available_for_use_date, + (n + asset_doc.number_of_depreciations_booked) * cint(row.frequency_of_depreciation), + ) + + depreciation_amount_without_pro_rata = depreciation_amount + + depreciation_amount, days, months = asset_doc.get_pro_rata_amt( + row, depreciation_amount, schedule_date, asset_doc.to_date + ) + + depreciation_amount = self.get_adjusted_depreciation_amount( + depreciation_amount_without_pro_rata, depreciation_amount + ) + + monthly_schedule_date = add_months(schedule_date, 1) + schedule_date = add_days(schedule_date, days) + last_schedule_date = schedule_date + + if not depreciation_amount: + continue + value_after_depreciation -= flt( + depreciation_amount, asset_doc.precision("gross_purchase_amount") + ) + + # 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 + ) + or value_after_depreciation < row.expected_value_after_useful_life + ): + depreciation_amount += value_after_depreciation - row.expected_value_after_useful_life + skip_row = True + + if depreciation_amount > 0: + self.add_depr_schedule_row( + schedule_date, + depreciation_amount, + row.depreciation_method, + ) + + # to ensure that final accumulated depreciation amount is accurate + def get_adjusted_depreciation_amount( + self, depreciation_amount_without_pro_rata, depreciation_amount_for_last_row + ): + if not self.opening_accumulated_depreciation: + depreciation_amount_for_first_row = self.get_depreciation_amount_for_first_row() + + if ( + depreciation_amount_for_first_row + depreciation_amount_for_last_row + != depreciation_amount_without_pro_rata + ): + depreciation_amount_for_last_row = ( + depreciation_amount_without_pro_rata - depreciation_amount_for_first_row + ) + + return depreciation_amount_for_last_row + + def get_depreciation_amount_for_first_row(self): + return self.get("depreciation_schedule")[0].depreciation_amount + + def add_depr_schedule_row( + self, + schedule_date, + depreciation_amount, + depreciation_method, + ): + self.append( + "depreciation_schedule", + { + "schedule_date": schedule_date, + "depreciation_amount": depreciation_amount, + "depreciation_method": depreciation_method, + }, + ) + + def set_accumulated_depreciation( + self, + row, + date_of_disposal=None, + date_of_return=None, + ignore_booked_entry=False, + ): + straight_line_idx = [ + d.idx for d in self.get("depreciation_schedule") if d.depreciation_method == "Straight Line" + ] + + accumulated_depreciation = flt(self.opening_accumulated_depreciation) + value_after_depreciation = flt(row.value_after_depreciation) + + for i, d in enumerate(self.get("depreciation_schedule")): + if ignore_booked_entry and d.journal_entry: + continue + + depreciation_amount = flt(d.depreciation_amount, d.precision("depreciation_amount")) + value_after_depreciation -= flt(depreciation_amount) + + # for the last row, if depreciation method = Straight Line + if ( + straight_line_idx + and i == max(straight_line_idx) - 1 + and not date_of_disposal + and not date_of_return + ): + depreciation_amount += flt( + value_after_depreciation - flt(row.expected_value_after_useful_life), + d.precision("depreciation_amount"), + ) + + d.depreciation_amount = depreciation_amount + accumulated_depreciation += d.depreciation_amount + d.accumulated_depreciation_amount = flt( + accumulated_depreciation, d.precision("accumulated_depreciation_amount") + ) def make_draft_asset_depr_schedules_if_not_present(asset_doc): @@ -108,7 +347,7 @@ def make_draft_asset_depr_schedules(asset_doc): def make_draft_asset_depr_schedule(asset_doc, row): asset_depr_schedule_doc = frappe.new_doc("Asset Depreciation Schedule") - prepare_draft_asset_depr_schedule_data(asset_depr_schedule_doc, asset_doc, row) + asset_depr_schedule_doc.prepare_draft_asset_depr_schedule_data(asset_doc, row) asset_depr_schedule_doc.insert() @@ -120,41 +359,11 @@ def update_draft_asset_depr_schedules(asset_doc): if not asset_depr_schedule_doc: continue - prepare_draft_asset_depr_schedule_data(asset_depr_schedule_doc, asset_doc, row) + asset_depr_schedule_doc.prepare_draft_asset_depr_schedule_data(asset_doc, row) asset_depr_schedule_doc.save() -def prepare_draft_asset_depr_schedule_data( - asset_depr_schedule_doc, - asset_doc, - row, - date_of_disposal=None, - date_of_return=None, - update_asset_finance_book_row=True, -): - set_draft_asset_depr_schedule_details(asset_depr_schedule_doc, asset_doc, row) - make_depr_schedule( - asset_depr_schedule_doc, asset_doc, row, date_of_disposal, update_asset_finance_book_row - ) - set_accumulated_depreciation(asset_depr_schedule_doc, row, date_of_disposal, date_of_return) - - -def set_draft_asset_depr_schedule_details(asset_depr_schedule_doc, asset_doc, row): - asset_depr_schedule_doc.asset = asset_doc.name - asset_depr_schedule_doc.finance_book = row.finance_book - asset_depr_schedule_doc.finance_book_id = row.idx - asset_depr_schedule_doc.opening_accumulated_depreciation = ( - asset_doc.opening_accumulated_depreciation - ) - asset_depr_schedule_doc.depreciation_method = row.depreciation_method - asset_depr_schedule_doc.total_number_of_depreciations = row.total_number_of_depreciations - asset_depr_schedule_doc.frequency_of_depreciation = row.frequency_of_depreciation - asset_depr_schedule_doc.rate_of_depreciation = row.rate_of_depreciation - asset_depr_schedule_doc.expected_value_after_useful_life = row.expected_value_after_useful_life - asset_depr_schedule_doc.status = "Draft" - - def convert_draft_asset_depr_schedules_into_active(asset_doc): for row in asset_doc.get("finance_books"): asset_depr_schedule_doc = get_asset_depr_schedule_doc(asset_doc.name, "Draft", row.finance_book) @@ -192,8 +401,8 @@ def make_new_active_asset_depr_schedules_and_cancel_current_ones( new_asset_depr_schedule_doc = frappe.copy_doc(current_asset_depr_schedule_doc) - make_depr_schedule(new_asset_depr_schedule_doc, asset_doc, row, date_of_disposal) - set_accumulated_depreciation(new_asset_depr_schedule_doc, row, date_of_disposal, date_of_return) + new_asset_depr_schedule_doc.make_depr_schedule(asset_doc, row, date_of_disposal) + new_asset_depr_schedule_doc.set_accumulated_depreciation(row, date_of_disposal, date_of_return) new_asset_depr_schedule_doc.notes = notes @@ -208,8 +417,7 @@ def get_temp_asset_depr_schedule_doc( ): asset_depr_schedule_doc = frappe.new_doc("Asset Depreciation Schedule") - prepare_draft_asset_depr_schedule_data( - asset_depr_schedule_doc, + asset_depr_schedule_doc.prepare_draft_asset_depr_schedule_data( asset_doc, row, date_of_disposal, @@ -254,275 +462,3 @@ def get_asset_depr_schedule_name(asset_name, status, finance_book=None): ["status", "=", status], ], ) - - -def make_depr_schedule( - asset_depr_schedule_doc, asset_doc, row, date_of_disposal, update_asset_finance_book_row=True -): - if row.depreciation_method != "Manual" and not asset_depr_schedule_doc.get( - "depreciation_schedule" - ): - asset_depr_schedule_doc.depreciation_schedule = [] - - if not asset_doc.available_for_use_date: - return - - start = clear_depr_schedule(asset_depr_schedule_doc) - - _make_depr_schedule( - asset_depr_schedule_doc, asset_doc, row, start, date_of_disposal, update_asset_finance_book_row - ) - - -def clear_depr_schedule(asset_depr_schedule_doc): - start = 0 - num_of_depreciations_completed = 0 - depr_schedule = [] - - for schedule in asset_depr_schedule_doc.get("depreciation_schedule"): - if schedule.journal_entry: - num_of_depreciations_completed += 1 - depr_schedule.append(schedule) - else: - start = num_of_depreciations_completed - break - - asset_depr_schedule_doc.depreciation_schedule = depr_schedule - - return start - - -def _make_depr_schedule( - asset_depr_schedule_doc, asset_doc, row, start, date_of_disposal, update_asset_finance_book_row -): - asset_doc.validate_asset_finance_books(row) - - value_after_depreciation = _get_value_after_depreciation_for_making_schedule(asset_doc, row) - row.value_after_depreciation = value_after_depreciation - - if update_asset_finance_book_row: - row.db_update() - - number_of_pending_depreciations = cint(row.total_number_of_depreciations) - cint( - asset_doc.number_of_depreciations_booked - ) - - has_pro_rata = asset_doc.check_is_pro_rata(row) - if has_pro_rata: - number_of_pending_depreciations += 1 - - skip_row = False - should_get_last_day = is_last_day_of_the_month(row.depreciation_start_date) - - for n in range(start, number_of_pending_depreciations): - # If depreciation is already completed (for double declining balance) - if skip_row: - continue - - depreciation_amount = get_depreciation_amount(asset_doc, value_after_depreciation, row) - - if not has_pro_rata or n < cint(number_of_pending_depreciations) - 1: - schedule_date = add_months(row.depreciation_start_date, n * cint(row.frequency_of_depreciation)) - - if should_get_last_day: - schedule_date = get_last_day(schedule_date) - - # schedule date will be a year later from start date - # so monthly schedule date is calculated by removing 11 months from it - monthly_schedule_date = add_months(schedule_date, -row.frequency_of_depreciation + 1) - - # if asset is being sold or scrapped - if date_of_disposal: - from_date = asset_doc.available_for_use_date - if asset_depr_schedule_doc.depreciation_schedule: - from_date = asset_depr_schedule_doc.depreciation_schedule[-1].schedule_date - - depreciation_amount, days, months = asset_doc.get_pro_rata_amt( - row, depreciation_amount, from_date, date_of_disposal - ) - - if depreciation_amount > 0: - add_depr_schedule_row( - asset_depr_schedule_doc, - date_of_disposal, - depreciation_amount, - row.depreciation_method, - ) - - break - - # For first row - if has_pro_rata and not asset_doc.opening_accumulated_depreciation and n == 0: - from_date = add_days( - asset_doc.available_for_use_date, -1 - ) # needed to calc depr amount for available_for_use_date too - depreciation_amount, days, months = asset_doc.get_pro_rata_amt( - row, depreciation_amount, from_date, row.depreciation_start_date - ) - - # For first depr schedule date will be the start date - # so monthly schedule date is calculated by removing - # month difference between use date and start date - monthly_schedule_date = add_months(row.depreciation_start_date, -months + 1) - - # For last row - elif has_pro_rata and n == cint(number_of_pending_depreciations) - 1: - if not asset_doc.flags.increase_in_asset_life: - # In case of increase_in_asset_life, the asset.to_date is already set on asset_repair submission - asset_doc.to_date = add_months( - asset_doc.available_for_use_date, - (n + asset_doc.number_of_depreciations_booked) * cint(row.frequency_of_depreciation), - ) - - depreciation_amount_without_pro_rata = depreciation_amount - - depreciation_amount, days, months = asset_doc.get_pro_rata_amt( - row, depreciation_amount, schedule_date, asset_doc.to_date - ) - - depreciation_amount = get_adjusted_depreciation_amount( - asset_depr_schedule_doc, depreciation_amount_without_pro_rata, depreciation_amount - ) - - monthly_schedule_date = add_months(schedule_date, 1) - schedule_date = add_days(schedule_date, days) - last_schedule_date = schedule_date - - if not depreciation_amount: - continue - value_after_depreciation -= flt( - depreciation_amount, asset_doc.precision("gross_purchase_amount") - ) - - # 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 - ) - or value_after_depreciation < row.expected_value_after_useful_life - ): - depreciation_amount += value_after_depreciation - row.expected_value_after_useful_life - skip_row = True - - if depreciation_amount > 0: - add_depr_schedule_row( - asset_depr_schedule_doc, - schedule_date, - depreciation_amount, - row.depreciation_method, - ) - - -def _get_value_after_depreciation_for_making_schedule(asset_doc, row): - # value_after_depreciation - current Asset value - if asset_doc.docstatus == 1 and row.value_after_depreciation: - value_after_depreciation = flt(row.value_after_depreciation) - else: - value_after_depreciation = flt(asset_doc.gross_purchase_amount) - flt( - asset_doc.opening_accumulated_depreciation - ) - - return value_after_depreciation - - -# to ensure that final accumulated depreciation amount is accurate -def get_adjusted_depreciation_amount( - asset_depr_schedule_doc, depreciation_amount_without_pro_rata, depreciation_amount_for_last_row -): - if not asset_depr_schedule_doc.opening_accumulated_depreciation: - depreciation_amount_for_first_row = get_depreciation_amount_for_first_row( - asset_depr_schedule_doc - ) - - if ( - depreciation_amount_for_first_row + depreciation_amount_for_last_row - != depreciation_amount_without_pro_rata - ): - depreciation_amount_for_last_row = ( - depreciation_amount_without_pro_rata - depreciation_amount_for_first_row - ) - - return depreciation_amount_for_last_row - - -def get_depreciation_amount_for_first_row(asset_depr_schedule_doc): - return asset_depr_schedule_doc.get("depreciation_schedule")[0].depreciation_amount - - -@erpnext.allow_regional -def get_depreciation_amount(asset_doc, depreciable_value, row): - if row.depreciation_method in ("Straight Line", "Manual"): - # if the Depreciation Schedule is being prepared for the first time - if not asset_doc.flags.increase_in_asset_life: - depreciation_amount = ( - flt(asset_doc.gross_purchase_amount) - flt(row.expected_value_after_useful_life) - ) / flt(row.total_number_of_depreciations) - - # if the Depreciation Schedule is being modified after Asset Repair - else: - depreciation_amount = ( - flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life) - ) / (date_diff(asset_doc.to_date, asset_doc.available_for_use_date) / 365) - else: - depreciation_amount = flt(depreciable_value * (flt(row.rate_of_depreciation) / 100)) - - return depreciation_amount - - -def add_depr_schedule_row( - asset_depr_schedule_doc, - schedule_date, - depreciation_amount, - depreciation_method, -): - asset_depr_schedule_doc.append( - "depreciation_schedule", - { - "schedule_date": schedule_date, - "depreciation_amount": depreciation_amount, - "depreciation_method": depreciation_method, - }, - ) - - -def set_accumulated_depreciation( - asset_depr_schedule_doc, - row, - date_of_disposal=None, - date_of_return=None, - ignore_booked_entry=False, -): - straight_line_idx = [ - d.idx - for d in asset_depr_schedule_doc.get("depreciation_schedule") - if d.depreciation_method == "Straight Line" - ] - - accumulated_depreciation = flt(asset_depr_schedule_doc.opening_accumulated_depreciation) - value_after_depreciation = flt(row.value_after_depreciation) - - for i, d in enumerate(asset_depr_schedule_doc.get("depreciation_schedule")): - if ignore_booked_entry and d.journal_entry: - continue - - depreciation_amount = flt(d.depreciation_amount, d.precision("depreciation_amount")) - value_after_depreciation -= flt(depreciation_amount) - - # for the last row, if depreciation method = Straight Line - if ( - straight_line_idx - and i == max(straight_line_idx) - 1 - and not date_of_disposal - and not date_of_return - ): - depreciation_amount += flt( - value_after_depreciation - flt(row.expected_value_after_useful_life), - d.precision("depreciation_amount"), - ) - - d.depreciation_amount = depreciation_amount - accumulated_depreciation += d.depreciation_amount - d.accumulated_depreciation_amount = flt( - accumulated_depreciation, d.precision("accumulated_depreciation_amount") - ) diff --git a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py index 539cdec74b7..31d6ffab5fb 100644 --- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py +++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py @@ -14,8 +14,6 @@ from erpnext.assets.doctype.asset.asset import get_asset_value_after_depreciatio from erpnext.assets.doctype.asset.depreciation import get_depreciation_accounts from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule import ( get_asset_depr_schedule_doc, - get_depreciation_amount, - set_accumulated_depreciation, ) @@ -164,7 +162,7 @@ class AssetValueAdjustment(Document): depreciation_amount = days * rate_per_day from_date = data.schedule_date else: - depreciation_amount = get_depreciation_amount(asset, value_after_depreciation, d) + depreciation_amount = asset.get_depreciation_amount(value_after_depreciation, d) if depreciation_amount: value_after_depreciation -= flt(depreciation_amount) @@ -172,7 +170,7 @@ class AssetValueAdjustment(Document): d.db_update() - set_accumulated_depreciation(new_asset_depr_schedule_doc, d, ignore_booked_entry=True) + new_asset_depr_schedule_doc.set_accumulated_depreciation(d, ignore_booked_entry=True) for asset_data in depr_schedule: if not asset_data.journal_entry: asset_data.db_update() diff --git a/erpnext/patches/v15_0/create_asset_depreciation_schedules_from_assets.py b/erpnext/patches/v15_0/create_asset_depreciation_schedules_from_assets.py index 5dc3cdde6f8..371ecbc8c13 100644 --- a/erpnext/patches/v15_0/create_asset_depreciation_schedules_from_assets.py +++ b/erpnext/patches/v15_0/create_asset_depreciation_schedules_from_assets.py @@ -1,9 +1,5 @@ import frappe -from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule import ( - set_draft_asset_depr_schedule_details, -) - def execute(): frappe.reload_doc("assets", "doctype", "Asset Depreciation Schedule") @@ -16,7 +12,7 @@ def execute(): for fb_row in finance_book_rows: asset_depr_schedule_doc = frappe.new_doc("Asset Depreciation Schedule") - set_draft_asset_depr_schedule_details(asset_depr_schedule_doc, asset, fb_row) + asset_depr_schedule_doc.set_draft_asset_depr_schedule_details(asset, fb_row) asset_depr_schedule_doc.insert()