fix: value of depreciable assets not updating after manual depr entry [v14] (backport #35010) (#35029)
* fix: value of depreciable assets not updating after manual depr entry [v14] (#35010)
* fix: update value of asset with calc_depr on after manual depr entry
* fix: value of asset with calc_depr on after manual depr entry not reflecting in asset_depr_and_bal report
* chore: add validation for depr journal entry
* test: manual_depr_for_depreciable_asset and manual_depr_w_incorrect_jv_voucher_type
* chore: unlink depreciable asset from manual depr entry
(cherry picked from commit 3c75e55cb9)
# Conflicts:
# erpnext/accounts/doctype/journal_entry/journal_entry.js
* chore: fixed conflicts
---------
Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
This commit is contained in:
@@ -297,7 +297,7 @@ def _make_test_records(verbose=None):
|
|||||||
# fixed asset depreciation
|
# fixed asset depreciation
|
||||||
["_Test Fixed Asset", "Current Assets", 0, "Fixed Asset", None],
|
["_Test Fixed Asset", "Current Assets", 0, "Fixed Asset", None],
|
||||||
["_Test Accumulated Depreciations", "Current Assets", 0, "Accumulated Depreciation", None],
|
["_Test Accumulated Depreciations", "Current Assets", 0, "Accumulated Depreciation", None],
|
||||||
["_Test Depreciations", "Expenses", 0, None, None],
|
["_Test Depreciations", "Expenses", 0, "Depreciation", None],
|
||||||
["_Test Gain/Loss on Asset Disposal", "Expenses", 0, None, None],
|
["_Test Gain/Loss on Asset Disposal", "Expenses", 0, None, None],
|
||||||
# Receivable / Payable Account
|
# Receivable / Payable Account
|
||||||
["_Test Receivable", "Current Assets", 0, "Receivable", None],
|
["_Test Receivable", "Current Assets", 0, "Receivable", None],
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ frappe.provide("erpnext.journal_entry");
|
|||||||
frappe.ui.form.on("Journal Entry", {
|
frappe.ui.form.on("Journal Entry", {
|
||||||
setup: function(frm) {
|
setup: function(frm) {
|
||||||
frm.add_fetch("bank_account", "account", "account");
|
frm.add_fetch("bank_account", "account", "account");
|
||||||
frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice'];
|
frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Asset', 'Asset Movement'];
|
||||||
},
|
},
|
||||||
|
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ class JournalEntry(AccountsController):
|
|||||||
self.validate_empty_accounts_table()
|
self.validate_empty_accounts_table()
|
||||||
self.set_account_and_party_balance()
|
self.set_account_and_party_balance()
|
||||||
self.validate_inter_company_accounts()
|
self.validate_inter_company_accounts()
|
||||||
|
self.validate_depr_entry_voucher_type()
|
||||||
|
|
||||||
if self.docstatus == 0:
|
if self.docstatus == 0:
|
||||||
self.apply_tax_withholding()
|
self.apply_tax_withholding()
|
||||||
@@ -134,6 +135,13 @@ class JournalEntry(AccountsController):
|
|||||||
if self.total_credit != doc.total_debit or self.total_debit != doc.total_credit:
|
if self.total_credit != doc.total_debit or self.total_debit != doc.total_credit:
|
||||||
frappe.throw(_("Total Credit/ Debit Amount should be same as linked Journal Entry"))
|
frappe.throw(_("Total Credit/ Debit Amount should be same as linked Journal Entry"))
|
||||||
|
|
||||||
|
def validate_depr_entry_voucher_type(self):
|
||||||
|
if (
|
||||||
|
any(d.account_type == "Depreciation" for d in self.get("accounts"))
|
||||||
|
and self.voucher_type != "Depreciation Entry"
|
||||||
|
):
|
||||||
|
frappe.throw(_("Journal Entry type should be set as Depreciation Entry for asset depreciation"))
|
||||||
|
|
||||||
def validate_stock_accounts(self):
|
def validate_stock_accounts(self):
|
||||||
stock_accounts = get_stock_accounts(self.company, self.doctype, self.name)
|
stock_accounts = get_stock_accounts(self.company, self.doctype, self.name)
|
||||||
for account in stock_accounts:
|
for account in stock_accounts:
|
||||||
@@ -237,25 +245,30 @@ class JournalEntry(AccountsController):
|
|||||||
self.remove(d)
|
self.remove(d)
|
||||||
|
|
||||||
def update_asset_value(self):
|
def update_asset_value(self):
|
||||||
if self.voucher_type != "Depreciation Entry":
|
if self.flags.planned_depr_entry or self.voucher_type != "Depreciation Entry":
|
||||||
return
|
return
|
||||||
|
|
||||||
processed_assets = []
|
|
||||||
|
|
||||||
for d in self.get("accounts"):
|
for d in self.get("accounts"):
|
||||||
if (
|
if (
|
||||||
d.reference_type == "Asset" and d.reference_name and d.reference_name not in processed_assets
|
d.reference_type == "Asset"
|
||||||
|
and d.reference_name
|
||||||
|
and d.account_type == "Depreciation"
|
||||||
|
and d.debit
|
||||||
):
|
):
|
||||||
processed_assets.append(d.reference_name)
|
|
||||||
|
|
||||||
asset = frappe.get_doc("Asset", d.reference_name)
|
asset = frappe.get_doc("Asset", d.reference_name)
|
||||||
|
|
||||||
if asset.calculate_depreciation:
|
if asset.calculate_depreciation:
|
||||||
continue
|
fb_idx = 1
|
||||||
|
if self.finance_book:
|
||||||
depr_value = d.debit or d.credit
|
for fb_row in asset.get("finance_books"):
|
||||||
|
if fb_row.finance_book == self.finance_book:
|
||||||
asset.db_set("value_after_depreciation", asset.value_after_depreciation - depr_value)
|
fb_idx = fb_row.idx
|
||||||
|
break
|
||||||
|
fb_row = asset.get("finance_books")[fb_idx - 1]
|
||||||
|
fb_row.value_after_depreciation -= d.debit
|
||||||
|
fb_row.db_update()
|
||||||
|
else:
|
||||||
|
asset.db_set("value_after_depreciation", asset.value_after_depreciation - d.debit)
|
||||||
|
|
||||||
asset.set_status()
|
asset.set_status()
|
||||||
|
|
||||||
@@ -320,35 +333,35 @@ class JournalEntry(AccountsController):
|
|||||||
if self.voucher_type != "Depreciation Entry":
|
if self.voucher_type != "Depreciation Entry":
|
||||||
return
|
return
|
||||||
|
|
||||||
processed_assets = []
|
|
||||||
|
|
||||||
for d in self.get("accounts"):
|
for d in self.get("accounts"):
|
||||||
if (
|
if (
|
||||||
d.reference_type == "Asset" and d.reference_name and d.reference_name not in processed_assets
|
d.reference_type == "Asset"
|
||||||
|
and d.reference_name
|
||||||
|
and d.account_type == "Depreciation"
|
||||||
|
and d.debit
|
||||||
):
|
):
|
||||||
processed_assets.append(d.reference_name)
|
|
||||||
|
|
||||||
asset = frappe.get_doc("Asset", d.reference_name)
|
asset = frappe.get_doc("Asset", d.reference_name)
|
||||||
|
|
||||||
if asset.calculate_depreciation:
|
if asset.calculate_depreciation:
|
||||||
|
fb_idx = None
|
||||||
for s in asset.get("schedules"):
|
for s in asset.get("schedules"):
|
||||||
if s.journal_entry == self.name:
|
if s.journal_entry == self.name:
|
||||||
s.db_set("journal_entry", None)
|
s.db_set("journal_entry", None)
|
||||||
|
fb_idx = cint(s.finance_book_id) or 1
|
||||||
idx = cint(s.finance_book_id) or 1
|
|
||||||
finance_books = asset.get("finance_books")[idx - 1]
|
|
||||||
finance_books.value_after_depreciation += s.depreciation_amount
|
|
||||||
finance_books.db_update()
|
|
||||||
|
|
||||||
asset.set_status()
|
|
||||||
|
|
||||||
break
|
break
|
||||||
|
if not fb_idx:
|
||||||
|
fb_idx = 1
|
||||||
|
if self.finance_book:
|
||||||
|
for fb_row in asset.get("finance_books"):
|
||||||
|
if fb_row.finance_book == self.finance_book:
|
||||||
|
fb_idx = fb_row.idx
|
||||||
|
break
|
||||||
|
fb_row = asset.get("finance_books")[fb_idx - 1]
|
||||||
|
fb_row.value_after_depreciation += d.debit
|
||||||
|
fb_row.db_update()
|
||||||
else:
|
else:
|
||||||
depr_value = d.debit or d.credit
|
asset.db_set("value_after_depreciation", asset.value_after_depreciation + d.debit)
|
||||||
|
asset.set_status()
|
||||||
asset.db_set("value_after_depreciation", asset.value_after_depreciation + depr_value)
|
|
||||||
|
|
||||||
asset.set_status()
|
|
||||||
|
|
||||||
def unlink_inter_company_jv(self):
|
def unlink_inter_company_jv(self):
|
||||||
if (
|
if (
|
||||||
|
|||||||
@@ -114,28 +114,6 @@ def get_assets(filters):
|
|||||||
sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period,
|
sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period,
|
||||||
sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period
|
sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period
|
||||||
from (SELECT a.asset_category,
|
from (SELECT a.asset_category,
|
||||||
ifnull(sum(case when ds.schedule_date < %(from_date)s and (ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then
|
|
||||||
ds.depreciation_amount
|
|
||||||
else
|
|
||||||
0
|
|
||||||
end), 0) as accumulated_depreciation_as_on_from_date,
|
|
||||||
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date >= %(from_date)s
|
|
||||||
and a.disposal_date <= %(to_date)s and ds.schedule_date <= a.disposal_date then
|
|
||||||
ds.depreciation_amount
|
|
||||||
else
|
|
||||||
0
|
|
||||||
end), 0) as depreciation_eliminated_during_the_period,
|
|
||||||
ifnull(sum(case when ds.schedule_date >= %(from_date)s and ds.schedule_date <= %(to_date)s
|
|
||||||
and (ifnull(a.disposal_date, 0) = 0 or ds.schedule_date <= a.disposal_date) then
|
|
||||||
ds.depreciation_amount
|
|
||||||
else
|
|
||||||
0
|
|
||||||
end), 0) as depreciation_amount_during_the_period
|
|
||||||
from `tabAsset` a, `tabDepreciation Schedule` ds
|
|
||||||
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent and ifnull(ds.journal_entry, '') != ''
|
|
||||||
group by a.asset_category
|
|
||||||
union
|
|
||||||
SELECT a.asset_category,
|
|
||||||
ifnull(sum(case when gle.posting_date < %(from_date)s and (ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then
|
ifnull(sum(case when gle.posting_date < %(from_date)s and (ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then
|
||||||
gle.debit
|
gle.debit
|
||||||
else
|
else
|
||||||
@@ -160,7 +138,7 @@ def get_assets(filters):
|
|||||||
aca.parent = a.asset_category and aca.company_name = %(company)s
|
aca.parent = a.asset_category and aca.company_name = %(company)s
|
||||||
join `tabCompany` company on
|
join `tabCompany` company on
|
||||||
company.name = %(company)s
|
company.name = %(company)s
|
||||||
where a.docstatus=1 and a.company=%(company)s and a.calculate_depreciation=0 and a.purchase_date <= %(to_date)s and gle.debit != 0 and gle.is_cancelled = 0 and gle.account = ifnull(aca.depreciation_expense_account, company.depreciation_expense_account)
|
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and gle.debit != 0 and gle.is_cancelled = 0 and gle.account = ifnull(aca.depreciation_expense_account, company.depreciation_expense_account)
|
||||||
group by a.asset_category
|
group by a.asset_category
|
||||||
union
|
union
|
||||||
SELECT a.asset_category,
|
SELECT a.asset_category,
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ def make_depreciation_entry(asset_name, date=None):
|
|||||||
je.append("accounts", debit_entry)
|
je.append("accounts", debit_entry)
|
||||||
|
|
||||||
je.flags.ignore_permissions = True
|
je.flags.ignore_permissions = True
|
||||||
|
je.flags.planned_depr_entry = True
|
||||||
je.save()
|
je.save()
|
||||||
if not je.meta.get_workflow():
|
if not je.meta.get_workflow():
|
||||||
je.submit()
|
je.submit()
|
||||||
|
|||||||
@@ -1421,7 +1421,7 @@ class TestDepreciationBasics(AssetSetup):
|
|||||||
)
|
)
|
||||||
|
|
||||||
self.assertEqual(asset.status, "Submitted")
|
self.assertEqual(asset.status, "Submitted")
|
||||||
self.assertEqual(asset.get("value_after_depreciation"), 100000)
|
self.assertEqual(asset.get_value_after_depreciation(), 100000)
|
||||||
|
|
||||||
jv = make_journal_entry(
|
jv = make_journal_entry(
|
||||||
"_Test Depreciations - _TC", "_Test Accumulated Depreciations - _TC", 100, save=False
|
"_Test Depreciations - _TC", "_Test Accumulated Depreciations - _TC", 100, save=False
|
||||||
@@ -1434,12 +1434,68 @@ class TestDepreciationBasics(AssetSetup):
|
|||||||
jv.submit()
|
jv.submit()
|
||||||
|
|
||||||
asset.reload()
|
asset.reload()
|
||||||
self.assertEqual(asset.get("value_after_depreciation"), 99900)
|
self.assertEqual(asset.get_value_after_depreciation(), 99900)
|
||||||
|
|
||||||
jv.cancel()
|
jv.cancel()
|
||||||
|
|
||||||
asset.reload()
|
asset.reload()
|
||||||
self.assertEqual(asset.get("value_after_depreciation"), 100000)
|
self.assertEqual(asset.get_value_after_depreciation(), 100000)
|
||||||
|
|
||||||
|
def test_manual_depreciation_for_depreciable_asset(self):
|
||||||
|
asset = create_asset(
|
||||||
|
item_code="Macbook Pro",
|
||||||
|
calculate_depreciation=1,
|
||||||
|
purchase_date="2020-01-30",
|
||||||
|
available_for_use_date="2020-01-30",
|
||||||
|
expected_value_after_useful_life=10000,
|
||||||
|
total_number_of_depreciations=10,
|
||||||
|
frequency_of_depreciation=1,
|
||||||
|
submit=1,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(asset.status, "Submitted")
|
||||||
|
self.assertEqual(asset.get_value_after_depreciation(), 100000)
|
||||||
|
|
||||||
|
jv = make_journal_entry(
|
||||||
|
"_Test Depreciations - _TC", "_Test Accumulated Depreciations - _TC", 100, save=False
|
||||||
|
)
|
||||||
|
for d in jv.accounts:
|
||||||
|
d.reference_type = "Asset"
|
||||||
|
d.reference_name = asset.name
|
||||||
|
jv.voucher_type = "Depreciation Entry"
|
||||||
|
jv.insert()
|
||||||
|
jv.submit()
|
||||||
|
|
||||||
|
asset.reload()
|
||||||
|
self.assertEqual(asset.get_value_after_depreciation(), 99900)
|
||||||
|
|
||||||
|
jv.cancel()
|
||||||
|
|
||||||
|
asset.reload()
|
||||||
|
self.assertEqual(asset.get_value_after_depreciation(), 100000)
|
||||||
|
|
||||||
|
def test_manual_depreciation_with_incorrect_jv_voucher_type(self):
|
||||||
|
asset = create_asset(
|
||||||
|
item_code="Macbook Pro",
|
||||||
|
calculate_depreciation=1,
|
||||||
|
purchase_date="2020-01-30",
|
||||||
|
available_for_use_date="2020-01-30",
|
||||||
|
expected_value_after_useful_life=10000,
|
||||||
|
total_number_of_depreciations=10,
|
||||||
|
frequency_of_depreciation=1,
|
||||||
|
submit=1,
|
||||||
|
)
|
||||||
|
|
||||||
|
jv = make_journal_entry(
|
||||||
|
"_Test Depreciations - _TC", "_Test Accumulated Depreciations - _TC", 100, save=False
|
||||||
|
)
|
||||||
|
for d in jv.accounts:
|
||||||
|
d.reference_type = "Asset"
|
||||||
|
d.reference_name = asset.name
|
||||||
|
d.account_type = "Depreciation"
|
||||||
|
jv.voucher_type = "Journal Entry"
|
||||||
|
|
||||||
|
self.assertRaises(frappe.ValidationError, jv.insert)
|
||||||
|
|
||||||
|
|
||||||
def create_asset_data():
|
def create_asset_data():
|
||||||
|
|||||||
Reference in New Issue
Block a user