Minor fixes for depr based on FY

This commit is contained in:
Nabin Hait 2018-02-07 18:25:45 +05:30
commit ed26028a93
14 changed files with 404 additions and 37 deletions

View File

@ -198,6 +198,19 @@ frappe.ui.form.on('Asset', {
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);
})
}
})
}
});
@ -316,4 +329,4 @@ erpnext.asset.transfer_asset = function(frm) {
})
});
dialog.show();
};
};

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",
@ -1277,4 +1307,4 @@
"sort_order": "DESC",
"track_changes": 0,
"track_seen": 0
}
}

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
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 \
@ -90,11 +90,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 = []
@ -104,18 +108,51 @@ 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))
next_depr_date = getdate(add_months(self.available_for_use_date,
number_of_pending_depreciations * 12))
if (cint(frappe.db.get_value("Asset Settings", None, "schedule_based_on_fiscal_year")) == 1
and getdate(self.next_depreciation_date) < next_depr_date):
depreciation_amount = self.get_depreciation_amount(value_after_depreciation)
if depreciation_amount:
value_after_depreciation -= flt(depreciation_amount)
number_of_pending_depreciations += 1
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)
if depreciation_amount:
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)
@ -125,12 +162,13 @@ class Asset(Document):
value_after_depreciation -= flt(depreciation_amount)
if i==len(self.get("schedules"))-1 and self.depreciation_method == "Straight Line":
depreciation_amount += flt(value_after_depreciation - flt(self.expected_value_after_useful_life),
d.precision("depreciation_amount"))
depreciation_amount += flt(value_after_depreciation -
flt(self.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"))
d.accumulated_depreciation_amount = flt(accumulated_depreciation,
d.precision("accumulated_depreciation_amount"))
def get_depreciation_amount(self, depreciable_value):
if self.depreciation_method in ("Straight Line", "Manual"):
@ -147,6 +185,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")])
@ -178,7 +231,7 @@ class Asset(Document):
def set_status(self, status=None):
'''Get and update status'''
if not status:
status = self.get_status()
status = self.get_status()
self.db_set("status", status)
def get_status(self):

View File

@ -12,24 +12,25 @@ 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`")
def test_purchase_asset(self):
asset = frappe.get_doc("Asset", "Macbook Pro 1")
asset.submit()
pi = make_purchase_invoice(asset.name, asset.item_code, asset.gross_purchase_amount,
asset.company, asset.purchase_date)
pi.supplier = "_Test Supplier"
pi.insert()
pi.submit()
asset.load_from_db()
self.assertEqual(asset.supplier, "_Test Supplier")
self.assertEqual(asset.purchase_date, getdate("2015-01-01"))
self.assertEqual(asset.purchase_invoice, pi.name)
expected_gle = (
("_Test Fixed Asset - _TC", 100000.0, 0.0),
("Creditors - _TC", 0.0, 100000.0)
@ -46,10 +47,10 @@ class TestAsset(unittest.TestCase):
asset.load_from_db()
self.assertEqual(asset.supplier, None)
self.assertEqual(asset.purchase_invoice, None)
self.assertFalse(frappe.db.get_value("GL Entry",
{"voucher_type": "Purchase Invoice", "voucher_no": pi.name}))
def test_schedule_for_straight_line_method(self):
asset = frappe.get_doc("Asset", "Macbook Pro 1")
@ -66,14 +67,14 @@ class TestAsset(unittest.TestCase):
for d in asset.get("schedules")]
self.assertEqual(schedules, expected_schedules)
def test_schedule_for_straight_line_method_for_existing_asset(self):
asset = frappe.get_doc("Asset", "Macbook Pro 1")
asset.is_existing_asset = 1
asset.number_of_depreciations_booked = 1
asset.opening_accumulated_depreciation = 40000
asset.save()
self.assertEqual(asset.status, "Draft")
expected_schedules = [
@ -102,7 +103,7 @@ class TestAsset(unittest.TestCase):
for d in asset.get("schedules")]
self.assertEqual(schedules, expected_schedules)
def test_schedule_for_double_declining_method_for_existing_asset(self):
asset = frappe.get_doc("Asset", "Macbook Pro 1")
asset.depreciation_method = "Double Declining Balance"
@ -120,7 +121,7 @@ class TestAsset(unittest.TestCase):
for d in asset.get("schedules")]
self.assertEqual(schedules, expected_schedules)
def test_schedule_for_manual_method(self):
asset = frappe.get_doc("Asset", "Macbook Pro 1")
asset.depreciation_method = "Manual"
@ -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()
@ -172,23 +196,23 @@ class TestAsset(unittest.TestCase):
self.assertEqual(gle, expected_gle)
self.assertEqual(asset.get("value_after_depreciation"), 70000)
def test_depreciation_entry_cancellation(self):
asset = frappe.get_doc("Asset", "Macbook Pro 1")
asset.submit()
post_depreciation_entries(date="2021-01-01")
asset.load_from_db()
# cancel depreciation entry
depr_entry = asset.get("schedules")[0].journal_entry
self.assertTrue(depr_entry)
frappe.get_doc("Journal Entry", depr_entry).cancel()
asset.load_from_db()
depr_entry = asset.get("schedules")[0].journal_entry
self.assertFalse(depr_entry)
def test_scrap_asset(self):
asset = frappe.get_doc("Asset", "Macbook Pro 1")
@ -283,7 +307,7 @@ def create_asset():
if not frappe.db.exists("Item", "Macbook Pro"):
create_fixed_asset_item()
asset = frappe.get_doc({
"doctype": "Asset",
"asset_name": "Macbook Pro 1",
@ -341,6 +365,21 @@ def set_depreciation_settings_in_company():
company.disposal_account = "_Test Gain/Loss on Asset Disposal - _TC"
company.depreciation_cost_center = "_Test Cost Center - _TC"
company.save()
# Enable booking asset depreciation entry automatically
frappe.db.set_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically", 1)
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

@ -12,4 +12,4 @@ class AssetCategory(Document):
def validate(self):
for field in ("total_number_of_depreciations", "frequency_of_depreciation"):
if cint(self.get(field))<1:
frappe.throw(_("{0} must be greater than 0").format(self.meta.get_label(field)), frappe.MandatoryError)
frappe.throw(_("{0} must be greater than 0").format(self.meta.get_label(field)), frappe.MandatoryError)

View File

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

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,9 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
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,9 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
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",
}
]
},
@ -74,4 +78,4 @@ def get_data():
},
]
}
]
]

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">