Prorated Asset Depreciation

This commit is contained in:
Charles-Henri Decultot 2018-01-05 17:49:06 +00:00
parent 620e0981cb
commit 34e335ba34
14 changed files with 398 additions and 42 deletions

View File

@ -185,20 +185,26 @@ frappe.ui.form.on('Asset', {
create_asset_maintenance: function(frm) {
frappe.call({
args: {
"asset": frm.doc.name,
"item_code": frm.doc.item_code,
"item_name": frm.doc.item_name,
"asset_category": frm.doc.asset_category,
"company": frm.doc.company
},
method: "erpnext.assets.doctype.asset.asset.create_asset_maintenance",
method: "GET",
callback: function(r) {
var doclist = frappe.model.sync(r.message);
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
}
})
}
},
calculate_depreciation: function(frm) {
frappe.db.get_value("Asset Settings", {'name':"Asset Settings"}, 'schedule_based_on_fiscal_year', (data) => {
if (data.schedule_based_on_fiscal_year == 1) {
frm.set_df_property("depreciation_method", "options", "\nStraight Line\nManual")
frm.toggle_reqd("available_for_use_date", true);
frm.toggle_display("frequency_of_depreciation", false);
frappe.db.get_value("Fiscal Year", {'name': frappe.sys_defaults.fiscal_year}, "year_end_date", (data) => {
frm.set_value("next_depreciation_date", data.year_end_date);
})
}
})
}
});
frappe.ui.form.on('Depreciation Schedule', {

View File

@ -507,6 +507,36 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "available_for_use_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Available-for-use Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@ -1221,7 +1251,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-12-19 12:58:44.137460",
"modified": "2018-01-05 09:53:05.945328",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset",

View File

@ -5,7 +5,7 @@
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils import flt, add_months, cint, nowdate, getdate, today
from frappe.utils import flt, add_months, cint, nowdate, getdate, today, date_diff, nowdate
from frappe.model.document import Document
from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import get_fixed_asset_account
from erpnext.assets.doctype.asset.depreciation \
@ -87,11 +87,15 @@ class Asset(Document):
if self.next_depreciation_date and getdate(self.next_depreciation_date) < getdate(self.purchase_date):
frappe.throw(_("Next Depreciation Date cannot be before Purchase Date"))
if self.next_depreciation_date and getdate(self.next_depreciation_date) < getdate(self.available_for_use_date):
frappe.throw(_("Next Depreciation Date cannot be before Available-for-use Date"))
if (flt(self.value_after_depreciation) > flt(self.expected_value_after_useful_life)
and not self.next_depreciation_date and self.calculate_depreciation):
frappe.throw(_("Please set Next Depreciation Date"))
def make_depreciation_schedule(self):
if self.depreciation_method != 'Manual':
self.schedules = []
@ -101,17 +105,42 @@ class Asset(Document):
number_of_pending_depreciations = cint(self.total_number_of_depreciations) - \
cint(self.number_of_depreciations_booked)
if number_of_pending_depreciations:
for n in xrange(number_of_pending_depreciations):
schedule_date = add_months(self.next_depreciation_date,
n * cint(self.frequency_of_depreciation))
if (cint(frappe.db.get_value("Asset Settings", None, "schedule_based_on_fiscal_year")) == 1) and getdate(self.next_depreciation_date) < getdate(add_months(self.available_for_use_date, number_of_pending_depreciations * 12)):
number_of_pending_depreciations += 1
depreciation_amount = self.get_depreciation_amount(value_after_depreciation)
value_after_depreciation -= flt(depreciation_amount)
for n in xrange(number_of_pending_depreciations):
if n == xrange(number_of_pending_depreciations)[-1]:
schedule_date = add_months(self.available_for_use_date, n * 12)
previous_scheduled_date = add_months(self.next_depreciation_date, (n-1) * 12)
depreciation_amount = self.get_depreciation_amount_prorata_temporis(value_after_depreciation, previous_scheduled_date, schedule_date)
self.append("schedules", {
"schedule_date": schedule_date,
"depreciation_amount": depreciation_amount
})
elif n == xrange(number_of_pending_depreciations)[0]:
schedule_date = self.next_depreciation_date
depreciation_amount = self.get_depreciation_amount_prorata_temporis(value_after_depreciation, self.available_for_use_date, schedule_date)
else:
schedule_date = add_months(self.next_depreciation_date, n * 12)
depreciation_amount = self.get_depreciation_amount_prorata_temporis(value_after_depreciation)
if value_after_depreciation != 0:
value_after_depreciation -= flt(depreciation_amount)
self.append("schedules", {
"schedule_date": schedule_date,
"depreciation_amount": depreciation_amount
})
else:
for n in xrange(number_of_pending_depreciations):
schedule_date = add_months(self.next_depreciation_date,
n * cint(self.frequency_of_depreciation))
depreciation_amount = self.get_depreciation_amount(value_after_depreciation)
value_after_depreciation -= flt(depreciation_amount)
self.append("schedules", {
"schedule_date": schedule_date,
"depreciation_amount": depreciation_amount
})
def set_accumulated_depreciation(self):
accumulated_depreciation = flt(self.opening_accumulated_depreciation)
@ -143,6 +172,21 @@ class Asset(Document):
return depreciation_amount
def get_depreciation_amount_prorata_temporis(self, depreciable_value, start_date=None, end_date=None):
if start_date and end_date:
prorata_temporis = min(abs(flt(date_diff(str(end_date), str(start_date)))) / flt(frappe.db.get_value("Asset Settings", None, "number_of_days_in_fiscal_year")), 1)
else:
prorata_temporis = 1
if self.depreciation_method in ("Straight Line", "Manual"):
depreciation_amount = (flt(self.value_after_depreciation) -
flt(self.expected_value_after_useful_life)) / (cint(self.total_number_of_depreciations) -
cint(self.number_of_depreciations_booked)) * prorata_temporis
return depreciation_amount
else:
self.get_depreciation_amount(depreciable_value)
def validate_expected_value_after_useful_life(self):
accumulated_depreciation_after_full_schedule = \
max([d.accumulated_depreciation_amount for d in self.get("schedules")])

View File

@ -12,6 +12,7 @@ from erpnext.assets.doctype.asset.asset import make_sales_invoice, make_purchase
class TestAsset(unittest.TestCase):
def setUp(self):
set_depreciation_settings_in_company()
remove_prorated_depreciation_schedule()
create_asset()
frappe.db.sql("delete from `tabTax Rule`")
@ -145,6 +146,29 @@ class TestAsset(unittest.TestCase):
self.assertEqual(schedules, expected_schedules)
def test_schedule_for_prorated_straight_line_method(self):
set_prorated_depreciation_schedule()
asset = frappe.get_doc("Asset", "Macbook Pro 1")
asset.is_existing_asset = 0
asset.available_for_use_date = "2020-01-30"
asset.next_depreciation_date = "2020-12-31"
asset.depreciation_method = "Straight Line"
asset.save()
expected_schedules = [
["2020-12-31", 28000, 28000],
["2021-12-31", 30000, 58000],
["2022-12-31", 30000, 88000],
["2023-01-30", 2000, 90000]
]
schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
for d in asset.get("schedules")]
self.assertEqual(schedules, expected_schedules)
remove_prorated_depreciation_schedule()
def test_depreciation(self):
asset = frappe.get_doc("Asset", "Macbook Pro 1")
asset.submit()
@ -345,3 +369,18 @@ def set_depreciation_settings_in_company():
# Enable booking asset depreciation entry automatically
frappe.db.set_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically", 1)
def remove_prorated_depreciation_schedule():
asset_settings = frappe.get_doc("Asset Settings", "Asset Settings")
asset_settings.schedule_based_on_fiscal_year = 0
asset_settings.save()
frappe.db.commit()
def set_prorated_depreciation_schedule():
asset_settings = frappe.get_doc("Asset Settings", "Asset Settings")
asset_settings.schedule_based_on_fiscal_year = 1
asset_settings.number_of_days_in_fiscal_year = 360
asset_settings.save()
frappe.db.commit()

View File

@ -0,0 +1,8 @@
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('Asset Settings', {
refresh: function(frm) {
}
});

View File

@ -0,0 +1,175 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2018-01-03 10:30:32.983381",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "depreciation_options",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Depreciation Options",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "schedule_based_on_fiscal_year",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Calculate Prorated Depreciation Schedule Based on Fiscal Year",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "360",
"depends_on": "eval:doc.schedule_based_on_fiscal_year",
"description": "This value is used for pro-rata temporis calculation",
"fieldname": "number_of_days_in_fiscal_year",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Number of Days in Fiscal Year",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2018-01-05 10:10:39.803255",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Settings",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 0,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 0,
"role": "Accounts Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
class AssetSettings(Document):
pass

View File

@ -0,0 +1,23 @@
/* eslint-disable */
// rename this file from _test_[name] to test_[name] to activate
// and remove above this line
QUnit.test("test: Asset Settings", function (assert) {
let done = assert.async();
// number of asserts
assert.expect(1);
frappe.run_serially([
// insert a new Asset Settings
() => frappe.tests.make('Asset Settings', [
// values to be set
{key: 'value'}
]),
() => {
assert.equal(cur_frm.doc.key, 'value');
},
() => done()
]);
});

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
class TestAssetSettings(unittest.TestCase):
pass

View File

@ -13,6 +13,10 @@ def get_data():
{
"type": "doctype",
"name": "Asset Category",
},
{
"type": "doctype",
"name": "Asset Settings",
}
]
},

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

View File

@ -50,6 +50,13 @@ On the scheduled date, system creates depreciation entry by creating a Journal E
In the depreciation entry, the "Accumulated Depreciation Account" is credited and "Depreciation Expense Account" is debited. The related accounts can be set in the Asset Category or Company.
If you are required to calculate the depreciation based on your Fiscal Year and prorated by the number of days left, select the corresponding option in "Account Settings".
The system will automatically set the fiscal year end date as the next depreciation date and calculate the depreciation amount prorata temporis based on the Available-for-use Date (IFRS16)
<img class="screenshot" alt="Asset" src="/docs/assets/img/asset/asset-prorated-depreciation.png">
For better visibility, net value of the asset on different depreciation dates are shown in a line graph.
<img class="screenshot" alt="Asset" src="/docs/assets/img/asset/asset-graph.png">