Merge pull request #4950 from nabinhait/fixed_asset_depreciation

Fixed asset depreciation
This commit is contained in:
Rushabh Mehta 2016-03-29 16:39:31 +05:30
commit 1d05ada5e9
51 changed files with 3475 additions and 1239 deletions

View File

@ -48,7 +48,8 @@
},
"Plant and Machinery": {
"account_type": "Fixed Asset"
}
},
"Accumulated Depreciations": {}
},
"Investments": {
"is_group": 1

View File

@ -52,7 +52,8 @@ def get():
},
_("Plant and Machinery"): {
"account_type": "Fixed Asset"
}
},
_("Accumulated Depreciations"): {}
},
_("Investments"): {
"is_group": 1

View File

@ -35,7 +35,12 @@ def _make_test_records(verbose):
# related to Account Inventory Integration
["_Test Account Stock In Hand", "Current Assets", 0, None, None],
["_Test Account Fixed Assets", "Current Assets", 0, None, None],
# fixed asset depreciation
["_Test Fixed Asset", "Current Assets", 0, "Fixed Asset", None],
["_Test Accumulated Depreciations", "Current Assets", 0, None, None],
["_Test Depreciations", "Expenses", 0, None, None],
["_Test Gain/Loss on Asset Disposal", "Expenses", 0, None, None],
# Receivable / Payable Account
["_Test Receivable", "Current Assets", 0, "Receivable", None],

View File

@ -0,0 +1,60 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.provide("erpnext.asset");
frappe.ui.form.on('Asset', {
onload: function(frm) {
frm.set_query("item_code", function() {
return {
"filters": {
"is_stock_item": 0,
"is_fixed_asset": 1,
"disabled": 0
}
};
});
},
refresh: function(frm) {
if (frm.doc.docstatus==1) {
if (in_list(["Submittted", "Partially Depreciated", "Fully Depreciated"], frm.doc.status)) {
cur_frm.add_custom_button("Scrap Asset", function() {
erpnext.asset.scrap_asset(frm);
});
} else if (frm.doc.status=='Scrapped') {
cur_frm.add_custom_button("Restore Asset", function() {
erpnext.asset.restore_asset(frm);
});
}
}
}
});
erpnext.asset.scrap_asset = function(frm) {
frappe.confirm(__("Do you really want to scrap this asset?"), function () {
frappe.call({
args: {
"asset_name": frm.doc.name
},
method: "erpnext.accounts.doctype.asset.depreciation.scrap_asset",
callback: function(r) {
cur_frm.reload_doc();
}
})
})
}
erpnext.asset.restore_asset = function(frm) {
frappe.confirm(__("Do you really want to restore this scrapped asset?"), function () {
frappe.call({
args: {
"asset_name": frm.doc.name
},
method: "erpnext.accounts.doctype.asset.depreciation.restore_asset",
callback: function(r) {
cur_frm.reload_doc();
}
})
})
}

View File

@ -0,0 +1,619 @@
{
"allow_copy": 0,
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:asset_name",
"creation": "2016-03-01 17:01:27.920130",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Document",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "asset_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Asset Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "asset_category",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Asset Category",
"length": 0,
"no_copy": 0,
"options": "Asset Category",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "item_code",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Item Code",
"length": 0,
"no_copy": 0,
"options": "Item",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"default": "Draft",
"fieldname": "status",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Status",
"length": 0,
"no_copy": 1,
"options": "Draft\nSubmitted\nPartially Depreciated\nFully Depreciated\nSold\nScrapped",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "purchase_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Purchase Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "supplier",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Supplier",
"length": 0,
"no_copy": 0,
"options": "Supplier",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "purchase_invoice",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Purchase Invoice",
"length": 0,
"no_copy": 1,
"options": "Purchase Invoice",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "company",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Company",
"length": 0,
"no_copy": 0,
"options": "Company",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "journal_entry_for_scrap",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Journal Entry for Scrap",
"length": 0,
"no_copy": 1,
"options": "Journal Entry",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "section_break_5",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"default": "",
"depends_on": "",
"fieldname": "depreciation_method",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Depreciation Method",
"length": 0,
"no_copy": 0,
"options": "\nStraight Line\nDouble Declining Balance",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "number_of_depreciations",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Number of Depreciations",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "number_of_months_in_a_period",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Number of Months in a Period",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "",
"fieldname": "next_depreciation_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Next Depreciation Date",
"length": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break_11",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "gross_purchase_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Gross Purchase Amount",
"length": 0,
"no_copy": 0,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"description": "",
"fieldname": "expected_value_after_useful_life",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Expected Value After Useful Life",
"length": 0,
"no_copy": 0,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "current_value",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Current Value (After Depreciation)",
"length": 0,
"no_copy": 1,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "section_break_14",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Depreciation Schedule",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "schedules",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Depreciation Schedules",
"length": 0,
"no_copy": 1,
"options": "Depreciation Schedule",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "amended_from",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Amended From",
"length": 0,
"no_copy": 1,
"options": "Asset",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-03-28 15:57:21.022765",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Asset",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 1,
"apply_user_permissions": 0,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"write": 1
}
],
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_seen": 0
}

View File

@ -0,0 +1,129 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils import flt, add_months, cint
from frappe.model.document import Document
class Asset(Document):
def validate(self):
self.set_status()
self.validate_fixed_asset_item()
self.validate_asset_values()
self.set_depreciation_settings()
self.make_depreciation_schedule()
self.validate_depreciation_settings_in_company()
def on_submit(self):
self.set_status()
def on_cancel(self):
self.validate_cancellation()
self.delete_depreciation_entries()
self.set_status()
def validate_fixed_asset_item(self):
item = frappe.get_doc("Item", self.item_code)
if item.disabled:
frappe.throw(_("Item {0} has been disabled").format(self.item_code))
if item.is_stock_item:
frappe.throw(_("Item {0} must be a non-stock item").format(self.item_code))
def validate_asset_values(self):
if flt(self.expected_value_after_useful_life) >= flt(self.gross_purchase_amount):
frappe.throw(_("Expected Value After Useful Life must be less than Gross Purchase Amount"))
if not flt(self.gross_purchase_amount):
frappe.throw(_("Gross Purchase Amount is mandatory"), frappe.MandatoryError)
if not self.current_value and self.next_depreciation_date:
self.current_value = flt(self.gross_purchase_amount)
else:
if flt(self.current_value) > flt(self.gross_purchase_amount):
frappe.throw(_("Current Value After Depreciation must be less than equal to {0}")
.format(flt(self.gross_purchase_amount)))
def set_depreciation_settings(self):
asset_category = frappe.get_doc("Asset Category", self.asset_category)
for field in ("depreciation_method", "number_of_depreciations", "number_of_months_in_a_period"):
if not self.get(field):
self.set(field, asset_category.get(field))
def make_depreciation_schedule(self):
self.schedules = []
if not self.get("schedules") and self.next_depreciation_date:
accumulated_depreciation = 0
value_after_depreciation = flt(self.current_value)
for n in xrange(self.number_of_depreciations):
schedule_date = add_months(self.next_depreciation_date,
n * cint(self.number_of_months_in_a_period))
depreciation_amount = self.get_depreciation_amount(value_after_depreciation)
self.append("schedules", {
"schedule_date": schedule_date,
"depreciation_amount": depreciation_amount,
"accumulated_depreciation_amount": accumulated_depreciation + depreciation_amount
})
accumulated_depreciation += flt(depreciation_amount)
value_after_depreciation -= flt(depreciation_amount)
def get_depreciation_amount(self, depreciable_value):
if self.depreciation_method == "Straight Line":
depreciation_amount = (flt(self.current_value) -
flt(self.expected_value_after_useful_life)) / cint(self.number_of_depreciations)
else:
factor = 200.0 / cint(self.number_of_depreciations)
depreciation_amount = flt(depreciable_value * factor / 100, 0)
value_after_depreciation = flt(depreciable_value) - depreciation_amount
if value_after_depreciation < flt(self.expected_value_after_useful_life):
depreciation_amount = flt(depreciable_value) - flt(self.expected_value_after_useful_life)
return depreciation_amount
def validate_cancellation(self):
if self.status not in ("Submitted", "Partially Depreciated", "Fully Depreciated"):
frappe.throw(_("Asset cannot be cancelled, as it is already {0}").format(self.status))
if self.purchase_invoice:
frappe.throw(_("Please cancel Purchase Invoice {0} first").format(self.purchase_invoice))
def delete_depreciation_entries(self):
total_depreciation_amount = 0
for d in self.get("schedules"):
if d.journal_entry:
frappe.get_doc("Journal Entry", d.journal_entry).cancel()
d.db_set("journal_entry", None)
total_depreciation_amount += flt(d.depreciation_amount)
self.db_set("current_value", (self.current_value + total_depreciation_amount))
def validate_depreciation_settings_in_company(self):
company = frappe.get_doc("Company", self.company)
for field in ("accumulated_depreciation_account", "depreciation_expense_account",
"disposal_account", "depreciation_cost_center"):
if not company.get(field):
frappe.throw(_("Please set {0} in Company {1}")
.format(company.meta.get_label(field), self.company))
def set_status(self, status=None):
if not status:
if self.docstatus == 0:
status = "Draft"
elif self.docstatus == 1:
status = "Submitted"
if self.journal_entry_for_scrap:
status = "Scrapped"
elif flt(self.current_value) <= flt(self.expected_value_after_useful_life):
status = "Fully Depreciated"
elif flt(self.current_value) < flt(self.gross_purchase_amount):
status = 'Partially Depreciated'
elif self.docstatus == 2:
status = "Cancelled"
self.db_set("status", status)

View File

@ -0,0 +1,168 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils import flt, today, getdate
def post_depreciation_entries(date=None):
if not date:
date = today()
for asset in get_depreciable_assets(date):
make_depreciation_entry(asset, date)
frappe.db.commit()
def get_depreciable_assets(date):
return frappe.db.sql_list("""select a.name
from tabAsset a, `tabDepreciation Schedule` ds
where a.name = ds.parent and a.docstatus=1 and ds.schedule_date<=%s
and a.status in ('Submitted', 'Partially Depreciated')
and ifnull(ds.journal_entry, '')=''""", date)
def make_depreciation_entry(asset_name, date=None):
if not date:
date = today()
asset = frappe.get_doc("Asset", asset_name)
fixed_asset_account, accumulated_depreciation_account, depreciation_expense_account = \
get_depreciation_accounts(asset)
depreciation_cost_center = frappe.db.get_value("Company", asset.company, "depreciation_cost_center")
for d in asset.get("schedules"):
if not d.journal_entry and getdate(d.schedule_date) <= getdate(date):
je = frappe.new_doc("Journal Entry")
je.voucher_type = "Depreciation Entry"
je.posting_date = d.schedule_date
je.company = asset.company
je.remark = "Depreciation Entry against {0} worth {1}".format(asset_name, d.depreciation_amount)
je.append("accounts", {
"account": accumulated_depreciation_account,
"credit_in_account_currency": d.depreciation_amount,
"reference_type": "Asset",
"reference_name": asset.name
})
je.append("accounts", {
"account": depreciation_expense_account,
"debit_in_account_currency": d.depreciation_amount,
"reference_type": "Asset",
"reference_name": asset.name,
"cost_center": depreciation_cost_center
})
je.flags.ignore_permissions = True
je.submit()
d.db_set("journal_entry", je.name)
asset.current_value -= d.depreciation_amount
asset.db_set("current_value", asset.current_value)
asset.set_status()
def get_depreciation_accounts(asset):
accounts = frappe.db.sql("""select fixed_asset_account, accumulated_depreciation_account,
depreciation_expense_account from `tabAsset Category Account`
where parent=%s and company=%s""", (asset.asset_category, asset.company), as_dict=1)[0]
fixed_asset_account = accounts.fixed_asset_account
accumulated_depreciation_account = accounts.accumulated_depreciation_account
depreciation_expense_account = accounts.depreciation_expense_account
if not accumulated_depreciation_account or not depreciation_expense_account:
accounts = frappe.db.get_value("Company", asset.company,
["accumulated_depreciation_account", "depreciation_expense_account"])
if not accumulated_depreciation_account:
accumulated_depreciation_account = accounts[0]
if not depreciation_expense_account:
depreciation_expense_account = accounts[1]
if not fixed_asset_account or not accumulated_depreciation_account or not depreciation_expense_account:
frappe.throw(_("Please set Depreciation related Accounts in Asset Category {0} or Company {1}")
.format(asset.asset_category, asset.company))
return fixed_asset_account, accumulated_depreciation_account, depreciation_expense_account
@frappe.whitelist()
def scrap_asset(asset_name):
asset = frappe.get_doc("Asset", asset_name)
if asset.docstatus != 1:
frappe.throw(_("Asset {0} must be submitted").format(asset.name))
elif asset.status in ("Cancelled", "Sold", "Scrapped"):
frappe.throw(_("Asset {0} cannot be scrapped, as it is already {1}").format(asset.name, asset.status))
je = frappe.new_doc("Journal Entry")
je.voucher_type = "Journal Entry"
je.posting_date = today()
je.company = asset.company
je.remark = "Scrap Entry for asset {0}".format(asset_name)
for entry in get_gl_entries_on_asset_disposal(asset):
entry.update({
"reference_type": "Asset",
"reference_name": asset_name
})
je.append("accounts", entry)
je.flags.ignore_permissions = True
je.submit()
frappe.db.set_value("Asset", asset_name, "journal_entry_for_scrap", je.name)
asset.set_status("Scrapped")
@frappe.whitelist()
def restore_asset(asset_name):
asset = frappe.get_doc("Asset", asset_name)
je = asset.journal_entry_for_scrap
asset.db_set("journal_entry_for_scrap", None)
frappe.get_doc("Journal Entry", je).cancel()
asset.set_status()
@frappe.whitelist()
def get_gl_entries_on_asset_disposal(asset, selling_amount=0):
fixed_asset_account, accumulated_depr_account, depr_expense_account = get_depreciation_accounts(asset)
disposal_account, depreciation_cost_center = get_disposal_account_and_cost_center(asset.company)
accumulated_depr_amount = flt(asset.gross_purchase_amount) - flt(asset.current_value)
gl_entries = [
{
"account": fixed_asset_account,
"credit_in_account_currency": asset.gross_purchase_amount,
"credit": asset.gross_purchase_amount
},
{
"account": accumulated_depr_account,
"debit_in_account_currency": accumulated_depr_amount,
"debit": accumulated_depr_amount
}
]
profit_amount = flt(selling_amount) - flt(asset.current_value)
if flt(asset.current_value) and profit_amount:
debit_or_credit = "debit" if profit_amount < 0 else "credit"
gl_entries.append({
"account": disposal_account,
"cost_center": depreciation_cost_center,
debit_or_credit: abs(profit_amount),
debit_or_credit + "_in_account_currency": abs(profit_amount)
})
return gl_entries
def get_disposal_account_and_cost_center(company):
disposal_account, depreciation_cost_center = frappe.db.get_value("Company", company,
["disposal_account", "depreciation_cost_center"])
if not disposal_account:
frappe.throw(_("Please set 'Asset Disposal Account' in Company {0}").format(company))
if not depreciation_cost_center:
frappe.throw(_("Please set 'Asset Depreciation Cost Center' in Company {0}").format(company))
return disposal_account, depreciation_cost_center

View File

@ -0,0 +1,204 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
from frappe.utils import cstr
from erpnext.accounts.doctype.asset.depreciation import post_depreciation_entries, scrap_asset, restore_asset
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
class TestAsset(unittest.TestCase):
def setUp(self):
set_depreciation_settings_in_company()
create_asset()
def test_fixed_asset_must_be_non_stock_item(self):
item = frappe.get_doc("Item", "Macbook Pro")
item.is_stock_item = 1
self.assertRaises(frappe.ValidationError, item.save)
def test_schedule_for_straight_line_method(self):
asset = frappe.get_doc("Asset", "Macbook Pro 1")
self.assertEqual(asset.status, "Draft")
expected_schedules = [
["2015-12-31", 30000, 30000],
["2016-03-31", 30000, 60000],
["2016-06-30", 30000, 90000]
]
schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
for d in asset.get("schedules")]
self.assertEqual(schedules, expected_schedules)
def test_schedule_for_double_declining_method(self):
asset = frappe.get_doc("Asset", "Macbook Pro 1")
asset.depreciation_method = "Double Declining Balance"
asset.save()
expected_schedules = [
["2015-12-31", 66667, 66667],
["2016-03-31", 22222, 88889],
["2016-06-30", 1111, 90000]
]
schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
for d in asset.get("schedules")]
self.assertEqual(schedules, expected_schedules)
def test_depreciation(self):
asset = frappe.get_doc("Asset", "Macbook Pro 1")
asset.submit()
asset.load_from_db()
self.assertEqual(asset.status, "Submitted")
post_depreciation_entries(date="2016-01-01")
asset.load_from_db()
self.assertEqual(asset.status, "Partially Depreciated")
expected_gle = (
("_Test Accumulated Depreciations - _TC", 0.0, 30000.0),
("_Test Depreciations - _TC", 30000.0, 0.0)
)
gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
where against_voucher_type='Asset' and against_voucher = %s
order by account""", asset.name)
self.assertEqual(gle, expected_gle)
self.assertEqual(asset.get("current_value"), 70000)
def test_scrap_asset(self):
asset = frappe.get_doc("Asset", "Macbook Pro 1")
asset.submit()
post_depreciation_entries(date="2016-01-01")
scrap_asset("Macbook Pro 1")
asset.load_from_db()
self.assertEqual(asset.status, "Scrapped")
self.assertTrue(asset.journal_entry_for_scrap)
expected_gle = (
("_Test Accumulated Depreciations - _TC", 30000.0, 0.0),
("_Test Fixed Asset - _TC", 0.0, 100000.0),
("_Test Gain/Loss on Asset Disposal - _TC", 70000.0, 0.0)
)
gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
where voucher_type='Journal Entry' and voucher_no = %s
order by account""", asset.journal_entry_for_scrap)
self.assertEqual(gle, expected_gle)
restore_asset("Macbook Pro 1")
asset.load_from_db()
self.assertFalse(asset.journal_entry_for_scrap)
self.assertEqual(asset.status, "Partially Depreciated")
def test_asset_sale(self):
frappe.get_doc("Asset", "Macbook Pro 1").submit()
post_depreciation_entries(date="2016-01-01")
si = create_sales_invoice(item_code="Macbook Pro", rate=25000, do_not_save=True)
si.get("items")[0].asset = "Macbook Pro 1"
si.submit()
self.assertEqual(frappe.db.get_value("Asset", "Macbook Pro 1", "status"), "Sold")
expected_gle = (
("_Test Accumulated Depreciations - _TC", 30000.0, 0.0),
("_Test Fixed Asset - _TC", 0.0, 100000.0),
("_Test Gain/Loss on Asset Disposal - _TC", 45000.0, 0.0),
("Debtors - _TC", 25000.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.assertEqual(gle, expected_gle)
si.cancel()
self.assertEqual(frappe.db.get_value("Asset", "Macbook Pro 1", "status"), "Partially Depreciated")
def tearDown(self):
asset = frappe.get_doc("Asset", "Macbook Pro 1")
if asset.docstatus == 1 and asset.status not in ("Scrapped", "Sold", "Draft", "Cancelled"):
asset.cancel()
self.assertEqual(frappe.db.get_value("Asset", "Macbook Pro 1", "status"), "Cancelled")
frappe.delete_doc("Asset", "Macbook Pro 1")
def create_asset():
if not frappe.db.exists("Asset Category", "Computers"):
create_asset_category()
if not frappe.db.exists("Item", "Macbook Pro"):
create_fixed_asset_item()
asset = frappe.get_doc({
"doctype": "Asset",
"asset_name": "Macbook Pro 1",
"asset_category": "Computers",
"item_code": "Macbook Pro",
"company": "_Test Company",
"purchase_date": "2015-01-01",
"next_depreciation_date": "2015-12-31",
"gross_purchase_amount": 100000,
"expected_value_after_useful_life": 10000
})
try:
asset.save()
except frappe.DuplicateEntryError:
pass
return asset
def create_asset_category():
asset_category = frappe.new_doc("Asset Category")
asset_category.asset_category_name = "Computers"
asset_category.number_of_depreciations = 3
asset_category.number_of_months_in_a_period = 3
asset_category.append("accounts", {
"company": "_Test Company",
"fixed_asset_account": "_Test Fixed Asset - _TC",
"accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC",
"depreciation_expense_account": "_Test Depreciations - _TC"
})
asset_category.insert()
def create_fixed_asset_item():
try:
frappe.get_doc({
"doctype": "Item",
"item_code": "Macbook Pro",
"item_name": "Macbook Pro",
"description": "Macbook Pro Retina Display",
"item_group": "All Item Groups",
"stock_uom": "Nos",
"is_fixed_asset": 1,
"is_stock_item": 0
}).insert()
except frappe.DuplicateEntryError:
pass
def set_depreciation_settings_in_company():
company = frappe.get_doc("Company", "_Test Company")
company.accumulated_depreciation_account = "_Test Accumulated Depreciations - _TC"
company.depreciation_expense_account = "_Test Depreciations - _TC"
company.disposal_account = "_Test Gain/Loss on Asset Disposal - _TC"
company.depreciation_cost_center = "_Test Cost Center - _TC"
company.save()

View File

@ -0,0 +1,36 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
cur_frm.fields_dict['accounts'].grid.get_field('fixed_asset_account').get_query = function(doc, cdt, cdn) {
var d = locals[cdt][cdn];
return {
"filters": {
"account_type": "Fixed Asset",
"root_type": "Asset",
"is_group": 0,
"company": d.company
}
};
}
cur_frm.fields_dict['accounts'].grid.get_field('accumulated_depreciation_account').get_query = function(doc, cdt, cdn) {
var d = locals[cdt][cdn];
return {
"filters": {
"root_type": "Asset",
"is_group": 0,
"company": d.company
}
};
}
cur_frm.fields_dict['accounts'].grid.get_field('depreciation_expense_account').get_query = function(doc, cdt, cdn) {
var d = locals[cdt][cdn];
return {
"filters": {
"root_type": "Expense",
"is_group": 0,
"company": d.company
}
};
}

View File

@ -0,0 +1,232 @@
{
"allow_copy": 0,
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:asset_category_name",
"creation": "2016-03-01 17:41:39.778765",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Document",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "asset_category_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Asset Category Name",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"default": "Straight Line",
"fieldname": "depreciation_method",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Depreciation Method",
"length": 0,
"no_copy": 0,
"options": "\nStraight Line\nDouble Declining Balance",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "number_of_depreciations",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Number of Depreciations",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "number_of_months_in_a_period",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Number of Months in a Period",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "section_break_2",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Accounts",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "accounts",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Accounts",
"length": 0,
"no_copy": 0,
"options": "Asset Category Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-03-28 12:45:42.131821",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Asset Category",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_seen": 0
}

View File

@ -0,0 +1,15 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.model.document import Document
class AssetCategory(Document):
def validate(self):
for field in ("depreciation_method", "number_of_depreciations",
"number_of_months_in_a_period", "accounts"):
if not self.get(field):
frappe.throw(_("{0} is mandatory").format(self.meta.get_label(field)), frappe.MandatoryError)

View File

@ -0,0 +1,29 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
class TestAssetCategory(unittest.TestCase):
def test_mandatory_fields(self):
asset_category = frappe.new_doc("Asset Category")
asset_category.asset_category_name = "Computers"
self.assertRaises(frappe.MandatoryError, asset_category.insert)
asset_category.number_of_depreciations = 5
asset_category.number_of_months_in_a_period = 12
asset_category.append("accounts", {
"company": "_Test Company",
"fixed_asset_account": "_Test Fixed Asset - _TC",
"accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC",
"depreciation_expense_account": "_Test Depreciations - _TC"
})
try:
asset_category.insert()
except frappe.DuplicateEntryError:
pass

View File

@ -0,0 +1,136 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"creation": "2016-03-07 15:55:18.806409",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "company",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Company",
"length": 0,
"no_copy": 0,
"options": "Company",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "fixed_asset_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Fixed Asset Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "accumulated_depreciation_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Accumulated Depreciation Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "depreciation_expense_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Depreciation Expense Account",
"length": 0,
"no_copy": 0,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2016-03-07 19:02:09.879979",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Asset Category Account",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC"
}

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, 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 AssetCategoryAccount(Document):
pass

View File

@ -0,0 +1,160 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 1,
"autoname": "",
"creation": "2016-03-02 15:11:01.278862",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Document",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "schedule_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Schedule Date",
"length": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "depreciation_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Depreciation Amount",
"length": 0,
"no_copy": 1,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "accumulated_depreciation_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Accumulated Depreciation Amount",
"length": 0,
"no_copy": 1,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "journal_entry",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Journal Entry",
"length": 0,
"no_copy": 1,
"options": "Journal Entry",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2016-03-09 12:21:27.938215",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Depreciation Schedule",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC"
}

View File

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, 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 DepreciationSchedule(Document):
pass

View File

@ -88,8 +88,8 @@ class GLEntry(Document):
"Cost Center", self.cost_center, "company")
return self.cost_center_company[self.cost_center]
if self.cost_center and _get_cost_center_company() != self.company:
if self.cost_center and _get_cost_center_company() != self.company:
frappe.throw(_("Cost Center {0} does not belong to Company {1}").format(self.cost_center, self.company))
def validate_party(self):

File diff suppressed because it is too large Load Diff

View File

@ -16,6 +16,7 @@
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_list_view": 1,
"label": "Account",
@ -44,6 +45,7 @@
"fieldtype": "Data",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Account Type",
@ -68,6 +70,7 @@
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Account Balance",
@ -96,6 +99,7 @@
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 1,
"in_list_view": 0,
"label": "Cost Center",
@ -124,6 +128,7 @@
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
@ -146,6 +151,7 @@
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Party Type",
@ -170,6 +176,7 @@
"fieldtype": "Dynamic Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Party",
@ -194,6 +201,7 @@
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Party Balance",
@ -221,6 +229,7 @@
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Currency",
@ -245,6 +254,7 @@
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Account Currency",
@ -270,6 +280,7 @@
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
@ -293,6 +304,7 @@
"fieldtype": "Float",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Exchange Rate",
@ -317,6 +329,7 @@
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Amount",
@ -340,6 +353,7 @@
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Debit",
@ -365,6 +379,7 @@
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Debit in Company Currency",
@ -392,6 +407,7 @@
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
@ -414,6 +430,7 @@
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Credit",
@ -439,6 +456,7 @@
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Credit in Company Currency",
@ -466,6 +484,7 @@
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Reference",
@ -489,12 +508,13 @@
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Reference Type",
"length": 0,
"no_copy": 0,
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim",
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@ -514,6 +534,7 @@
"fieldtype": "Dynamic Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Reference Name",
@ -539,6 +560,7 @@
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
@ -561,6 +583,7 @@
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Is Advance",
@ -587,6 +610,7 @@
"fieldtype": "Text",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Against Account",
@ -614,12 +638,13 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2015-12-02 04:14:37.571883",
"modified": "2016-03-07 19:10:27.135795",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry Account",
"owner": "Administrator",
"permissions": [],
"read_only": 0,
"read_only_onload": 0
"read_only_onload": 0,
"sort_order": "DESC"
}

View File

@ -196,7 +196,7 @@ cur_frm.fields_dict['credit_to'].get_query = function(doc) {
// Get Print Heading
cur_frm.fields_dict['select_print_heading'].get_query = function(doc, cdt, cdn) {
return{
return {
filters:[
['Print Heading', 'docstatus', '!=', 2]
]
@ -204,12 +204,24 @@ return{
}
cur_frm.set_query("expense_account", "items", function(doc) {
return{
query: "erpnext.accounts.doctype.purchase_invoice.purchase_invoice.get_expense_account",
return {
query: "erpnext.controllers.queries.get_expense_account",
filters: {'company': doc.company}
}
});
cur_frm.set_query("asset", "items", function(doc, cdt, cdn) {
var d = locals[cdt][cdn];
return {
filters: {
'item_code': d.item_code,
'docstatus': 1,
'company': doc.company,
'status': 'Submitted'
}
}
});
cur_frm.cscript.expense_account = function(doc, cdt, cdn){
var d = locals[cdt][cdn];
if(d.idx == 1 && d.expense_account){

View File

@ -55,14 +55,15 @@ class PurchaseInvoice(BuyingController):
self.set_against_expense_account()
self.validate_write_off_account()
self.update_valuation_rate("items")
self.validate_multiple_billing("Purchase Receipt", "pr_detail", "amount",
"items")
self.validate_multiple_billing("Purchase Receipt", "pr_detail", "amount", "items")
self.validate_fixed_asset_account()
self.create_remarks()
def create_remarks(self):
if not self.remarks:
if self.bill_no and self.bill_date:
self.remarks = _("Against Supplier Invoice {0} dated {1}").format(self.bill_no, formatdate(self.bill_date))
self.remarks = _("Against Supplier Invoice {0} dated {1}").format(self.bill_no,
formatdate(self.bill_date))
else:
self.remarks = _("No Remarks")
@ -152,12 +153,13 @@ class PurchaseInvoice(BuyingController):
stock_items = self.get_stock_items()
for item in self.get("items"):
# in case of auto inventory accounting,
# against expense account is always "Stock Received But Not Billed"
# for a stock item and if not epening entry and not drop-ship entry
# expense account is always "Stock Received But Not Billed" for a stock item
# except epening entry, drop-ship entry and fixed asset items
if auto_accounting_for_stock and item.item_code in stock_items \
and self.is_opening == 'No' and (not item.po_detail or
not frappe.db.get_value("Purchase Order Item", item.po_detail, "delivered_by_supplier")):
if auto_accounting_for_stock and item.item_code in stock_items and self.is_opening == 'No' \
and (not item.po_detail
or not frappe.db.get_value("Purchase Order Item", item.po_detail, "delivered_by_supplier")
or not frappe.db.get_value("Item", item.item_code, "is_fixed_asset")):
item.expense_account = stock_not_billed_account
item.cost_center = None
@ -235,6 +237,7 @@ class PurchaseInvoice(BuyingController):
def on_submit(self):
self.check_prev_docstatus()
self.validate_asset()
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
self.company, self.base_grand_total)
@ -248,7 +251,34 @@ class PurchaseInvoice(BuyingController):
self.update_billing_status_in_pr()
self.update_project()
def validate_asset(self):
for d in self.get("items"):
if frappe.db.get_value("Item", d.item_code, "is_fixed_asset"):
if not d.asset:
frappe.throw(_("Row #{0}: Asset is mandatory against a Fixed Asset Item").format(d.idx))
else:
asset = frappe.get_doc("Asset", d.asset)
super(PurchaseInvoice, self).validate_asset(asset, d)
if getdate(asset.purchase_date) != getdate(self.posting_date):
frappe.throw(_("Purchase Date of asset {0} does not match with Purchase Invoice date")
.format(d.asset))
if asset.supplier and asset.supplier != self.supplier:
frappe.throw(_("Supplier of asset {0} does not match with the supplier in the Purchase Invoice").format(d.asset))
if asset.status != "Submitted":
frappe.throw(_("Row #{0}: Asset {1} is already {2}")
.format(d.idx, d.asset, asset.status))
frappe.db.set_value("Asset", asset.name, "purchase_invoice",
(self.name if self.docstatus==1 else None))
if self.docstatus==1 and not asset.supplier:
frappe.db.set_value("Asset", asset.name, "supplier", self.supplier)
def make_gl_entries(self):
auto_accounting_for_stock = \
cint(frappe.defaults.get_global_default("auto_accounting_for_stock"))
@ -415,6 +445,7 @@ class PurchaseInvoice(BuyingController):
self.make_gl_entries_on_cancel()
self.update_project()
self.validate_asset()
def update_project(self):
project_list = []
@ -465,27 +496,17 @@ class PurchaseInvoice(BuyingController):
for pr in set(updated_pr):
frappe.get_doc("Purchase Receipt", pr).update_billing_percentage(update_modified=update_modified)
def validate_fixed_asset_account(self):
for d in self.get('items'):
if frappe.db.get_value("Item", d.item_code, "is_fixed_asset"):
account_type = frappe.db.get_value("Account", d.expense_account, "account_type")
if account_type != 'Fixed Asset':
frappe.throw(_("Row {0}# Account must be of type 'Fixed Asset'").format(d.idx))
def on_recurring(self, reference_doc):
self.due_date = None
@frappe.whitelist()
def get_expense_account(doctype, txt, searchfield, start, page_len, filters):
from erpnext.controllers.queries import get_match_cond
# expense account can be any Debit account,
# but can also be a Liability account with account_type='Expense Account' in special circumstances.
# Hence the first condition is an "OR"
return frappe.db.sql("""select tabAccount.name from `tabAccount`
where (tabAccount.report_type = "Profit and Loss"
or tabAccount.account_type in ("Expense Account", "Fixed Asset", "Temporary"))
and tabAccount.is_group=0
and tabAccount.docstatus!=2
and tabAccount.company = %(company)s
and tabAccount.{key} LIKE %(txt)s
{mcond}""".format( key=frappe.db.escape(searchfield), mcond=get_match_cond(doctype) ),
{ 'company': filters['company'], 'txt': "%%%s%%" % frappe.db.escape(txt) })
@frappe.whitelist()
def make_debit_note(source_name, target_doc=None):
from erpnext.controllers.sales_and_purchase_return import make_return_doc

View File

@ -1076,6 +1076,32 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "asset",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Asset",
"length": 0,
"no_copy": 1,
"options": "Asset",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
@ -1239,7 +1265,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2016-03-18 05:05:27.752823",
"modified": "2016-03-28 05:05:27.752823",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",

View File

@ -471,3 +471,15 @@ cur_frm.set_query("debit_to", function(doc) {
}
}
});
cur_frm.set_query("asset", "items", function(doc, cdt, cdn) {
var d = locals[cdt][cdn];
return {
filters: [
["Asset", "item_code", "=", d.item_code],
["Asset", "docstatus", "=", 1],
["Asset", "status", "in", ["Submitted", "Partially Depreciated", "Fully Depreciated"]],
["Asset", "company", "=", doc.company]
]
}
});

View File

@ -13,6 +13,7 @@ from frappe.model.mapper import get_mapped_doc
from erpnext.controllers.selling_controller import SellingController
from erpnext.accounts.utils import get_account_currency
from erpnext.stock.doctype.delivery_note.delivery_note import update_billed_amount_based_on_so
from erpnext.accounts.doctype.asset.depreciation import get_gl_entries_on_asset_disposal
form_grid_templates = {
"items": "templates/form_grid/item_grid.html"
@ -55,11 +56,11 @@ class SalesInvoice(SellingController):
self.validate_uom_is_integer("stock_uom", "qty")
self.check_close_sales_order("sales_order")
self.validate_debit_to_acc()
self.validate_fixed_asset_account()
self.clear_unallocated_advances("Sales Invoice Advance", "advances")
self.validate_advance_jv("Sales Order")
self.add_remarks()
self.validate_write_off_account()
self.set_income_account_for_fixed_assets()
if cint(self.is_pos):
self.validate_pos()
@ -309,14 +310,6 @@ class SalesInvoice(SellingController):
self.party_account_currency = account.account_currency
def validate_fixed_asset_account(self):
"""Validate Fixed Asset and whether Income Account Entered Exists"""
for d in self.get('items'):
is_asset_item = frappe.db.get_value("Item", d.item_code, "is_asset_item")
account_type = frappe.db.get_value("Account", d.income_account, "account_type")
if is_asset_item == 1 and account_type != 'Fixed Asset':
msgprint(_("Account {0} must be of type 'Fixed Asset' as Item {1} is an Asset Item").format(d.income_account, d.item_code), raise_exception=True)
def validate_with_previous_doc(self):
super(SalesInvoice, self).validate_with_previous_doc({
"Sales Order": {
@ -456,6 +449,17 @@ class SalesInvoice(SellingController):
msgprint(_("POS Profile required to make POS Entry"), raise_exception=True)
return warehouse
def set_income_account_for_fixed_assets(self):
disposal_account = None
for d in self.get("items"):
if frappe.db.get_value("Item", d.item_code, "is_fixed_asset"):
if not disposal_account:
disposal_account = frappe.db.get_value("Company", self.company, "disposal_account")
if not disposal_account:
frappe.throw(_("Please mention 'Gain/Loss Account on Asset Disposal' in Company"))
d.income_account = disposal_account
def on_update(self):
if cint(self.is_pos) == 1:
@ -480,6 +484,13 @@ class SalesInvoice(SellingController):
if d.delivery_note and frappe.db.get_value("Delivery Note", d.delivery_note, "docstatus") != 1:
throw(_("Delivery Note {0} is not submitted").format(d.delivery_note))
def validate_asset(self, asset, item_row):
super(SalesInvoice, self).validate_asset(asset, item_row)
if self.docstatus == 1 and asset.status in ("Scrapped", "Cancelled", "Sold"):
frappe.throw(_("Row #{0}: Asset {1} cannot be submitted, it is already {2}")
.format(item_row.idx, asset.name, asset.status))
def make_gl_entries(self, repost_future_gle=True):
gl_entries = self.get_gl_entries()
@ -566,17 +577,31 @@ class SalesInvoice(SellingController):
# income account gl entries
for item in self.get("items"):
if flt(item.base_net_amount):
account_currency = get_account_currency(item.income_account)
gl_entries.append(
self.get_gl_dict({
"account": item.income_account,
"against": self.customer,
"credit": item.base_net_amount,
"credit_in_account_currency": item.base_net_amount \
if account_currency==self.company_currency else item.net_amount,
"cost_center": item.cost_center
}, account_currency)
)
if frappe.db.get_value("Item", item.item_code, "is_fixed_asset"):
if not item.asset:
frappe.throw(_("Row #{0}: Asset is mandatory against a Fixed Asset Item")
.format(item.idx))
else:
asset = frappe.get_doc("Asset", item.asset)
self.validate_asset(asset, item)
fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(asset, item.base_net_amount)
for gle in fixed_asset_gl_entries:
gl_entries.append(self.get_gl_dict(gle))
asset.set_status("Sold" if self.docstatus==1 else None)
else:
account_currency = get_account_currency(item.income_account)
gl_entries.append(
self.get_gl_dict({
"account": item.income_account,
"against": self.customer,
"credit": item.base_net_amount,
"credit_in_account_currency": item.base_net_amount \
if account_currency==self.company_currency else item.net_amount,
"cost_center": item.cost_center
}, account_currency)
)
# expense account gl entries
if cint(frappe.defaults.get_global_default("auto_accounting_for_stock")) \

View File

@ -1573,6 +1573,32 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "asset",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Asset",
"length": 0,
"no_copy": 1,
"options": "Asset",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
@ -1631,7 +1657,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2016-03-22 11:09:35.263922",
"modified": "2016-03-29 11:09:35.263922",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",

View File

@ -458,7 +458,20 @@ class AccountsController(TransactionBase):
# Note: not validating with gle account because we don't have the account
# at quotation / sales order level and we shouldn't stop someone
# from creating a sales invoice if sales order is already created
def validate_asset(self, asset, item_row):
if asset.company != self.company:
frappe.throw(_("Row #{0}: Asset {1} does not belong to company {2}")
.format(item_row.idx, item_row.asset, self.company))
elif asset.item_code != item_row.item_code:
frappe.throw(_("Row #{0}: Asset {1} does not linked to Item {2}")
.format(item_row.idx, item_row.asset, item_row.item_code))
elif asset.docstatus != 1:
frappe.throw(_("Row #{0}: Asset {1} must be submitted").format(item_row.idx, item_row.asset))
elif item_row.qty > 1:
frappe.throw(_("Row #{0}: Qty must be 1, as item is linked to an asset").format(item_row.idx))
@frappe.whitelist()
def get_tax_rate(account_head):
return frappe.db.get_value("Account", account_head, "tax_rate")

View File

@ -341,3 +341,27 @@ def get_income_account(doctype, txt, searchfield, start, page_len, filters):
'txt': "%%%s%%" % frappe.db.escape(txt),
'company': filters.get("company", "")
})
@frappe.whitelist()
def get_expense_account(doctype, txt, searchfield, start, page_len, filters):
from erpnext.controllers.queries import get_match_cond
if not filters: filters = {}
condition = ""
if filters.get("company"):
condition += "and tabAccount.company = %(company)s"
return frappe.db.sql("""select tabAccount.name from `tabAccount`
where (tabAccount.report_type = "Profit and Loss"
or tabAccount.account_type in ("Expense Account", "Fixed Asset", "Temporary"))
and tabAccount.is_group=0
and tabAccount.docstatus!=2
and tabAccount.{key} LIKE %(txt)s
{condition} {match_condition}"""
.format(condition=condition, key=frappe.db.escape(searchfield),
match_condition=get_match_cond(doctype)), {
'company': filters['company'],
'txt': "%%%s%%" % frappe.db.escape(txt)
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

View File

@ -10,6 +10,7 @@ credit-limit
opening-entry
accounting-reports
accounting-entries
managing-fixed-assets
budgeting
opening-accounts
item-wise-tax

View File

@ -0,0 +1,71 @@
In ERPNext, you can maintain your fixed asset records like Computers, Building, Cars etc and manage depreciations, sell or disposal of those assets.
## Asset Category
To start first you should create Asset Category, depending on the type of assets. For example, all your desktops and laptops can be part of a Asset Category named "Computers". Here, you can set default depreciation method, periodicity and depreciation related accounts, which will be applicable to all the assets under the category.
<img class="screenshot" alt="Asset Category" src="{{docs_base_url}}/assets/img/accounts/asset-category.png">
> **Note:** You can also set default depreciation related Accounts and Cost Centers in Company.
## Asset
Next step will be creating the fixed asset records. The assets which are partially / fully depreciated can also be created/maintained for the future reference.
<img class="screenshot" alt="Asset" src="{{docs_base_url}}/assets/img/accounts/asset.png">
Explanation of the fields:
1. Asset Category: The category of assets it belongs to.
2. Item Code: The item/product code for the asset, which must be marked as a fixed asset and a non-stock item.
3. Status: The options are - Draft, Submitted, Partially Depreciated, Fully Depreciated, Sold and Scrapped.
4. Gross Purchase Amount: The purchase cost of the asset
5. Expected Value After Useful Life: Useful Life is the time period over in which the company expects that the asset will be productive. After that period, either the asset is scrapped or sold. In case it is sold, mention the estimated value here. This value is also known as Salvage Value, Scrap Value or Residual Value.
6. Current Value (After Depreciation): In case you are creating record of an existing asset which has already been partially/fully depreciated, mention the currect value of the asset. In case of new asset, mention the purchase amount or leave it blank.
7. Depreciation Method: There are two options: Straight Line and Double Declining Balance.
- Straight Line: This method spreads the cost of the fixed asset evenly over its useful life.
- Double Declining Method: An accelerated method of depreciation, it results in higher depreciation expense in the earlier years of ownership.
8. Number of Depreciations: The number of depreciations during the useful life. In case of existing assets which are partially depreciated, mention the number of pending depreciations.
9. Number of Months in a Period: The number of months between two depreciations.
10. Next Depreciation Date: Mention the next depreciation date, even if it is the first one. If depreciation already completed, leave it blank.
### Depreciations
The system automatically creates a schedule for depreciation based on depreciation method and other related inputs in the Asset record.
<img class="screenshot" alt="Asset" src="{{docs_base_url}}/assets/img/accounts/depreciation-schedule.png">
On the scheduled date, system creates depreciation entry by creating a Journal Entry and the same Journal Entry is updated in the depreciation table for reference. Next Depreciation Date and Current Value are also updated on submission of depreciation entry.
<img class="screenshot" alt="Asset" src="{{docs_base_url}}/assets/img/accounts/depreciation-entry.png">
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.
## Purchase an asset
For purchasing an asset, first create an item for the asset with "Is Fixed Asset" checked. Then create a Purchase Invoice against that item. In the Purchase Invoice Item row, you have to mention Asset name and associated fixed asset account should be set as Expense Account. Fixed asset accounts are identified based on "Fixed Asset" account type.
<img class="screenshot" alt="Asset" src="{{docs_base_url}}/assets/img/accounts/asset-purchase-invoice.png">
System will validate purchase date, supplier with the value mentioned in the Asset record. On submission of the Invoice, the "Fixed Asset Account" will be debited. It will also update Purchase Invoice number in the Asset.
## Sell an ssset
To sale an asset, create a Sales Invoice against the item linked with the asset. On submission of Sales Invoice, following entries will take place:
- "Receivable Account" (Debtors) will be debited by the sales amount.
- "Fixed Asset Account" will be credited by the purchase amount of asset.
- "Accumulated Depreciation Account" will be debited by the total depreciated amount till now.
- "Gain/Loss Account on Asset Disposal" will be credited/debited based on gain/loss amount. The Gain/Loss account can be set in Company record.
<img class="screenshot" alt="Asset" src="{{docs_base_url}}/assets/img/accounts/asset-sales.png">
## Scrap an Asset
You can scrap an asset anytime using the "Scrap Asset" button in the Asset record. The "Gain/Loss Account on Asset Disposal" mentioned in the Company is debited by the Current Value (After Depreciation) of the asset. , After scrapping, you can also restore the asset using "Restore Asset" button.
<img class="screenshot" alt="Asset" src="{{docs_base_url}}/assets/img/accounts/scrap-journal-entry.png">

View File

@ -137,7 +137,8 @@ scheduler_events = {
"erpnext.support.doctype.issue.issue.auto_close_tickets",
"erpnext.accounts.doctype.fiscal_year.fiscal_year.auto_create_fiscal_year",
"erpnext.hr.doctype.employee.employee.send_birthday_reminders",
"erpnext.projects.doctype.task.task.set_tasks_as_overdue"
"erpnext.projects.doctype.task.task.set_tasks_as_overdue",
"erpnext.accounts.doctype.asset.depreciation.post_depreciation_entries"
]
}

View File

@ -56,7 +56,7 @@ class BOM(Document):
self.manage_default_bom()
def get_item_det(self, item_code):
item = frappe.db.sql("""select name, item_name, is_asset_item, is_purchase_item,
item = frappe.db.sql("""select name, item_name, is_fixed_asset, is_purchase_item,
docstatus, description, image, is_sub_contracted_item, stock_uom, default_bom,
last_purchase_rate
from `tabItem` where name=%s""", item_code, as_dict = 1)

View File

@ -258,4 +258,5 @@ execute:frappe.delete_doc_if_exists("Web Form", "contact") #2016-03-10
erpnext.patches.v6_20x.remove_fiscal_year_from_holiday_list
erpnext.patches.v6_24.map_customer_address_to_shipping_address_on_po
erpnext.patches.v6_27.fix_recurring_order_status
erpnext.patches.v6_20x.remove_customer_supplier_roles
erpnext.patches.v6_20x.remove_customer_supplier_roles
erpnext.patches.v6_24.rename_item_field

View File

@ -0,0 +1,10 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.utils.rename_field import rename_field
def execute():
frappe.reload_doctype("Item")
rename_field("Item", "is_asset_item", "is_fixed_asset")

View File

@ -136,8 +136,12 @@ erpnext.company.setup_queries = function(frm) {
["default_expense_account", {"root_type": "Expense"}],
["default_income_account", {"root_type": "Income"}],
["round_off_account", {"root_type": "Expense"}],
["accumulated_depreciation_account", {"root_type": "Asset"}],
["depreciation_expense_account", {"root_type": "Expense"}],
["disposal_account", {"report_type": "Profit and Loss"}],
["cost_center", {}],
["round_off_cost_center", {}]
["round_off_cost_center", {}],
["depreciation_cost_center", {}]
], function(i, v) {
erpnext.company.set_custom_query(frm, v);
});

View File

@ -253,7 +253,6 @@
"no_copy": 0,
"options": "Terms and Conditions",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@ -961,6 +960,159 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "fixed_asset_depreciation_settings",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Fixed Asset Depreciation Settings",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "accumulated_depreciation_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Accumulated Depreciation Account",
"length": 0,
"no_copy": 1,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "depreciation_expense_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Depreciation Expense Account",
"length": 0,
"no_copy": 1,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break_40",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "disposal_account",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Gain/Loss Account on Asset Disposal",
"length": 0,
"no_copy": 1,
"options": "Account",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "depreciation_cost_center",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Asset Depreciation Cost Center",
"length": 0,
"no_copy": 1,
"options": "Cost Center",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
@ -1235,8 +1387,8 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2016-03-08 20:21:46.331870",
"modified_by": "anand@erpnext.com",
"modified": "2016-03-10 04:34:43.440914",
"modified_by": "Administrator",
"module": "Setup",
"name": "Company",
"owner": "Administrator",

View File

@ -49,7 +49,9 @@ def before_tests():
"company_tagline" :"Testing",
"email" :"test@erpnext.com",
"password" :"test",
"chart_of_accounts" : "Standard"
"chart_of_accounts" : "Standard",
"domain" : "Manufacturing",
})
frappe.db.sql("delete from `tabLeave Allocation`")

View File

@ -111,40 +111,27 @@ frappe.ui.form.on("Item", {
$.extend(erpnext.item, {
setup_queries: function(frm) {
// Expense Account
// ---------------------------------
frm.fields_dict['expense_account'].get_query = function(doc) {
return {
filters: {
"report_type": "Profit and Loss",
"is_group": 0
}
query: "erpnext.controllers.queries.get_expense_account",
}
}
// Income Account
// --------------------------------
frm.fields_dict['income_account'].get_query = function(doc) {
return {
query: "erpnext.controllers.queries.get_income_account"
}
}
// Purchase Cost Center
// -----------------------------
frm.fields_dict['buying_cost_center'].get_query = function(doc) {
return {
filters:{ "is_group": 0 }
filters: { "is_group": 0 }
}
}
// Sales Cost Center
// -----------------------------
frm.fields_dict['selling_cost_center'].get_query = function(doc) {
return {
filters:{ "is_group": 0 }
filters: { "is_group": 0 }
}
}
@ -159,7 +146,7 @@ $.extend(erpnext.item, {
}
}
frm.fields_dict['item_group'].get_query = function(doc,cdt,cdn) {
frm.fields_dict['item_group'].get_query = function(doc, cdt, cdn) {
return {
filters: [
['Item Group', 'docstatus', '!=', 2]

View File

@ -251,6 +251,36 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"default": "",
"depends_on": "",
"description": "",
"fieldname": "is_fixed_asset",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Is Fixed Asset",
"length": 0,
"no_copy": 0,
"oldfieldname": "is_asset_item",
"oldfieldtype": "Select",
"options": "",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
@ -600,36 +630,6 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"default": "",
"depends_on": "eval:doc.is_stock_item",
"description": "",
"fieldname": "is_asset_item",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Is Fixed Asset Item",
"length": 0,
"no_copy": 0,
"oldfieldname": "is_asset_item",
"oldfieldtype": "Select",
"options": "",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
@ -2323,7 +2323,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 1,
"modified": "2016-03-28 08:29:07.922559",
"modified": "2016-03-29 08:29:07.922559",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item",

View File

@ -80,10 +80,12 @@ class Item(WebsiteGenerator):
self.validate_variant_attributes()
self.validate_website_image()
self.make_thumbnail()
self.validate_fixed_asset_item()
if not self.get("__islocal"):
self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group")
self.old_website_item_groups = frappe.db.sql_list("""select item_group from `tabWebsite Item Group`
self.old_website_item_groups = frappe.db.sql_list("""select item_group
from `tabWebsite Item Group`
where parentfield='website_item_groups' and parenttype='Item' and parent=%s""", self.name)
def on_update(self):
@ -566,6 +568,10 @@ class Item(WebsiteGenerator):
if variant:
frappe.throw(_("Item variant {0} exists with same attributes")
.format(variant), ItemVariantExistsError)
def validate_fixed_asset_item(self):
if self.is_fixed_asset and self.is_stock_item:
frappe.throw(_("Fixed Asset Item must be a non-stock item"))
def validate_end_of_life(item_code, end_of_life=None, disabled=None, verbose=1):
if (not end_of_life) or (disabled is None):

View File

@ -9,7 +9,7 @@
"has_serial_no": 0,
"income_account": "Sales - _TC",
"inspection_required": 0,
"is_asset_item": 0,
"is_fixed_asset": 0,
"is_pro_applicable": 0,
"is_purchase_item": 1,
"is_sales_item": 1,
@ -41,7 +41,7 @@
"has_serial_no": 0,
"income_account": "Sales - _TC",
"inspection_required": 0,
"is_asset_item": 0,
"is_fixed_asset": 0,
"is_pro_applicable": 0,
"is_purchase_item": 1,
"is_sales_item": 1,
@ -64,7 +64,7 @@
"has_serial_no": 0,
"income_account": "Sales - _TC",
"inspection_required": 0,
"is_asset_item": 0,
"is_fixed_asset": 0,
"is_pro_applicable": 0,
"is_purchase_item": 1,
"is_sales_item": 1,
@ -93,7 +93,7 @@
"has_serial_no": 0,
"income_account": "Sales - _TC",
"inspection_required": 0,
"is_asset_item": 0,
"is_fixed_asset": 0,
"is_pro_applicable": 0,
"is_purchase_item": 1,
"is_sales_item": 1,
@ -113,7 +113,7 @@
"has_serial_no": 0,
"income_account": "Sales - _TC",
"inspection_required": 0,
"is_asset_item": 0,
"is_fixed_asset": 0,
"is_pro_applicable": 0,
"is_purchase_item": 1,
"is_sales_item": 1,
@ -134,7 +134,7 @@
"has_serial_no": 0,
"income_account": "Sales - _TC",
"inspection_required": 0,
"is_asset_item": 0,
"is_fixed_asset": 0,
"is_pro_applicable": 1,
"is_purchase_item": 1,
"is_sales_item": 1,
@ -151,7 +151,7 @@
"has_batch_no": 0,
"has_serial_no": 0,
"inspection_required": 0,
"is_asset_item": 0,
"is_fixed_asset": 0,
"is_pro_applicable": 0,
"is_purchase_item": 1,
"is_sales_item": 1,
@ -169,7 +169,7 @@
"has_batch_no": 0,
"has_serial_no": 1,
"inspection_required": 0,
"is_asset_item": 0,
"is_fixed_asset": 0,
"is_pro_applicable": 0,
"is_purchase_item": 1,
"is_sales_item": 1,
@ -187,7 +187,7 @@
"has_batch_no": 0,
"has_serial_no": 1,
"inspection_required": 0,
"is_asset_item": 0,
"is_fixed_asset": 0,
"is_pro_applicable": 0,
"is_purchase_item": 1,
"is_sales_item": 1,
@ -209,7 +209,7 @@
"has_serial_no": 0,
"income_account": "Sales - _TC",
"inspection_required": 0,
"is_asset_item": 0,
"is_fixed_asset": 0,
"is_pro_applicable": 1,
"is_sales_item": 1,
"is_stock_item": 1,
@ -229,7 +229,7 @@
"has_serial_no": 0,
"income_account": "Sales - _TC",
"inspection_required": 0,
"is_asset_item": 0,
"is_fixed_asset": 0,
"is_pro_applicable": 1,
"is_purchase_item": 1,
"is_sales_item": 1,
@ -250,7 +250,7 @@
"has_serial_no": 0,
"income_account": "Sales - _TC",
"inspection_required": 0,
"is_asset_item": 0,
"is_fixed_asset": 0,
"is_pro_applicable": 1,
"is_purchase_item": 1,
"is_sales_item": 1,