From 8af6a113d1bb4993b5310a2365dd7a85454b14fe Mon Sep 17 00:00:00 2001 From: Anand Baburajan Date: Tue, 23 May 2023 23:04:04 +0530 Subject: [PATCH] fix: incorrect depr schedule and posting dates on selling of existing assets [v14] (#35396) * fix: use date in asset get gl entries functions * fix: calc depr amount properly on selling of existing assets * fix: calc depr amount properly on selling of existing assets again * chore: remove unnecessary line breaks --- .../doctype/sales_invoice/sales_invoice.py | 14 +++- erpnext/assets/doctype/asset/asset.py | 11 ++- erpnext/assets/doctype/asset/depreciation.py | 32 +++++--- erpnext/assets/doctype/asset/test_asset.py | 73 +++++++++++++++++++ .../asset_capitalization.py | 1 + 5 files changed, 114 insertions(+), 17 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index bb85fedab05..0dcd2e291a6 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1179,7 +1179,12 @@ class SalesInvoice(SellingController): if self.is_return: fixed_asset_gl_entries = get_gl_entries_on_asset_regain( - asset, item.base_net_amount, item.finance_book, self.get("doctype"), self.get("name") + asset, + item.base_net_amount, + item.finance_book, + self.get("doctype"), + self.get("name"), + self.get("posting_date"), ) asset.db_set("disposal_date", None) @@ -1194,7 +1199,12 @@ class SalesInvoice(SellingController): asset.reload() fixed_asset_gl_entries = get_gl_entries_on_asset_disposal( - asset, item.base_net_amount, item.finance_book, self.get("doctype"), self.get("name") + asset, + item.base_net_amount, + item.finance_book, + self.get("doctype"), + self.get("name"), + self.get("posting_date"), ) asset.db_set("disposal_date", self.posting_date) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 39e65a7b97b..8754bb7125a 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -343,7 +343,7 @@ class Asset(AccountsController): # if asset is being sold if date_of_disposal: - from_date = self.get_from_date(finance_book.finance_book) + from_date = self.get_from_date_for_disposal(finance_book) depreciation_amount, days, months = self.get_pro_rata_amt( finance_book, depreciation_amount, @@ -500,16 +500,19 @@ class Asset(AccountsController): return start - def get_from_date(self, finance_book): + def get_from_date_for_disposal(self, finance_book): if not self.get("schedules"): - return self.available_for_use_date + return add_months( + getdate(self.available_for_use_date), + (self.number_of_depreciations_booked * finance_book.frequency_of_depreciation), + ) if len(self.finance_books) == 1: return self.schedules[-1].schedule_date from_date = "" for schedule in self.get("schedules"): - if schedule.finance_book == finance_book: + if schedule.finance_book == finance_book.finance_book: from_date = schedule.schedule_date if from_date: diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index a1d29f88cb1..163752b65eb 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -272,7 +272,7 @@ def scrap_asset(asset_name): je.company = asset.company je.remark = "Scrap Entry for asset {0}".format(asset_name) - for entry in get_gl_entries_on_asset_disposal(asset): + for entry in get_gl_entries_on_asset_disposal(asset, date): entry.update({"reference_type": "Asset", "reference_name": asset_name}) je.append("accounts", entry) @@ -395,8 +395,11 @@ def disposal_happens_in_the_future(posting_date_of_disposal): def get_gl_entries_on_asset_regain( - asset, selling_amount=0, finance_book=None, voucher_type=None, voucher_no=None + asset, selling_amount=0, finance_book=None, voucher_type=None, voucher_no=None, date=None ): + if not date: + date = getdate() + ( fixed_asset_account, asset, @@ -414,7 +417,7 @@ def get_gl_entries_on_asset_regain( "debit_in_account_currency": asset.gross_purchase_amount, "debit": asset.gross_purchase_amount, "cost_center": depreciation_cost_center, - "posting_date": getdate(), + "posting_date": date, }, item=asset, ), @@ -424,7 +427,7 @@ def get_gl_entries_on_asset_regain( "credit_in_account_currency": accumulated_depr_amount, "credit": accumulated_depr_amount, "cost_center": depreciation_cost_center, - "posting_date": getdate(), + "posting_date": date, }, item=asset, ), @@ -433,7 +436,7 @@ def get_gl_entries_on_asset_regain( profit_amount = abs(flt(value_after_depreciation)) - abs(flt(selling_amount)) if profit_amount: get_profit_gl_entries( - asset, profit_amount, gl_entries, disposal_account, depreciation_cost_center + asset, profit_amount, gl_entries, disposal_account, depreciation_cost_center, date ) if voucher_type and voucher_no: @@ -445,8 +448,11 @@ def get_gl_entries_on_asset_regain( def get_gl_entries_on_asset_disposal( - asset, selling_amount=0, finance_book=None, voucher_type=None, voucher_no=None + asset, selling_amount=0, finance_book=None, voucher_type=None, voucher_no=None, date=None ): + if not date: + date = getdate() + ( fixed_asset_account, asset, @@ -464,7 +470,7 @@ def get_gl_entries_on_asset_disposal( "credit_in_account_currency": asset.gross_purchase_amount, "credit": asset.gross_purchase_amount, "cost_center": depreciation_cost_center, - "posting_date": getdate(), + "posting_date": date, }, item=asset, ), @@ -474,7 +480,7 @@ def get_gl_entries_on_asset_disposal( "debit_in_account_currency": accumulated_depr_amount, "debit": accumulated_depr_amount, "cost_center": depreciation_cost_center, - "posting_date": getdate(), + "posting_date": date, }, item=asset, ), @@ -483,7 +489,7 @@ def get_gl_entries_on_asset_disposal( profit_amount = flt(selling_amount) - flt(value_after_depreciation) if profit_amount: get_profit_gl_entries( - asset, profit_amount, gl_entries, disposal_account, depreciation_cost_center + asset, profit_amount, gl_entries, disposal_account, depreciation_cost_center, date ) if voucher_type and voucher_no: @@ -517,8 +523,12 @@ def get_asset_details(asset, finance_book=None): def get_profit_gl_entries( - asset, profit_amount, gl_entries, disposal_account, depreciation_cost_center + asset, profit_amount, gl_entries, disposal_account, depreciation_cost_center, date=None ): + + if not date: + date = getdate() + debit_or_credit = "debit" if profit_amount < 0 else "credit" gl_entries.append( asset.get_gl_dict( @@ -527,7 +537,7 @@ def get_profit_gl_entries( "cost_center": depreciation_cost_center, debit_or_credit: abs(profit_amount), debit_or_credit + "_in_account_currency": abs(profit_amount), - "posting_date": getdate(), + "posting_date": date, }, item=asset, ) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 1968154383d..1c0972f2374 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -327,6 +327,79 @@ class TestAsset(AssetSetup): si.cancel() self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Partially Depreciated") + def test_gle_made_by_asset_sale_for_existing_asset(self): + from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice + + asset = create_asset( + calculate_depreciation=1, + available_for_use_date="2020-04-01", + purchase_date="2020-04-01", + expected_value_after_useful_life=0, + total_number_of_depreciations=5, + number_of_depreciations_booked=2, + frequency_of_depreciation=12, + depreciation_start_date="2023-03-31", + opening_accumulated_depreciation=24000, + gross_purchase_amount=60000, + submit=1, + ) + + expected_depr_values = [ + ["2023-03-31", 12000, 36000], + ["2024-03-31", 12000, 48000], + ["2025-03-31", 12000, 60000], + ] + + for i, schedule in enumerate(asset.schedules): + self.assertEqual(getdate(expected_depr_values[i][0]), schedule.schedule_date) + self.assertEqual(expected_depr_values[i][1], schedule.depreciation_amount) + self.assertEqual(expected_depr_values[i][2], schedule.accumulated_depreciation_amount) + + post_depreciation_entries(date="2023-03-31") + + si = create_sales_invoice( + item_code="Macbook Pro", asset=asset.name, qty=1, rate=40000, posting_date=getdate("2023-05-23") + ) + asset.load_from_db() + + self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Sold") + + expected_values = [["2023-03-31", 12000, 36000], ["2023-05-23", 1742.47, 37742.47]] + + for i, schedule in enumerate(asset.schedules): + self.assertEqual(getdate(expected_values[i][0]), schedule.schedule_date) + self.assertEqual(expected_values[i][1], schedule.depreciation_amount) + self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount) + self.assertTrue(schedule.journal_entry) + + expected_gle = ( + ( + "_Test Accumulated Depreciations - _TC", + 37742.47, + 0.0, + ), + ( + "_Test Fixed Asset - _TC", + 0.0, + 60000.0, + ), + ( + "_Test Gain/Loss on Asset Disposal - _TC", + 0.0, + 17742.47, + ), + ("Debtors - _TC", 40000.0, 0.0), + ) + + gle = frappe.db.sql( + """select account, debit, credit from `tabGL Entry` + where voucher_type='Sales Invoice' and voucher_no = %s + order by account""", + si.name, + ) + + self.assertSequenceEqual(gle, expected_gle) + def test_asset_with_maintenance_required_status_after_sale(self): asset = create_asset( calculate_depreciation=1, diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index 90b4d7e5834..f648823b5bf 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -436,6 +436,7 @@ class AssetCapitalization(StockController): item.get("finance_book") or self.get("finance_book"), self.get("doctype"), self.get("name"), + self.get("posting_date"), ) asset.db_set("disposal_date", self.posting_date)