Merge pull request #5249 from nabinhait/fixed_asset

Fixed asset
This commit is contained in:
Rushabh Mehta 2016-04-21 11:41:11 +05:30
commit 202cb7cdf1
41 changed files with 2772 additions and 1981 deletions

View File

@ -8,29 +8,84 @@ frappe.ui.form.on('Asset', {
frm.set_query("item_code", function() {
return {
"filters": {
"is_stock_item": 0,
"is_fixed_asset": 1,
"disabled": 0
}
};
});
frm.set_query("warehouse", function() {
return {
"filters": {
"company": frm.doc.company
}
};
});
},
refresh: function(frm) {
frappe.ui.form.trigger("Asset", "is_existing_asset");
if (frm.doc.docstatus==1) {
if (in_list(["Submittted", "Partially Depreciated", "Fully Depreciated"], frm.doc.status)) {
cur_frm.add_custom_button("Scrap Asset", function() {
if (frm.doc.status=='Submitted' && !frm.doc.is_existing_asset && !frm.doc.purchase_invoice) {
frm.add_custom_button("Make Purchase Invoice", function() {
erpnext.asset.make_purchase_invoice(frm);
});
}
if (in_list(["Submitted", "Partially Depreciated", "Fully Depreciated"], frm.doc.status)) {
frm.add_custom_button("Scrap Asset", function() {
erpnext.asset.scrap_asset(frm);
});
frm.add_custom_button("Sale Asset", function() {
erpnext.asset.make_sales_invoice(frm);
});
} else if (frm.doc.status=='Scrapped') {
cur_frm.add_custom_button("Restore Asset", function() {
frm.add_custom_button("Restore Asset", function() {
erpnext.asset.restore_asset(frm);
});
}
}
},
is_existing_asset: function(frm) {
frm.toggle_enable("supplier", frm.doc.is_existing_asset);
frm.toggle_reqd("next_depreciation_date", !frm.doc.is_existing_asset);
}
});
erpnext.asset.make_purchase_invoice = function(frm) {
frappe.call({
args: {
"asset": frm.doc.name,
"item_code": frm.doc.item_code,
"gross_purchase_amount": frm.doc.gross_purchase_amount,
"company": frm.doc.company,
"posting_date": frm.doc.purchase_date
},
method: "erpnext.accounts.doctype.asset.asset.make_purchase_invoice",
callback: function(r) {
var doclist = frappe.model.sync(r.message);
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
}
})
}
erpnext.asset.make_sales_invoice = function(frm) {
frappe.call({
args: {
"asset": frm.doc.name,
"item_code": frm.doc.item_code,
"company": frm.doc.company
},
method: "erpnext.accounts.doctype.asset.asset.make_sales_invoice",
callback: function(r) {
var doclist = frappe.model.sync(r.message);
frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
}
})
}
erpnext.asset.scrap_asset = function(frm) {
frappe.confirm(__("Do you really want to scrap this asset?"), function () {
frappe.call({

View File

@ -137,6 +137,83 @@
"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": "warehouse",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Warehouse",
"length": 0,
"no_copy": 0,
"options": "Warehouse",
"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": "is_existing_asset",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Is Existing Asset",
"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,
@ -218,24 +295,23 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "company",
"fieldtype": "Link",
"fieldname": "disposal_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Company",
"label": "Disposal Date",
"length": 0,
"no_copy": 0,
"options": "Company",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 1,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
@ -290,134 +366,6 @@
"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,
@ -475,14 +423,15 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "current_value",
"depends_on": "is_existing_asset",
"fieldname": "opening_accumulated_depreciation",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Current Value (After Depreciation)",
"label": "Opening Accumulated Depreciation",
"length": 0,
"no_copy": 1,
"options": "Company:company:default_currency",
@ -497,6 +446,186 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "value_after_depreciation",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Value After Depreciation",
"length": 0,
"no_copy": 0,
"options": "Company:company:default_currency",
"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": "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,
"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": "total_number_of_depreciations",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Total 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,
"depends_on": "eval:(doc.is_existing_asset && doc.opening_accumulated_depreciation)",
"fieldname": "number_of_depreciations_booked",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Number of Depreciations Booked",
"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": "frequency_of_depreciation",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Frequency of Depreciation (Months)",
"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,
@ -583,7 +712,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-03-31 05:02:49.890116",
"modified": "2016-04-20 18:09:07.573716",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Asset",
@ -611,8 +740,9 @@
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"read_only_onload": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_seen": 0

View File

@ -5,17 +5,21 @@
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils import flt, add_months, cint
from frappe.utils import flt, add_months, cint, nowdate, getdate
from frappe.model.document import Document
from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import get_fixed_asset_account
from erpnext.accounts.doctype.asset.depreciation \
import get_disposal_account_and_cost_center, get_depreciation_accounts
class Asset(Document):
def validate(self):
self.status = self.get_status()
self.validate_fixed_asset_item()
self.validate_item()
self.validate_asset_values()
self.set_depreciation_settings()
self.make_depreciation_schedule()
self.validate_depreciation_settings_in_company()
# Validate depreciation related accounts
get_depreciation_accounts(self)
def on_submit(self):
self.set_status()
@ -25,59 +29,87 @@ class Asset(Document):
self.delete_depreciation_entries()
self.set_status()
def validate_fixed_asset_item(self):
def validate_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):
self.value_after_depreciation = flt(self.gross_purchase_amount) - flt(self.opening_accumulated_depreciation)
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)
if not self.is_existing_asset:
self.opening_accumulated_depreciation = 0
self.number_of_depreciations_booked = 0
if not self.next_depreciation_date:
frappe.throw(_("Next Depreciation Date is mandatory for new asset"))
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)))
depreciable_amount = flt(self.gross_purchase_amount) - flt(self.expected_value_after_useful_life)
if flt(self.opening_accumulated_depreciation) > depreciable_amount:
frappe.throw(_("Opening Accumulated Depreciation must be less than equal to {0}")
.format(depreciable_amount))
if self.opening_accumulated_depreciation:
if not self.number_of_depreciations_booked:
frappe.throw(_("Please set Number of Depreciations Booked"))
else:
self.number_of_depreciations_booked = 0
if cint(self.number_of_depreciations_booked) > cint(self.total_number_of_depreciations):
frappe.throw(_("Number of Depreciations Booked cannot be greater than Total Number of Depreciations"))
if self.next_depreciation_date and getdate(self.next_depreciation_date) < getdate(nowdate()):
frappe.throw(_("Next Depreciation Date must be on or after today"))
if (flt(self.value_after_depreciation) > flt(self.expected_value_after_useful_life)
and not self.next_depreciation_date):
frappe.throw(_("Please set Next Depreciation Date"))
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"):
for field in ("depreciation_method", "total_number_of_depreciations", "frequency_of_depreciation"):
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))
accumulated_depreciation = flt(self.opening_accumulated_depreciation)
value_after_depreciation = flt(self.value_after_depreciation)
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))
depreciation_amount = self.get_depreciation_amount(value_after_depreciation)
depreciation_amount = self.get_depreciation_amount(value_after_depreciation)
accumulated_depreciation += flt(depreciation_amount)
value_after_depreciation -= flt(depreciation_amount)
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)
self.append("schedules", {
"schedule_date": schedule_date,
"depreciation_amount": depreciation_amount,
"accumulated_depreciation_amount": accumulated_depreciation
})
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)
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))
else:
factor = 200.0 / cint(self.number_of_depreciations)
factor = 200.0 / self.total_number_of_depreciations
depreciation_amount = flt(depreciable_value * factor / 100, 0)
value_after_depreciation = flt(depreciable_value) - depreciation_amount
@ -94,25 +126,16 @@ class Asset(Document):
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))
self.db_set("value_after_depreciation",
(flt(self.gross_purchase_amount) - flt(self.opening_accumulated_depreciation)))
def set_status(self, status=None):
'''Set asset and update status'''
'''Get and update status'''
if not status:
status = self.get_status()
self.db_set("status", status)
@ -125,11 +148,46 @@ class Asset(Document):
status = "Submitted"
if self.journal_entry_for_scrap:
status = "Scrapped"
elif flt(self.current_value) <= flt(self.expected_value_after_useful_life):
elif flt(self.value_after_depreciation) <= flt(self.expected_value_after_useful_life):
status = "Fully Depreciated"
elif flt(self.current_value) < flt(self.gross_purchase_amount):
elif flt(self.value_after_depreciation) < flt(self.gross_purchase_amount):
status = 'Partially Depreciated'
elif self.docstatus == 2:
status = "Cancelled"
return status
@frappe.whitelist()
def make_purchase_invoice(asset, item_code, gross_purchase_amount, company, posting_date):
pi = frappe.new_doc("Purchase Invoice")
pi.company = company
pi.currency = frappe.db.get_value("Company", company, "default_currency")
pi.posting_date = posting_date
pi.append("items", {
"item_code": item_code,
"is_fixed_asset": 1,
"asset": asset,
"expense_account": get_fixed_asset_account(asset),
"qty": 1,
"price_list_rate": gross_purchase_amount,
"rate": gross_purchase_amount
})
pi.set_missing_values()
return pi
@frappe.whitelist()
def make_sales_invoice(asset, item_code, company):
si = frappe.new_doc("Sales Invoice")
si.company = company
si.currency = frappe.db.get_value("Company", company, "default_currency")
disposal_account, depreciation_cost_center = get_disposal_account_and_cost_center(company)
si.append("items", {
"item_code": item_code,
"is_fixed_asset": 1,
"asset": asset,
"income_account": disposal_account,
"cost_center": depreciation_cost_center,
"qty": 1
})
si.set_missing_values()
return si

View File

@ -58,9 +58,9 @@ def make_depreciation_entry(asset_name, date=None):
je.submit()
d.db_set("journal_entry", je.name)
asset.current_value -= d.depreciation_amount
asset.value_after_depreciation -= d.depreciation_amount
asset.db_set("current_value", asset.current_value)
asset.db_set("value_after_depreciation", asset.value_after_depreciation)
asset.set_status()
def get_depreciation_accounts(asset):
@ -115,7 +115,8 @@ def scrap_asset(asset_name):
je.flags.ignore_permissions = True
je.submit()
frappe.db.set_value("Asset", asset_name, "disposal_date", today())
frappe.db.set_value("Asset", asset_name, "journal_entry_for_scrap", je.name)
asset.set_status("Scrapped")
@ -124,7 +125,10 @@ def restore_asset(asset_name):
asset = frappe.get_doc("Asset", asset_name)
je = asset.journal_entry_for_scrap
asset.db_set("disposal_date", None)
asset.db_set("journal_entry_for_scrap", None)
frappe.get_doc("Journal Entry", je).cancel()
asset.set_status()
@ -133,7 +137,7 @@ def restore_asset(asset_name):
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)
accumulated_depr_amount = flt(asset.gross_purchase_amount) - flt(asset.value_after_depreciation)
gl_entries = [
{
@ -148,8 +152,8 @@ def get_gl_entries_on_asset_disposal(asset, selling_amount=0):
}
]
profit_amount = flt(selling_amount) - flt(asset.current_value)
if flt(asset.current_value) and profit_amount:
profit_amount = flt(selling_amount) - flt(asset.value_after_depreciation)
if flt(asset.value_after_depreciation) and profit_amount:
debit_or_credit = "debit" if profit_amount < 0 else "credit"
gl_entries.append({
"account": disposal_account,
@ -160,12 +164,13 @@ def get_gl_entries_on_asset_disposal(asset, selling_amount=0):
return gl_entries
@frappe.whitelist()
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))
frappe.throw(_("Please set 'Gain/Loss Account on Asset Disposal' in Company {0}").format(company))
if not depreciation_cost_center:
frappe.throw(_("Please set 'Asset Depreciation Cost Center' in Company {0}").format(company))

View File

@ -5,19 +5,50 @@ from __future__ import unicode_literals
import frappe
import unittest
from frappe.utils import cstr
from frappe.utils import cstr, nowdate, getdate
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
from erpnext.accounts.doctype.asset.asset import make_sales_invoice, make_purchase_invoice
class TestAsset(unittest.TestCase):
def setUp(self):
set_depreciation_settings_in_company()
create_asset()
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)
)
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)
gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
where voucher_type='Purchase Invoice' and voucher_no = %s
order by account""", pi.name)
self.assertEqual(gle, expected_gle)
pi.cancel()
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")
@ -25,9 +56,28 @@ class TestAsset(unittest.TestCase):
self.assertEqual(asset.status, "Draft")
expected_schedules = [
["2015-12-31", 30000, 30000],
["2016-03-31", 30000, 60000],
["2016-06-30", 30000, 90000]
["2020-12-31", 30000, 30000],
["2021-03-31", 30000, 60000],
["2021-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_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 = [
["2020-12-31", 25000, 65000],
["2021-03-31", 25000, 90000]
]
schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
@ -42,9 +92,27 @@ class TestAsset(unittest.TestCase):
asset.save()
expected_schedules = [
["2015-12-31", 66667, 66667],
["2016-03-31", 22222, 88889],
["2016-06-30", 1111, 90000]
["2020-12-31", 66667, 66667],
["2021-03-31", 22222, 88889],
["2021-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_schedule_for_double_declining_method_for_existing_asset(self):
asset = frappe.get_doc("Asset", "Macbook Pro 1")
asset.depreciation_method = "Double Declining Balance"
asset.is_existing_asset = 1
asset.number_of_depreciations_booked = 1
asset.opening_accumulated_depreciation = 50000
asset.save()
expected_schedules = [
["2020-12-31", 33333, 83333],
["2021-03-31", 6667, 90000]
]
schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
@ -58,7 +126,7 @@ class TestAsset(unittest.TestCase):
asset.load_from_db()
self.assertEqual(asset.status, "Submitted")
post_depreciation_entries(date="2016-01-01")
post_depreciation_entries(date="2021-01-01")
asset.load_from_db()
self.assertEqual(asset.status, "Partially Depreciated")
@ -73,13 +141,12 @@ class TestAsset(unittest.TestCase):
order by account""", asset.name)
self.assertEqual(gle, expected_gle)
self.assertEqual(asset.get("current_value"), 70000)
self.assertEqual(asset.get("value_after_depreciation"), 70000)
def test_scrap_asset(self):
asset = frappe.get_doc("Asset", "Macbook Pro 1")
asset.submit()
post_depreciation_entries(date="2016-01-01")
post_depreciation_entries(date="2021-01-01")
scrap_asset("Macbook Pro 1")
@ -107,10 +174,13 @@ class TestAsset(unittest.TestCase):
def test_asset_sale(self):
frappe.get_doc("Asset", "Macbook Pro 1").submit()
post_depreciation_entries(date="2016-01-01")
post_depreciation_entries(date="2021-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 = make_sales_invoice(asset="Macbook Pro 1", item_code="Macbook Pro", company="_Test Company")
si.customer = "_Test Customer"
si.due_date = nowdate()
si.get("items")[0].rate = 25000
si.insert()
si.submit()
self.assertEqual(frappe.db.get_value("Asset", "Macbook Pro 1", "status"), "Sold")
@ -156,9 +226,10 @@ def create_asset():
"item_code": "Macbook Pro",
"company": "_Test Company",
"purchase_date": "2015-01-01",
"next_depreciation_date": "2015-12-31",
"next_depreciation_date": "2020-12-31",
"gross_purchase_amount": 100000,
"expected_value_after_useful_life": 10000
"expected_value_after_useful_life": 10000,
"warehouse": "_Test Warehouse - _TC"
})
try:
asset.save()
@ -170,8 +241,8 @@ def create_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.total_number_of_depreciations = 3
asset_category.frequency_of_depreciation = 3
asset_category.append("accounts", {
"company_name": "_Test Company",
"fixed_asset_account": "_Test Fixed Asset - _TC",
@ -189,8 +260,7 @@ def create_fixed_asset_item():
"description": "Macbook Pro Retina Display",
"item_group": "All Item Groups",
"stock_uom": "Nos",
"is_stock_item": 0,
"is_fixed_asset": 1
"is_stock_item": 0
}).insert()
except frappe.DuplicateEntryError:
pass

View File

@ -89,14 +89,14 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "number_of_depreciations",
"fieldname": "total_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",
"label": "Total Number of Depreciations",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -114,14 +114,14 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "number_of_months_in_a_period",
"fieldname": "frequency_of_depreciation",
"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",
"label": "Frequency of Depreciation (Months)",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -196,7 +196,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2016-03-31 05:37:48.481012",
"modified": "2016-04-20 13:23:09.890324",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Asset Category",
@ -244,6 +244,7 @@
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",

View File

@ -10,6 +10,6 @@ from frappe.model.document import Document
class AssetCategory(Document):
def validate(self):
for field in ("number_of_depreciations", "number_of_months_in_a_period"):
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)

View File

@ -13,8 +13,8 @@ class TestAssetCategory(unittest.TestCase):
self.assertRaises(frappe.MandatoryError, asset_category.insert)
asset_category.number_of_depreciations = 3
asset_category.number_of_months_in_a_period = 3
asset_category.total_number_of_depreciations = 3
asset_category.frequency_of_depreciation = 3
asset_category.append("accounts", {
"company_name": "_Test Company",
"fixed_asset_account": "_Test Fixed Asset - _TC",

View File

@ -146,15 +146,17 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2016-03-09 12:21:27.938215",
"modified": "2016-04-20 16:43:21.407123",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Depreciation Schedule",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC"
"sort_order": "DESC",
"track_seen": 0
}

View File

@ -34,5 +34,29 @@
"year": "_Test Fiscal Year 2017",
"year_end_date": "2017-12-31",
"year_start_date": "2017-01-01"
},
{
"doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2018",
"year_end_date": "2018-12-31",
"year_start_date": "2018-01-01"
},
{
"doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2019",
"year_end_date": "2019-12-31",
"year_start_date": "2019-01-01"
},
{
"doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2020",
"year_end_date": "2020-12-31",
"year_start_date": "2020-01-01"
},
{
"doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2021",
"year_end_date": "2021-12-31",
"year_start_date": "2021-01-01"
}
]

View File

@ -156,6 +156,22 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
frm: cur_frm
})
},
asset: function(frm, cdt, cdn) {
var row = locals[cdt][cdn];
if(row.asset) {
frappe.call({
method: erpnext.accounts.doctype.purchase_invoice.purchase_invoice.get_fixed_asset_account,
args: {
"asset": row.asset,
"account": row.expense_account
},
callback: function(r, rt) {
frappe.model.set_value(cdt, cdn, "expense_account", r.message);
}
})
}
}
});
cur_frm.script_manager.make(erpnext.accounts.PurchaseInvoice);

View File

@ -62,6 +62,7 @@ class PurchaseInvoice(BuyingController):
self.set_against_expense_account()
self.validate_write_off_account()
self.validate_multiple_billing("Purchase Receipt", "pr_detail", "amount", "items")
self.validate_fixed_asset()
self.validate_fixed_asset_account()
self.create_remarks()
@ -165,11 +166,10 @@ class PurchaseInvoice(BuyingController):
# in case of auto inventory accounting,
# 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 self.is_opening == 'No' \
and item.item_code in stock_items 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")):
if auto_accounting_for_stock and item.item_code in stock_items and self.is_opening == 'No' \
and (not item.po_detail or not item.is_fixed_asset
or not frappe.db.get_value("Purchase Order Item", item.po_detail, "delivered_by_supplier")):
if self.update_stock:
item.expense_account = warehouse_account[item.warehouse]["name"]
@ -290,8 +290,6 @@ class PurchaseInvoice(BuyingController):
self.check_prev_docstatus()
self.update_status_updater_args()
self.validate_asset()
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
self.company, self.base_grand_total)
@ -312,34 +310,23 @@ class PurchaseInvoice(BuyingController):
self.make_gl_entries()
self.update_project()
self.update_fixed_asset()
def validate_asset(self):
def update_fixed_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))
if d.is_fixed_asset:
asset = frappe.get_doc("Asset", d.asset)
if self.docstatus==1:
asset.purchase_invoice = self.name
asset.purchase_date = self.posting_date
asset.supplier = self.supplier
else:
asset = frappe.get_doc("Asset", d.asset)
asset.purchase_invoice = None
asset.supplier = None
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))
asset.flags.ignore_validate_update_after_submit = True
asset.save()
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, repost_future_gle=False):
self.auto_accounting_for_stock = \
cint(frappe.defaults.get_global_default("auto_accounting_for_stock"))
@ -612,7 +599,7 @@ class PurchaseInvoice(BuyingController):
self.make_gl_entries_on_cancel()
self.update_project()
self.validate_asset()
self.update_fixed_asset()
def update_project(self):
project_list = []
@ -666,7 +653,7 @@ class PurchaseInvoice(BuyingController):
def validate_fixed_asset_account(self):
for d in self.get('items'):
if frappe.db.get_value("Item", d.item_code, "is_fixed_asset"):
if d.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))
@ -678,3 +665,18 @@ class PurchaseInvoice(BuyingController):
def make_debit_note(source_name, target_doc=None):
from erpnext.controllers.sales_and_purchase_return import make_return_doc
return make_return_doc("Purchase Invoice", source_name, target_doc)
@frappe.whitelist()
def get_fixed_asset_account(asset, account=None):
if account:
if frappe.db.get_value("Account", account, "account_type") != "Fixed Asset":
account=None
if not account:
asset_category, company = frappe.db.get_value("Asset", asset, ["asset_category", "company"])
account = frappe.db.get_value("Asset Category Account",
filters={"parent": asset_category, "company_name": company}, fieldname="fixed_asset_account")
return account

View File

@ -1016,6 +1016,31 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"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,
"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,
@ -1098,6 +1123,7 @@
"bold": 0,
"collapsible": 0,
"default": ":Company",
"depends_on": "eval:!doc.is_fixed_asset",
"fieldname": "cost_center",
"fieldtype": "Link",
"hidden": 0,
@ -1335,6 +1361,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "is_fixed_asset",
"fieldname": "asset",
"fieldtype": "Link",
"hidden": 0,
@ -1545,7 +1572,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2016-04-18 08:08:53.056818",
"modified": "2016-04-18 08:08:53.056819",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",

View File

@ -269,6 +269,22 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_sales_return",
frm: cur_frm
})
},
asset: function(frm, cdt, cdn) {
var row = locals[cdt][cdn];
if(row.asset) {
frappe.call({
method: erpnext.accounts.doctype.asset.depreciation.get_disposal_account_and_cost_center,
args: {
"company": frm.doc.company
},
callback: function(r, rt) {
frappe.model.set_value(cdt, cdn, "income_account", r.message[0]);
frappe.model.set_value(cdt, cdn, "cost_center", r.message[1]);
}
})
}
}
});

View File

@ -13,7 +13,8 @@ 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
from erpnext.accounts.doctype.asset.depreciation \
import get_disposal_account_and_cost_center, get_gl_entries_on_asset_disposal
form_grid_templates = {
"items": "templates/form_grid/item_grid.html"
@ -60,8 +61,9 @@ class SalesInvoice(SellingController):
self.validate_advance_jv("Sales Order")
self.add_remarks()
self.validate_write_off_account()
self.validate_fixed_asset()
self.set_income_account_for_fixed_assets()
if cint(self.is_pos):
self.validate_pos()
@ -454,16 +456,15 @@ class SalesInvoice(SellingController):
return warehouse
def set_income_account_for_fixed_assets(self):
disposal_account = None
disposal_account = depreciation_cost_center = None
for d in self.get("items"):
if frappe.db.get_value("Item", d.item_code, "is_fixed_asset"):
if d.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"))
disposal_account, depreciation_cost_center = get_disposal_account_and_cost_center(self.company)
d.income_account = disposal_account
if not d.cost_center:
d.cost_center = depreciation_cost_center
def check_prev_docstatus(self):
for d in self.get('items'):
@ -472,13 +473,6 @@ 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()
@ -565,19 +559,16 @@ class SalesInvoice(SellingController):
# income account gl entries
for item in self.get("items"):
if flt(item.base_net_amount):
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)
if item.is_fixed_asset:
asset = frappe.get_doc("Asset", item.asset)
fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(asset, item.base_net_amount)
for gle in fixed_asset_gl_entries:
gle["against"] = self.customer
gl_entries.append(self.get_gl_dict(gle))
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)
asset.db_set("disposal_date", self.posting_date)
asset.set_status("Sold" if self.docstatus==1 else None)
else:
account_currency = get_account_currency(item.income_account)
gl_entries.append(
@ -743,4 +734,4 @@ def make_delivery_note(source_name, target_doc=None):
@frappe.whitelist()
def make_sales_return(source_name, target_doc=None):
from erpnext.controllers.sales_and_purchase_return import make_return_doc
return make_return_doc("Sales Invoice", source_name, target_doc)
return make_return_doc("Sales Invoice", source_name, target_doc)

View File

@ -0,0 +1,41 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.query_reports["Asset Depreciation Ledger"] = {
"filters": [
{
"fieldname":"company",
"label": __("Company"),
"fieldtype": "Link",
"options": "Company",
"default": frappe.defaults.get_user_default("Company"),
"reqd": 1
},
{
"fieldname":"from_date",
"label": __("From Date"),
"fieldtype": "Date",
"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
"reqd": 1
},
{
"fieldname":"to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": frappe.datetime.get_today(),
"reqd": 1
},
{
"fieldname":"asset",
"label": __("Asset"),
"fieldtype": "Link",
"options": "Asset"
},
{
"fieldname":"asset_category",
"label": __("Asset Category"),
"fieldtype": "Link",
"options": "Asset Category"
}
]
}

View File

@ -0,0 +1,18 @@
{
"add_total_row": 0,
"apply_user_permissions": 1,
"creation": "2016-04-08 14:49:58.133098",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"modified": "2016-04-08 14:49:58.133098",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Asset Depreciation Ledger",
"owner": "Administrator",
"ref_doctype": "Asset",
"report_name": "Asset Depreciation Ledger",
"report_type": "Script Report"
}

View File

@ -0,0 +1,118 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe import _
def execute(filters=None):
columns, data = get_columns(), get_data(filters)
return columns, data
def get_data(filters):
data = frappe.db.sql("""
select
a.name as asset, a.asset_category, a.status,
a.depreciation_method, a.purchase_date, a.gross_purchase_amount,
ds.schedule_date as depreciation_date, ds.depreciation_amount,
ds.accumulated_depreciation_amount,
(a.gross_purchase_amount - ds.accumulated_depreciation_amount) as amount_after_depreciation,
ds.journal_entry as depreciation_entry
from
`tabAsset` a, `tabDepreciation Schedule` ds
where
a.name = ds.parent
and a.docstatus=1
and ifnull(ds.journal_entry, '') != ''
and ds.schedule_date between %(from_date)s and %(to_date)s
and a.company = %(company)s
{conditions}
order by
a.name asc, ds.schedule_date asc
""".format(conditions=get_filter_conditions(filters)), filters, as_dict=1)
return data
def get_filter_conditions(filters):
conditions = ""
if filters.get("asset"):
conditions += " and a.name = %(asset)s"
if filters.get("asset_category"):
conditions += " and a.asset_category = %(asset_category)s"
return conditions
def get_columns():
return [
{
"label": _("Asset"),
"fieldname": "asset",
"fieldtype": "Link",
"options": "Asset",
"width": 120
},
{
"label": _("Depreciation Date"),
"fieldname": "depreciation_date",
"fieldtype": "Date",
"width": 120
},
{
"label": _("Purchase Amount"),
"fieldname": "gross_purchase_amount",
"fieldtype": "Currency",
"width": 120
},
{
"label": _("Depreciation Amount"),
"fieldname": "depreciation_amount",
"fieldtype": "Currency",
"width": 140
},
{
"label": _("Accumulated Depreciation Amount"),
"fieldname": "accumulated_depreciation_amount",
"fieldtype": "Currency",
"width": 210
},
{
"label": _("Amount After Depreciation"),
"fieldname": "amount_after_depreciation",
"fieldtype": "Currency",
"width": 180
},
{
"label": _("Depreciation Entry"),
"fieldname": "depreciation_entry",
"fieldtype": "Link",
"options": "Journal Entry",
"width": 140
},
{
"label": _("Asset Category"),
"fieldname": "asset_category",
"fieldtype": "Link",
"options": "Asset Category",
"width": 120
},
{
"label": _("Current Status"),
"fieldname": "status",
"fieldtype": "Data",
"width": 120
},
{
"label": _("Depreciation Method"),
"fieldname": "depreciation_method",
"fieldtype": "Data",
"width": 130
},
{
"label": _("Purchase Date"),
"fieldname": "purchase_date",
"fieldtype": "Date",
"width": 120
}
]

View File

@ -0,0 +1,35 @@
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.query_reports["Asset Depreciations and Balances"] = {
"filters": [
{
"fieldname":"company",
"label": __("Company"),
"fieldtype": "Link",
"options": "Company",
"default": frappe.defaults.get_user_default("Company"),
"reqd": 1
},
{
"fieldname":"from_date",
"label": __("From Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_start_date"),
"reqd": 1
},
{
"fieldname":"to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_end_date"),
"reqd": 1
},
{
"fieldname":"asset_category",
"label": __("Asset Category"),
"fieldtype": "Link",
"options": "Asset Category"
}
]
}

View File

@ -0,0 +1,18 @@
{
"add_total_row": 0,
"apply_user_permissions": 1,
"creation": "2016-04-08 14:56:37.235981",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"modified": "2016-04-08 14:56:37.235981",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Asset Depreciations and Balances",
"owner": "Administrator",
"ref_doctype": "Asset",
"report_name": "Asset Depreciations and Balances",
"report_type": "Script Report"
}

View File

@ -0,0 +1,184 @@
# Copyright (c) 2013, 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 formatdate, getdate, flt, add_days
def execute(filters=None):
filters.day_before_from_date = add_days(filters.from_date, -1)
columns, data = get_columns(filters), get_data(filters)
return columns, data
def get_data(filters):
data = []
asset_categories = get_asset_categories(filters)
assets = get_assets(filters)
asset_costs = get_asset_costs(assets, filters)
asset_depreciations = get_accumulated_depreciations(assets, filters)
for asset_category in asset_categories:
row = frappe._dict()
row.asset_category = asset_category
row.update(asset_costs.get(asset_category))
row.cost_as_on_to_date = (flt(row.cost_as_on_from_date) + flt(row.cost_of_new_purchase)
- flt(row.cost_of_sold_asset) - flt(row.cost_of_scrapped_asset))
row.update(asset_depreciations.get(asset_category))
row.accumulated_depreciation_as_on_to_date = (flt(row.accumulated_depreciation_as_on_from_date) +
flt(row.depreciation_amount_during_the_period) - flt(row.depreciation_eliminated))
row.net_asset_value_as_on_from_date = (flt(row.cost_as_on_from_date) -
flt(row.accumulated_depreciation_as_on_from_date))
row.net_asset_value_as_on_to_date = (flt(row.cost_as_on_to_date) -
flt(row.accumulated_depreciation_as_on_to_date))
data.append(row)
return data
def get_asset_categories(filters):
return frappe.db.sql_list("""
select distinct asset_category from `tabAsset`
where docstatus=1 and company=%s and purchase_date <= %s
""", (filters.company, filters.to_date))
def get_assets(filters):
return frappe.db.sql("""
select name, asset_category, purchase_date, gross_purchase_amount, disposal_date, status
from `tabAsset`
where docstatus=1 and company=%s and purchase_date <= %s""",
(filters.company, filters.to_date), as_dict=1)
def get_asset_costs(assets, filters):
asset_costs = frappe._dict()
for d in assets:
asset_costs.setdefault(d.asset_category, frappe._dict({
"cost_as_on_from_date": 0,
"cost_of_new_purchase": 0,
"cost_of_sold_asset": 0,
"cost_of_scrapped_asset": 0
}))
costs = asset_costs[d.asset_category]
if getdate(d.purchase_date) < getdate(filters.from_date):
if not d.disposal_date or getdate(d.disposal_date) >= getdate(filters.from_date):
costs.cost_as_on_from_date += flt(d.gross_purchase_amount)
else:
costs.cost_of_new_purchase += flt(d.gross_purchase_amount)
if d.disposal_date and getdate(d.disposal_date) >= getdate(filters.from_date) \
and getdate(d.disposal_date) <= getdate(filters.to_date):
if d.status == "Sold":
costs.cost_of_sold_asset += flt(d.gross_purchase_amount)
elif d.status == "Scrapped":
costs.cost_of_scrapped_asset += flt(d.gross_purchase_amount)
return asset_costs
def get_accumulated_depreciations(assets, filters):
asset_depreciations = frappe._dict()
for d in assets:
asset = frappe.get_doc("Asset", d.name)
asset_depreciations.setdefault(d.asset_category, frappe._dict({
"accumulated_depreciation_as_on_from_date": asset.opening_accumulated_depreciation,
"depreciation_amount_during_the_period": 0,
"depreciation_eliminated_during_the_period": 0
}))
depr = asset_depreciations[d.asset_category]
for schedule in asset.get("schedules"):
if getdate(schedule.schedule_date) < getdate(filters.from_date):
if not asset.disposal_date and getdate(asset.disposal_date) >= getdate(filters.from_date):
depr.accumulated_depreciation_as_on_from_date += flt(schedule.depreciation_amount)
elif getdate(schedule.schedule_date) <= getdate(filters.to_date):
depr.depreciation_amount_during_the_period += flt(schedule.depreciation_amount)
if asset.disposal_date and getdate(schedule.schedule_date) > getdate(asset.disposal_date):
depr.depreciation_eliminated_during_the_period += flt(schedule.depreciation_amount)
return asset_depreciations
def get_columns(filters):
return [
{
"label": _("Asset Category"),
"fieldname": "asset_category",
"fieldtype": "Link",
"options": "Asset Category",
"width": 120
},
{
"label": _("Cost as on") + " " + formatdate(filters.day_before_from_date),
"fieldname": "cost_as_on_from_date",
"fieldtype": "Currency",
"width": 140
},
{
"label": _("Cost of New Purchase"),
"fieldname": "cost_of_new_purchase",
"fieldtype": "Currency",
"width": 140
},
{
"label": _("Cost of Sold Asset"),
"fieldname": "cost_of_sold_asset",
"fieldtype": "Currency",
"width": 140
},
{
"label": _("Cost of Scrapped Asset"),
"fieldname": "cost_of_scrapped_asset",
"fieldtype": "Currency",
"width": 140
},
{
"label": _("Cost as on") + " " + formatdate(filters.to_date),
"fieldname": "cost_as_on_to_date",
"fieldtype": "Currency",
"width": 140
},
{
"label": _("Accumulated Depreciation as on") + " " + formatdate(filters.day_before_from_date),
"fieldname": "accumulated_depreciation_as_on_from_date",
"fieldtype": "Currency",
"width": 270
},
{
"label": _("Depreciation Amount during the period"),
"fieldname": "depreciation_amount_during_the_period",
"fieldtype": "Currency",
"width": 240
},
{
"label": _("Depreciation Eliminated due to disposal of assets"),
"fieldname": "depreciation_eliminated_during_the_period",
"fieldtype": "Currency",
"width": 300
},
{
"label": _("Accumulated Depreciation as on") + " " + formatdate(filters.to_date),
"fieldname": "accumulated_depreciation_as_on_to_date",
"fieldtype": "Currency",
"width": 270
},
{
"label": _("Net Asset value as on") + " " + formatdate(filters.day_before_from_date),
"fieldname": "net_asset_value_as_on_from_date",
"fieldtype": "Currency",
"width": 200
},
{
"label": _("Net Asset value as on") + " " + formatdate(filters.to_date),
"fieldname": "net_asset_value_as_on_to_date",
"fieldtype": "Currency",
"width": 200
}
]

View File

@ -79,18 +79,4 @@ class PurchaseCommon(BuyingController):
status = frappe.db.get_value(doctype, docname, "status")
if status == "Closed":
frappe.throw(_("{0} {1} status is {2}").format(doctype, docname, status), frappe.InvalidStatusError)
def check_docstatus(self, check, doctype, docname, detail_doctype = ''):
if check == 'Next':
submitted = frappe.db.sql("""select t1.name from `tab%s` t1,`tab%s` t2
where t1.name = t2.parent and t2.prevdoc_docname = %s and t1.docstatus = 1"""
% (doctype, detail_doctype, '%s'), docname)
if submitted:
frappe.throw(_("{0} {1} has already been submitted").format(doctype, submitted[0][0]))
if check == 'Previous':
submitted = frappe.db.sql("""select name from `tab%s`
where docstatus = 1 and name = %s""" % (doctype, '%s'), docname)
if not submitted:
frappe.throw(_("{0} {1} is not submitted").format(doctype, submitted[0][0]))
frappe.throw(_("{0} {1} status is {2}").format(doctype, docname, status), frappe.InvalidStatusError)

View File

@ -5,7 +5,7 @@ from __future__ import unicode_literals
import frappe
import json
from frappe.utils import cstr, flt
from frappe import msgprint, _, throw
from frappe import msgprint, _
from frappe.model.mapper import get_mapped_doc
from erpnext.controllers.buying_controller import BuyingController
from erpnext.stock.doctype.item.item import get_last_purchase_details
@ -189,17 +189,6 @@ class PurchaseOrder(BuyingController):
pc_obj = frappe.get_doc('Purchase Common')
self.check_for_closed_status(pc_obj)
# Check if Purchase Receipt has been submitted against current Purchase Order
pc_obj.check_docstatus(check = 'Next', doctype = 'Purchase Receipt', docname = self.name, detail_doctype = 'Purchase Receipt Item')
# Check if Purchase Invoice has been submitted against current Purchase Order
submitted = frappe.db.sql_list("""select t1.name
from `tabPurchase Invoice` t1,`tabPurchase Invoice Item` t2
where t1.name = t2.parent and t2.purchase_order = %s and t1.docstatus = 1""",
self.name)
if submitted:
throw(_("Purchase Invoice {0} is already submitted").format(", ".join(submitted)))
frappe.db.set(self,'status','Cancelled')
self.update_prevdoc_status()

View File

@ -340,6 +340,18 @@ def get_data():
"label": _("Other Reports"),
"icon": "icon-table",
"items": [
{
"type": "report",
"name": "Asset Depreciation Ledger",
"doctype": "Asset",
"is_query_report": True,
},
{
"type": "report",
"name": "Asset Depreciations and Balances",
"doctype": "Asset",
"is_query_report": True,
},
{
"type": "report",
"name": "Trial Balance for Party",

View File

@ -4,7 +4,7 @@
from __future__ import unicode_literals
import frappe
from frappe import _, throw
from frappe.utils import today, flt, cint, fmt_money, formatdate
from frappe.utils import today, flt, cint, fmt_money, formatdate, getdate
from erpnext.setup.utils import get_company_currency, get_exchange_rate
from erpnext.accounts.utils import get_fiscal_years, validate_fiscal_year, get_account_currency
from erpnext.utilities.transaction_base import TransactionBase
@ -470,18 +470,46 @@ class AccountsController(TransactionBase):
# 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))
def validate_fixed_asset(self):
for d in self.get("items"):
if d.is_fixed_asset:
if d.qty > 1:
frappe.throw(_("Row #{0}: Qty must be 1, as item is a fixed asset. Please use separate row for multiple qty.").format(d.idx))
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))
if d.meta.get_field("asset"):
if not d.asset:
frappe.throw(_("Row #{0}: Asset is mandatory for fixed asset purchase/sale")
.format(d.idx))
else:
asset = frappe.get_doc("Asset", d.asset)
if asset.company != self.company:
frappe.throw(_("Row #{0}: Asset {1} does not belong to company {2}")
.format(d.idx, d.asset, self.company))
elif asset.item_code != d.item_code:
frappe.throw(_("Row #{0}: Asset {1} does not linked to Item {2}")
.format(d.idx, d.asset, d.item_code))
elif asset.docstatus != 1:
frappe.throw(_("Row #{0}: Asset {1} must be submitted").format(d.idx, d.asset))
elif self.doctype == "Purchase Invoice":
if asset.status != "Submitted":
frappe.throw(_("Row #{0}: Asset {1} is already {2}")
.format(d.idx, d.asset, asset.status))
elif getdate(asset.purchase_date) != getdate(self.posting_date):
frappe.throw(_("Row #{0}: Posting Date must be same as purchase date {1} of asset {2}").format(d.idx, asset.purchase_date, d.asset))
elif asset.is_existing_asset:
frappe.throw(_("Row #{0}: Purchase Invoice cannot be made against an existing asset {1}").format(d.idx, d.asset))
elif self.docstatus=="Sales Invoice" and self.docstatus == 1:
if self.update_stock:
frappe.throw(_("'Update Stock' cannot be checked for fixed asset sale"))
elif asset.status in ("Scrapped", "Cancelled", "Sold"):
frappe.throw(_("Row #{0}: Asset {1} cannot be submitted, it is already {2}")
.format(d.idx, d.asset, asset.status))
@frappe.whitelist()
def get_tax_rate(account_head):

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 KiB

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 243 KiB

After

Width:  |  Height:  |  Size: 288 KiB

View File

@ -310,6 +310,27 @@
<tr >
<td>19</td>
<td ><code>project</code></td>
<td >
Link</td>
<td >
Project
</td>
<td>
<a href="https://frappe.github.io/erpnext/current/models/projects/project">Project</a>
</td>
</tr>
<tr >
<td>20</td>
<td ><code>col_break3</code></td>
<td class="info">
Column Break</td>
@ -321,7 +342,7 @@
</tr>
<tr >
<td>20</td>
<td>21</td>
<td ><code>min_order_qty</code></td>
<td >
Float</td>
@ -333,7 +354,7 @@
</tr>
<tr >
<td>21</td>
<td>22</td>
<td ><code>projected_qty</code></td>
<td >
Float</td>
@ -345,7 +366,7 @@
</tr>
<tr >
<td>22</td>
<td>23</td>
<td ><code>ordered_qty</code></td>
<td >
Float</td>
@ -357,7 +378,7 @@
</tr>
<tr >
<td>23</td>
<td>24</td>
<td ><code>page_break</code></td>
<td >
Check</td>

View File

@ -1,4 +1,4 @@
In ERPNext, you can maintain your fixed asset records like Computers, Building, Cars etc and manage depreciations, sell or disposal of those assets.
In ERPNext, you can maintain fixed asset records like Computers, Furnitures, Cars etc and manage depreciations, sell or disposal of those assets.
## Asset Category
@ -11,24 +11,27 @@ To start first you should create Asset Category, depending on the type of assets
## 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.
Next step will be creating the fixed asset records. Asset record is the heart of fixed asset management, all the activities like purchasing, depreciation, scrapping or sales are managed against it.
<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.
2. Is Existing Asset: Check if the asset is being carried forward from the previous Fiscal Year. The existing assets which are partially / fully depreciated can also be created/maintained for the future reference.
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.
4. Warehouse: Set the location of the asset.
5. Gross Purchase Amount: The purchase cost of the asset
6. 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.
7. Opening Accumulated Depreciation: The accumulated depreciation amount which has already been booked for an existing asset.
8. 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.
9. 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.
10. Total Number of Depreciations: The total number of depreciations during the useful life. In case of existing assets which are partially depreciated, mention the number of pending depreciations.
11. Number of Depreciations Booked: Enter the number of already booked depreciations for an existing asset.
12. Frequency of Depreciation (Months): The number of months between two depreciations.
13. Next Depreciation Date: Mention the next depreciation date, even if it is the first one. If the asset is an existing one and depreciation has already been completed, leave it blank.
### Depreciations
@ -45,16 +48,16 @@ In the depreciation entry, the "Accumulated Depreciation Account" is credited an
## 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.
For purchasing a new asset, create and submit the asset record with all the depreciation settings. Then create a Purchase Invoice via "Make Purchase Invoice" button. On clicking the button, system will load a new Purchase Invoice form with pre-loaded items table. It will also set proper fixed asset account (defined in teh Asset Category) in the Expense Account field. You need to select Supplier and other necessary details and submit the Purchase Invoice.
<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.
On submission of the invoice, the "Fixed Asset Account" will be debited and payable account will be credited. It also updates purchase date, supplier and Purchase Invoice no in the Asset record.
## Sell an ssset
## Sale 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:
To sale an asset, open the asset record and create a Sales Invoice using "Sale Asset" button. On submission of the 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.
@ -66,6 +69,6 @@ To sale an asset, create a Sales Invoice against the item linked with the asset.
## 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.
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

@ -3,7 +3,6 @@ auto-creation-of-material-request
depreciation-entry
maintain-stock-field-frozen-in-item-master
manage-rejected-finished-goods-items
managing-assets
managing-batch-wise-inventory
managing-fractions-in-uom
opening-stock-balance-entry-for-the-serialized-and-batch-item

View File

@ -1,23 +0,0 @@
#Managing Assets
Items like machinery, furniture, land and property, patents etc. can be categorized as fixed asset of a company. In ERPNext, you can maintain fixed asset items, mainly stock items in a separate Warehouse.
For the high value asset items, serialized inventory can be maintained. It will allow assigning unique Serial No. to each unit of an asset item.
####Fixed Asset Master
While creating Item Code for the fixed asset item, you should check "Is Fixed Asset" field.
<img alt ="Fixed Asset Item" class="screenshot" src="{{docs_base_url}}/assets/img/articles/managing-assets-1.png">
Other item properties like Stock/Non-stock item can be updated on the nature of asset. Like patent and trademarks will be non-stock assets.
If your asset item is serialized, click [here]({{docs_base_url}}/user/videos/learn/serialized-inventory.html) to learn how serialized inventory is managed in ERPNext.
####Warehouse for Fixed Asset
Separate Warehouse should be created for the fixed asset items. All the sales, purchase and stock transactions for asset items should be done for fixed asset warehouse only.
As per the perpetual inventory valuation system, accounting ledger is auto-created for a warehouse. You can move the accounting ledger of fixed asset from Stock Asset (default group) to Fixed Asset's group account.
<!-- markdown -->

View File

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

View File

@ -1,12 +1,12 @@
[
{
"bom_no": "BOM-_Test FG Item-001",
"company": "_Test Company",
"doctype": "Production Order",
"fg_warehouse": "_Test Warehouse 1 - _TC",
"production_item": "_Test FG Item",
"qty": 10.0,
"stock_uom": "_Test UOM",
"bom_no": "BOM-_Test FG Item-001",
"company": "_Test Company",
"doctype": "Production Order",
"fg_warehouse": "_Test Warehouse 1 - _TC",
"production_item": "_Test FG Item",
"qty": 10.0,
"stock_uom": "_Test UOM",
"wip_warehouse": "_Test Warehouse - _TC"
}
]

View File

@ -1,10 +0,0 @@
# 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

@ -276,36 +276,6 @@
"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": 1,
@ -2256,7 +2226,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 1,
"modified": "2016-04-15 07:10:24.271811",
"modified": "2016-04-15 07:10:24.271815",
"modified_by": "Administrator",
"module": "Stock",
"name": "Item",

View File

@ -88,7 +88,6 @@ 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")
@ -609,11 +608,6 @@ class Item(WebsiteGenerator):
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"))
@frappe.whitelist()
def get_dashboard_data(name):
'''load dashboard related data'''

View File

@ -9,7 +9,6 @@
"has_serial_no": 0,
"income_account": "Sales - _TC",
"inspection_required": 0,
"is_fixed_asset": 0,
"is_pro_applicable": 0,
"is_stock_item": 1,
"is_sub_contracted_item": 0,
@ -39,7 +38,6 @@
"has_serial_no": 0,
"income_account": "Sales - _TC",
"inspection_required": 0,
"is_fixed_asset": 0,
"is_pro_applicable": 0,
"is_stock_item": 1,
"is_sub_contracted_item": 0,
@ -60,7 +58,6 @@
"has_serial_no": 0,
"income_account": "Sales - _TC",
"inspection_required": 0,
"is_fixed_asset": 0,
"is_pro_applicable": 0,
"is_stock_item": 1,
"is_sub_contracted_item": 0,
@ -87,7 +84,6 @@
"has_serial_no": 0,
"income_account": "Sales - _TC",
"inspection_required": 0,
"is_fixed_asset": 0,
"is_pro_applicable": 0,
"is_sub_contracted_item": 0,
"item_code": "_Test Item Home Desktop 200",
@ -104,7 +100,6 @@
"has_serial_no": 0,
"income_account": "Sales - _TC",
"inspection_required": 0,
"is_fixed_asset": 0,
"is_pro_applicable": 0,
"is_stock_item": 0,
"is_sub_contracted_item": 0,
@ -123,7 +118,6 @@
"has_serial_no": 0,
"income_account": "Sales - _TC",
"inspection_required": 0,
"is_fixed_asset": 0,
"is_pro_applicable": 1,
"is_stock_item": 1,
"is_sub_contracted_item": 1,
@ -138,7 +132,6 @@
"has_batch_no": 0,
"has_serial_no": 0,
"inspection_required": 0,
"is_fixed_asset": 0,
"is_pro_applicable": 0,
"is_stock_item": 0,
"is_sub_contracted_item": 0,
@ -154,7 +147,6 @@
"has_batch_no": 0,
"has_serial_no": 1,
"inspection_required": 0,
"is_fixed_asset": 0,
"is_pro_applicable": 0,
"is_stock_item": 1,
"is_sub_contracted_item": 0,
@ -170,7 +162,6 @@
"has_batch_no": 0,
"has_serial_no": 1,
"inspection_required": 0,
"is_fixed_asset": 0,
"is_pro_applicable": 0,
"is_stock_item": 1,
"is_sub_contracted_item": 0,
@ -190,7 +181,6 @@
"has_serial_no": 0,
"income_account": "Sales - _TC",
"inspection_required": 0,
"is_fixed_asset": 0,
"is_pro_applicable": 1,
"is_stock_item": 1,
"is_sub_contracted_item": 0,
@ -209,7 +199,6 @@
"has_serial_no": 0,
"income_account": "Sales - _TC",
"inspection_required": 0,
"is_fixed_asset": 0,
"is_pro_applicable": 1,
"is_stock_item": 1,
"is_sub_contracted_item": 1,
@ -228,7 +217,6 @@
"has_serial_no": 0,
"income_account": "Sales - _TC",
"inspection_required": 0,
"is_fixed_asset": 0,
"is_pro_applicable": 1,
"is_stock_item": 1,
"is_sub_contracted_item": 1,

View File

@ -115,7 +115,6 @@ class MaterialRequest(BuyingController):
pc_obj = frappe.get_doc('Purchase Common')
pc_obj.check_for_closed_status(self.doctype, self.name)
pc_obj.check_docstatus(check = 'Next', doctype = 'Purchase Order', docname = self.name, detail_doctype = 'Purchase Order Item')
self.update_requested_qty()