diff --git a/erpnext/accounts/doctype/asset/asset.js b/erpnext/accounts/doctype/asset/asset.js index 0dfdb219ffe..0479a4ec113 100644 --- a/erpnext/accounts/doctype/asset/asset.js +++ b/erpnext/accounts/doctype/asset/asset.js @@ -24,6 +24,7 @@ frappe.ui.form.on('Asset', { refresh: function(frm) { frappe.ui.form.trigger("Asset", "is_existing_asset"); + frm.toggle_display("next_depreciation_date", frm.doc.docstatus < 1); if (frm.doc.docstatus==1) { if (frm.doc.status=='Submitted' && !frm.doc.is_existing_asset && !frm.doc.purchase_invoice) { @@ -32,6 +33,10 @@ frappe.ui.form.on('Asset', { }); } if (in_list(["Submitted", "Partially Depreciated", "Fully Depreciated"], frm.doc.status)) { + frm.add_custom_button("Transfer Asset", function() { + erpnext.asset.transfer_asset(frm); + }); + frm.add_custom_button("Scrap Asset", function() { erpnext.asset.scrap_asset(frm); }); @@ -45,9 +50,56 @@ frappe.ui.form.on('Asset', { erpnext.asset.restore_asset(frm); }); } + + frm.trigger("show_graph"); } }, + show_graph: function(frm) { + var x_intervals = ["x", frm.doc.purchase_date]; + var asset_values = ["Asset Value", frm.doc.gross_purchase_amount]; + var last_depreciation_date = frm.doc.purchase_date; + + if(frm.doc.opening_accumulated_depreciation) { + last_depreciation_date = frappe.datetime.add_months(frm.doc.next_depreciation_date, + -1*frm.doc.frequency_of_depreciation); + + x_intervals.push(last_depreciation_date); + asset_values.push(flt(frm.doc.gross_purchase_amount) - + flt(frm.doc.opening_accumulated_depreciation)); + } + + $.each(frm.doc.schedules || [], function(i, v) { + x_intervals.push(v.schedule_date); + asset_value = flt(frm.doc.gross_purchase_amount) - flt(v.accumulated_depreciation_amount); + if(v.journal_entry) { + last_depreciation_date = v.schedule_date; + asset_values.push(asset_value) + } else { + if (in_list(["Scrapped", "Sold"], frm.doc.status)) { + asset_values.push(null) + } else { + asset_values.push(asset_value) + } + } + }) + + if(in_list(["Scrapped", "Sold"], frm.doc.status)) { + x_intervals.push(frm.doc.disposal_date); + asset_values.push(0); + last_depreciation_date = frm.doc.disposal_date; + } + + frm.dashboard.reset(); + frm.dashboard.add_graph({ + x: 'x', + columns: [x_intervals, asset_values], + regions: { + 'Asset Value': [{'start': last_depreciation_date, 'style':'dashed'}] + } + }); + }, + 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); @@ -112,4 +164,55 @@ erpnext.asset.restore_asset = function(frm) { } }) }) +} + +erpnext.asset.transfer_asset = function(frm) { + var dialog = new frappe.ui.Dialog({ + title: __("Transfer Asset"), + fields: [ + { + "label": __("Target Warehouse"), + "fieldname": "target_warehouse", + "fieldtype": "Link", + "options": "Warehouse", + "get_query": function () { + return { + filters: [["Warehouse", "company", "in", ["", cstr(frm.doc.company)]]] + } + }, + "reqd": 1 + }, + { + "label": __("Date"), + "fieldname": "transfer_date", + "fieldtype": "Datetime", + "reqd": 1, + "default": frappe.datetime.now_datetime() + } + ] + }); + + dialog.set_primary_action(__("Transfer"), function() { + args = dialog.get_values(); + if(!args) return; + dialog.hide(); + return frappe.call({ + type: "GET", + method: "erpnext.accounts.doctype.asset.asset.transfer_asset", + args: { + args: { + "asset": frm.doc.name, + "transaction_date": args.transfer_date, + "source_warehouse": frm.doc.warehouse, + "target_warehouse": args.target_warehouse, + "company": frm.doc.company + } + }, + freeze: true, + callback: function(r) { + cur_frm.reload_doc(); + } + }) + }); + dialog.show(); } \ No newline at end of file diff --git a/erpnext/accounts/doctype/asset/asset.json b/erpnext/accounts/doctype/asset/asset.json index 70c1c166f49..c272a5809ac 100644 --- a/erpnext/accounts/doctype/asset/asset.json +++ b/erpnext/accounts/doctype/asset/asset.json @@ -97,7 +97,7 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, - "in_list_view": 0, + "in_list_view": 1, "label": "Status", "length": 0, "no_copy": 1, @@ -113,6 +113,31 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 1, + "bold": 0, + "collapsible": 0, + "fieldname": "image", + "fieldtype": "Attach Image", + "hidden": 1, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Image", + "length": 0, + "no_copy": 1, + "permlevel": 0, + "precision": "", + "print_hide": 1, + "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, @@ -706,13 +731,14 @@ "hide_heading": 0, "hide_toolbar": 0, "idx": 72, + "image_field": "image", "in_create": 0, "in_dialog": 0, "is_submittable": 1, "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2016-04-20 18:09:07.573716", + "modified": "2016-04-22 11:15:40.055518", "modified_by": "Administrator", "module": "Accounts", "name": "Asset", diff --git a/erpnext/accounts/doctype/asset/asset.py b/erpnext/accounts/doctype/asset/asset.py index da229fee9fd..23bb08be59f 100644 --- a/erpnext/accounts/doctype/asset/asset.py +++ b/erpnext/accounts/doctype/asset/asset.py @@ -190,4 +190,17 @@ def make_sales_invoice(asset, item_code, company): "qty": 1 }) si.set_missing_values() - return si \ No newline at end of file + return si + +@frappe.whitelist() +def transfer_asset(args): + import json + args = json.loads(args) + movement_entry = frappe.new_doc("Asset Movement") + movement_entry.update(args) + movement_entry.insert() + movement_entry.submit() + + frappe.db.commit() + + frappe.msgprint(_("Asset Movement record {0} created").format("{0}".format(movement_entry.name))) \ No newline at end of file diff --git a/erpnext/accounts/doctype/asset_movement/__init__.py b/erpnext/accounts/doctype/asset_movement/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/doctype/asset_movement/asset_movement.js b/erpnext/accounts/doctype/asset_movement/asset_movement.js new file mode 100644 index 00000000000..680eedc9bc6 --- /dev/null +++ b/erpnext/accounts/doctype/asset_movement/asset_movement.js @@ -0,0 +1,15 @@ +// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Asset Movement', { + onload: function(frm) { + frm.add_fetch("asset", "warehouse", "source_warehouse"); + + frm.set_query("target_warehouse", function() { + return { + filters: [["Warehouse", "company", "in", ["", cstr(frm.doc.company)]]] + } + }) + + } +}); diff --git a/erpnext/accounts/doctype/asset_movement/asset_movement.json b/erpnext/accounts/doctype/asset_movement/asset_movement.json new file mode 100644 index 00000000000..59b88236dad --- /dev/null +++ b/erpnext/accounts/doctype/asset_movement/asset_movement.json @@ -0,0 +1,274 @@ +{ + "allow_copy": 0, + "allow_import": 1, + "allow_rename": 0, + "autoname": "AM-.#####", + "creation": "2016-04-25 18:00:23.559973", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "fields": [ + { + "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": 0, + "options": "Asset", + "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": "transaction_date", + "fieldtype": "Datetime", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Transaction 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": "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": "column_break_4", + "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": "source_warehouse", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Source Warehouse", + "length": 0, + "no_copy": 0, + "options": "Warehouse", + "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": "target_warehouse", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Target 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": "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 Movement", + "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-04-25 19:14:08.853429", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Asset Movement", + "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": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "apply_user_permissions": 0, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "apply_user_permissions": 0, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 1, + "write": 1 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/asset_movement/asset_movement.py b/erpnext/accounts/doctype/asset_movement/asset_movement.py new file mode 100644 index 00000000000..574c49992b4 --- /dev/null +++ b/erpnext/accounts/doctype/asset_movement/asset_movement.py @@ -0,0 +1,48 @@ +# -*- 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 AssetMovement(Document): + def validate(self): + self.validate_asset() + self.validate_warehouses() + + def validate_asset(self): + status, company = frappe.db.get_value("Asset", self.asset, ["status", "company"]) + if status in ("Draft", "Scrapped", "Sold"): + frappe.throw(_("{0} asset cannot be transferred").format(status)) + + if company != self.company: + frappe.throw(_("Asset {0} does not belong to company {1}").format(self.asset, self.company)) + + def validate_warehouses(self): + if not self.source_warehouse: + self.source_warehouse = frappe.db.get_value("Asset", self.asset, "warehouse") + + if self.source_warehouse == self.target_warehouse: + frappe.throw(_("Source and Target Warehouse cannot be same")) + + def on_submit(self): + self.set_latest_warehouse_in_asset() + + def on_cancel(self): + self.set_latest_warehouse_in_asset() + + def set_latest_warehouse_in_asset(self): + latest_movement_entry = frappe.db.sql("""select target_warehouse from `tabAsset Movement` + where asset=%s and docstatus=1 and company=%s + order by transaction_date desc limit 1""", (self.asset, self.company)) + + if latest_movement_entry: + warehouse = latest_movement_entry[0][0] + else: + warehouse = frappe.db.sql("""select source_warehouse from `tabAsset Movement` + where asset=%s and docstatus=2 and company=%s + order by transaction_date asc limit 1""", (self.asset, self.company))[0][0] + + frappe.db.set_value("Asset", self.asset, "warehouse", warehouse) \ No newline at end of file diff --git a/erpnext/accounts/doctype/asset_movement/test_asset_movement.py b/erpnext/accounts/doctype/asset_movement/test_asset_movement.py new file mode 100644 index 00000000000..9880efc37f8 --- /dev/null +++ b/erpnext/accounts/doctype/asset_movement/test_asset_movement.py @@ -0,0 +1,51 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +from frappe.utils import now +import unittest +from erpnext.accounts.doctype.asset.test_asset import create_asset + + +class TestAssetMovement(unittest.TestCase): + def test_movement(self): + asset = create_asset() + + if asset.docstatus == 0: + asset.submit() + + movement1 = create_asset_movement(asset, target_warehouse="_Test Warehouse 1 - _TC") + self.assertEqual(frappe.db.get_value("Asset", asset.name, "warehouse"), "_Test Warehouse 1 - _TC") + + movement2 = create_asset_movement(asset, target_warehouse="_Test Warehouse 2 - _TC") + self.assertEqual(frappe.db.get_value("Asset", asset.name, "warehouse"), "_Test Warehouse 2 - _TC") + + movement1.cancel() + self.assertEqual(frappe.db.get_value("Asset", asset.name, "warehouse"), "_Test Warehouse 2 - _TC") + + movement2.cancel() + self.assertEqual(frappe.db.get_value("Asset", asset.name, "warehouse"), "_Test Warehouse - _TC") + + asset.load_from_db() + asset.cancel() + frappe.delete_doc("Asset", asset.name) + + +def create_asset_movement(asset, target_warehouse, transaction_date=None): + if not transaction_date: + transaction_date = now() + + movement = frappe.new_doc("Asset Movement") + movement.update({ + "asset": asset.name, + "transaction_date": transaction_date, + "target_warehouse": target_warehouse, + "company": asset.company + }) + + movement.insert() + movement.submit() + + return movement diff --git a/erpnext/config/accounts.py b/erpnext/config/accounts.py index 72ed0f9bbca..d0629e7cb0e 100644 --- a/erpnext/config/accounts.py +++ b/erpnext/config/accounts.py @@ -221,6 +221,11 @@ def get_data(): "name": "Period Closing Voucher", "description": _("Close Balance Sheet and book Profit or Loss.") }, + { + "type": "doctype", + "name": "Asset Movement", + "description": _("Transfer an asset from one warehouse to another") + }, ] }, { diff --git a/erpnext/docs/assets/img/accounts/asset-graph.png b/erpnext/docs/assets/img/accounts/asset-graph.png new file mode 100644 index 00000000000..5b300bb1da1 Binary files /dev/null and b/erpnext/docs/assets/img/accounts/asset-graph.png differ diff --git a/erpnext/docs/assets/img/accounts/asset-movement-using-button.png b/erpnext/docs/assets/img/accounts/asset-movement-using-button.png new file mode 100644 index 00000000000..b9ca68d36ab Binary files /dev/null and b/erpnext/docs/assets/img/accounts/asset-movement-using-button.png differ diff --git a/erpnext/docs/assets/img/accounts/asset-movement.png b/erpnext/docs/assets/img/accounts/asset-movement.png new file mode 100644 index 00000000000..39f7b341a04 Binary files /dev/null and b/erpnext/docs/assets/img/accounts/asset-movement.png differ diff --git a/erpnext/docs/user/manual/en/accounts/managing-fixed-assets.md b/erpnext/docs/user/manual/en/accounts/managing-fixed-assets.md index 2acdd376a4f..1cdbd1e1907 100644 --- a/erpnext/docs/user/manual/en/accounts/managing-fixed-assets.md +++ b/erpnext/docs/user/manual/en/accounts/managing-fixed-assets.md @@ -45,6 +45,10 @@ On the scheduled date, system creates depreciation entry by creating a Journal E In the depreciation entry, the "Accumulated Depreciation Account" is credited and "Depreciation Expense Account" is debited. The related accounts can be set in the Asset Category or Company. +For better visibility, net value of the asset on different depreciation dates are shown in a line graph. + +Asset + ## Purchase an asset @@ -71,4 +75,14 @@ To sale an asset, open the asset record and create a Sales Invoice using "Sale A 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. -Asset \ No newline at end of file +Asset + +## Asset Movement + +The movement of the assets (from one warehouse to another) is also tracked via Asset Movement form. + +Asset + +There is also a dedicated button "Transfer Asset" inside the Asset form to track the Asset Movement. + +Asset \ No newline at end of file