From 4fdb05228d45e3e0f0473e3c27632fde463a0f17 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 9 Mar 2016 12:40:56 +0530 Subject: [PATCH] fixed asset depreciation --- erpnext/accounts/doctype/asset/asset.js | 27 ++- erpnext/accounts/doctype/asset/asset.json | 174 ++++++++++++++-- erpnext/accounts/doctype/asset/asset.py | 107 ++++++---- .../accounts/doctype/asset/depreciation.py | 191 ++++++++++-------- .../doctype/asset_category/asset_category.js | 36 +++- .../asset_category/asset_category.json | 104 ++++------ .../doctype/asset_category/asset_category.py | 7 +- .../asset_category_account/__init__.py | 0 .../asset_category_account.json | 136 +++++++++++++ .../asset_category_account.py | 10 + .../depreciation_schedule.json | 73 +++---- .../journal_entry_account.json | 31 ++- .../purchase_invoice/purchase_invoice.js | 12 ++ .../purchase_invoice/purchase_invoice.py | 24 ++- .../doctype/sales_invoice/sales_invoice.js | 12 ++ .../doctype/sales_invoice/sales_invoice.py | 49 ++++- erpnext/controllers/accounts_controller.py | 15 +- erpnext/hooks.py | 3 +- erpnext/setup/doctype/company/company.js | 6 +- erpnext/setup/doctype/company/company.json | 36 ++-- erpnext/stock/doctype/item/item.json | 60 +++--- erpnext/stock/doctype/item/item.py | 8 +- 22 files changed, 793 insertions(+), 328 deletions(-) create mode 100644 erpnext/accounts/doctype/asset_category_account/__init__.py create mode 100644 erpnext/accounts/doctype/asset_category_account/asset_category_account.json create mode 100644 erpnext/accounts/doctype/asset_category_account/asset_category_account.py diff --git a/erpnext/accounts/doctype/asset/asset.js b/erpnext/accounts/doctype/asset/asset.js index e961b7d5ed..91d41e5dd4 100644 --- a/erpnext/accounts/doctype/asset/asset.js +++ b/erpnext/accounts/doctype/asset/asset.js @@ -4,20 +4,37 @@ frappe.provide("erpnext.asset"); frappe.ui.form.on('Asset', { - refresh: function(frm) { - cur_frm.add_custom_button("Scrap", function() { - erpnext.asset.scrap_asset(); + onload: function(frm) { + frm.set_query("item_code", function() { + return { + "filters": { + "is_stock_item": 0, + "is_fixed_asset": 1, + "disabled": 0 + } + }; }); }, + + refresh: function(frm) { + if(frm.doc.docstatus==1 && frm.doc.status=='Available') { + cur_frm.add_custom_button("Scrap", function() { + erpnext.asset.scrap_asset(frm); + }); + } + } }); -erpnext.asset.scrap_asset = function() { +erpnext.asset.scrap_asset = function(frm) { frappe.confirm(__("Do you really want to scrap this asset?"), function () { frappe.call({ args: { "asset_name": frm.doc.name }, - method: "erpnext.accounts.doctype.asset.depreciation.scrap_asset" + method: "erpnext.accounts.doctype.asset.depreciation.scrap_asset", + callback: function(r) { + cur_frm.reload_doc(); + } }) }) } \ No newline at end of file diff --git a/erpnext/accounts/doctype/asset/asset.json b/erpnext/accounts/doctype/asset/asset.json index 760e00d5a5..8c29c440fe 100644 --- a/erpnext/accounts/doctype/asset/asset.json +++ b/erpnext/accounts/doctype/asset/asset.json @@ -60,6 +60,32 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "item_code", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Item Code", + "length": 0, + "no_copy": 0, + "options": "Item", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_on_submit": 0, "bold": 0, @@ -74,13 +100,13 @@ "in_list_view": 0, "label": "Status", "length": 0, - "no_copy": 0, + "no_copy": 1, "options": "Available\nSold\nScrapped", "permlevel": 0, "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, - "read_only": 0, + "read_only": 1, "report_hide": 0, "reqd": 1, "search_index": 0, @@ -162,6 +188,32 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "purchase_invoice", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Purchase Invoice", + "length": 0, + "no_copy": 1, + "options": "Purchase Invoice", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_on_submit": 0, "bold": 0, @@ -216,7 +268,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "default": "Straight Line", + "default": "", "depends_on": "", "fieldname": "depreciation_method", "fieldtype": "Select", @@ -244,15 +296,14 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "description": "The time period over which the company expects that the asset will be productive", - "fieldname": "useful_life", + "fieldname": "number_of_depreciations", "fieldtype": "Int", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, - "label": "Useful Life (Years)", + "label": "Number of Depreciations", "length": 0, "no_copy": 0, "permlevel": 0, @@ -270,14 +321,14 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "fieldname": "start_depreciation_from_purchase_date", - "fieldtype": "Check", + "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": "Start Depreciation from Purchase Date", + "label": "Number of Months in a Period", "length": 0, "no_copy": 0, "permlevel": 0, @@ -291,6 +342,32 @@ "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": 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, @@ -319,16 +396,17 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "fieldname": "gross_value", + "fieldname": "gross_purchase_amount", "fieldtype": "Currency", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, - "label": "Gross Value", + "label": "Gross Purchase Amount", "length": 0, "no_copy": 0, + "options": "Company:company:default_currency", "permlevel": 0, "precision": "", "print_hide": 0, @@ -344,17 +422,18 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "description": "The estimated resale value of an asset at the end of its useful life", - "fieldname": "salvage_value", - "fieldtype": "Data", + "description": "", + "fieldname": "expected_value_after_useful_life", + "fieldtype": "Currency", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, - "label": "Salvage Value", + "label": "Expected Value After Useful Life", "length": 0, "no_copy": 0, + "options": "Company:company:default_currency", "permlevel": 0, "precision": "", "print_hide": 0, @@ -369,7 +448,33 @@ { "allow_on_submit": 0, "bold": 0, - "collapsible": 1, + "collapsible": 0, + "fieldname": "current_value", + "fieldtype": "Currency", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Current Value (After Depreciation)", + "length": 0, + "no_copy": 0, + "options": "Company:company:default_currency", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, "fieldname": "section_break_14", "fieldtype": "Section Break", "hidden": 0, @@ -404,13 +509,38 @@ "in_list_view": 0, "label": "Depreciation Schedules", "length": 0, - "no_copy": 0, + "no_copy": 1, "options": "Depreciation Schedule", "permlevel": 0, "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, - "read_only": 0, + "read_only": 1, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "amended_from", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Amended From", + "length": 0, + "no_copy": 1, + "options": "Asset", + "permlevel": 0, + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 1, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -423,11 +553,11 @@ "idx": 0, "in_create": 0, "in_dialog": 0, - "is_submittable": 0, + "is_submittable": 1, "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2016-03-03 17:34:00.991228", + "modified": "2016-03-09 12:22:05.223886", "modified_by": "Administrator", "module": "Accounts", "name": "Asset", @@ -437,7 +567,7 @@ { "amend": 0, "apply_user_permissions": 0, - "cancel": 0, + "cancel": 1, "create": 1, "delete": 1, "email": 1, @@ -451,7 +581,7 @@ "role": "Accounts User", "set_user_permissions": 0, "share": 1, - "submit": 0, + "submit": 1, "write": 1 } ], diff --git a/erpnext/accounts/doctype/asset/asset.py b/erpnext/accounts/doctype/asset/asset.py index 0479d0aeaf..fb1502e881 100644 --- a/erpnext/accounts/doctype/asset/asset.py +++ b/erpnext/accounts/doctype/asset/asset.py @@ -5,61 +5,90 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import flt, add_years +from frappe.utils import flt, add_months, cint from frappe.model.document import Document class Asset(Document): def validate(self): - self.validate_mandatory() + self.validate_fixed_asset_item() + self.validate_asset_values() + self.set_depreciation_settings() self.make_depreciation_schedule() - - def validate_mandatory(self): - if not self.useful_life: - self.useful_life = frappe.db.get_value("Asset Category", self.asset_category, "useful_life") - if not self.useful_life: - frappe.throw(_("Useful Life is mandatory")) - + + def on_cancel(self): + self.validate_cancellation() + self.delete_depreciation_entries() + + def validate_fixed_asset_item(self): + item = frappe.get_doc("Item", self.item_code) + if item.disabled: + frappe.throw(_("Item {0} has been disabled").format(self.item_code)) + if item.is_stock_item: + frappe.throw(_("Item {0} must be a non-stock item").format(self.item_code)) + + def validate_asset_values(self): + if flt(self.expected_value_after_useful_life) >= flt(self.gross_purchase_amount): + frappe.throw(_("Expected Value After Useful Life must be less than Gross Purchase Amount")) + + if not self.current_value: + self.current_value = flt(self.gross_purchase_amount) + else: + if flt(self.current_value) > flt(self.gross_purchase_amount): + frappe.throw(_("Current Value After Depreciation must be less than equal to {0}") + .format(flt(self.gross_purchase_amount))) + + def set_depreciation_settings(self): + asset_category = frappe.get_doc("Asset Category", self.asset_category) + + for field in ("depreciation_method", "number_of_depreciations", "number_of_months_in_a_period"): + if not self.get(field): + self.set(field, asset_category.get(field)) + def make_depreciation_schedule(self): self.schedules = [] if not self.get("schedules") and self.status == "Available": - depreciation_method = self.get_depreciation_method() - accumulated_depreciation = 0 - value_after_depreciation = flt(self.gross_value) - for n in xrange(self.useful_life): - depreciation_date = add_years(self.purchase_date, - n if self.start_depreciation_from_purchase_date else n+1) - depreciation_amount = self.get_depreciation_amount(value_after_depreciation, - depreciation_method) + value_after_depreciation = flt(self.current_value) + for n in xrange(self.number_of_depreciations): + schedule_date = add_months(self.next_depreciation_date, + n * cint(self.number_of_months_in_a_period)) + + depreciation_amount = self.get_depreciation_amount(value_after_depreciation) self.append("schedules", { - "depreciation_date": depreciation_date, + "schedule_date": schedule_date, "depreciation_amount": depreciation_amount, "accumulated_depreciation_amount": accumulated_depreciation + depreciation_amount }) - - def get_depreciation_method(self): - depreciation_method = self.depreciation_method or \ - frappe.db.get_value("Asset Category", self.asset_category, "depreciation_method") or \ - frappe.db.get_value("Company", self.company, "depreciation_method") - - if not depreciation_method: - frappe.throw(_("Please set Depreciation Method in Asset Category {0} or Company {1}") - .format(self.asset_category, self.company)) - - def get_depreciation_amount(self, depreciable_value, depreciation_method=None): - if not depreciation_method: - depreciation_method = self.get_depreciation_method() - - if depreciation_method == "Straight Line": - depreciation_amount = (flt(self.gross_value) - flt(self.salvage_value)) / flt(self.useful_life) + accumulated_depreciation += flt(depreciation_amount) + value_after_depreciation -= flt(depreciation_amount) + + def get_depreciation_amount(self, depreciable_value): + if self.depreciation_method == "Straight Line": + depreciation_amount = (flt(self.current_value) - + flt(self.expected_value_after_useful_life)) / cint(self.number_of_depreciations) else: - factor = 200 / self.useful_life + factor = 200 / cint(self.number_of_depreciations) depreciation_amount = depreciable_value * factor / 100 - + value_after_depreciation = flt(depreciable_value) - depreciation_amount - if value_after_depreciation < self.salvage_value: - depreciation_amount = flt(depreciable_value) - flt(self.salvage_value) - + if value_after_depreciation < flt(self.expected_value_after_useful_life): + depreciation_amount = flt(depreciable_value) - flt(self.expected_value_after_useful_life) + return depreciation_amount + + def validate_cancellation(self): + if self.status != "Available": + frappe.throw(_("Asset {0} cannot be cancelled, as it is already {1}") + .format(self.name, self.status)) + + 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)) \ No newline at end of file diff --git a/erpnext/accounts/doctype/asset/depreciation.py b/erpnext/accounts/doctype/asset/depreciation.py index 48a964a6eb..bd2c74f297 100644 --- a/erpnext/accounts/doctype/asset/depreciation.py +++ b/erpnext/accounts/doctype/asset/depreciation.py @@ -5,113 +5,140 @@ from __future__ import unicode_literals import frappe from frappe import _ -from frappe.utils import flt, today +from frappe.utils import flt, today, getdate - -# Depreciate -#------------ - -def post_depreciation_entries(self): - assets = get_depreciable_assets() - for asset in assets: - depreciate_asset(asset.asset_name, asset.depreciation_amount, asset.schedule_row) - -def get_depreciable_assets(date=None): +def post_depreciation_entries(date=None): if not date: date = today() - - return frappe.db.sql(""" - select a.name as asset_name, ds.depreciation_amount, ds.name as schedule_row + for asset in get_depreciable_assets(date): + make_depreciation_entry(asset, date) + +def get_depreciable_assets(date): + return frappe.db.sql_list("""select a.name from tabAsset a, `tabDepreciation Schedule` ds - where - a.name = ds.parent and ds.depreciation_date=%s - and a.status = 'Available' and ds.posted=0 and a.docstatus < 2""", date, as_dict=1) - -def depreciate_asset(asset_name, depreciation_amount, schedule_row): - asset = frappe.get_doc("Asset", asset_name) - accumulated_depreciation_account, depreciation_expense_account = get_depreciation_accounts(asset) - - je = frappe.new_doc("Journal Entry") - je.voucher_type = "Depreciation Entry" - je.posting_date = today() - je.company = asset.company - je.remark = "Depreciation Entry against {0} worth {1}".format(asset_name, depreciation_amount) - - je.append("accounts", { - "account": accumulated_depreciation_account, - "credit_in_account_currency": depreciation_amount, - "reference_type": "Asset", - "reference_name": asset.name - }) - - je.append("accounts", { - "account": depreciation_expense_account, - "debit_in_account_currency": depreciation_amount, - "reference_type": "Asset", - "reference_name": asset.name - }) - - je.flags.ignore_permissions = True - je.submit() - - frappe.db.sql("""update `tabDepreciation Schedule` - set posted=1, journal_entry=%s, modified=now(), modified_by=%s where name=%s""", - (je.name, frappe.session.user, schedule_row)) + where a.name = ds.parent and a.docstatus=1 and ds.schedule_date<=%s + and a.status = 'Available' and ifnull(ds.journal_entry, '')=''""", date) +def make_depreciation_entry(asset_name, date=None): + if not date: + date = today() + + asset = frappe.get_doc("Asset", asset_name) + fixed_asset_account, accumulated_depreciation_account, depreciation_expense_account = \ + get_depreciation_accounts(asset) + + for d in asset.get("schedules"): + if not d.journal_entry and getdate(d.schedule_date) <= getdate(date): + je = frappe.new_doc("Journal Entry") + je.voucher_type = "Depreciation Entry" + je.posting_date = d.schedule_date + je.company = asset.company + je.remark = "Depreciation Entry against {0} worth {1}".format(asset_name, d.depreciation_amount) + + je.append("accounts", { + "account": accumulated_depreciation_account, + "credit_in_account_currency": d.depreciation_amount, + "reference_type": "Asset", + "reference_name": asset.name + }) + + je.append("accounts", { + "account": depreciation_expense_account, + "debit_in_account_currency": d.depreciation_amount, + "reference_type": "Asset", + "reference_name": asset.name + }) + + je.flags.ignore_permissions = True + je.submit() + + d.db_set("journal_entry", je.name) + asset.current_value -= d.depreciation_amount + frappe.db.set_value("Asset", asset_name, "current_value", asset.current_value) + def get_depreciation_accounts(asset): - accumulated_depreciation_account, depreciation_expense_account = frappe.db.get_value("Asset Category", - asset.asset_category, ["accumulated_depreciation_account", "depreciation_expense_account"]) - + accounts = frappe.db.sql("""select fixed_asset_account, accumulated_depreciation_account, + depreciation_expense_account from `tabAsset Category Account` + where parent=%s and company=%s""", (asset.asset_category, asset.company), as_dict=1)[0] + + fixed_asset_account = accounts.fixed_asset_account + accumulated_depreciation_account = accounts.accumulated_depreciation_account + depreciation_expense_account = accounts.depreciation_expense_account + if not accumulated_depreciation_account or not depreciation_expense_account: accounts = frappe.db.get_value("Company", asset.company, ["accumulated_depreciation_account", "depreciation_expense_account"]) + if not accumulated_depreciation_account: accumulated_depreciation_account = accounts[0] if not depreciation_expense_account: depreciation_expense_account = accounts[1] - if not accumulated_depreciation_account or not depreciation_expense_account: + if not fixed_asset_account or not accumulated_depreciation_account or not depreciation_expense_account: frappe.throw(_("Please set Depreciation related Accounts in Asset Category {0} or Company {1}") .format(asset.asset_category, asset.company)) - return accumulated_depreciation_account, depreciation_expense_account + return fixed_asset_account, accumulated_depreciation_account, depreciation_expense_account -# Scrap -#--------- - @frappe.whitelist() def scrap_asset(asset_name): asset = frappe.get_doc("Asset", asset_name) - asset.status = "Scrapped" - - accumulated_depr_amount = frappe.db.sql("""select accumulated_depreciation_amount - from `tabDepreciation Schedule` - where parent=%s and posted=1 - order by depreciation_date desc limit 1""") - - accumulated_depr_amount = flt(accumulated_depr_amount[0][0]) if accumulated_depr_amount else 0 - - net_value_after_depreciation = flt(asset.gross_value) - accumulated_depr_amount + if asset.docstatus != 1 or asset.status != 'Available': + frappe.throw(_("Asset {0} must be submitted and available").format(asset.name)) + je = frappe.new_doc("Journal Entry") - je.voucher_type = "Depreciation Entry" + je.voucher_type = "Journal Entry" je.posting_date = today() je.company = asset.company - je.remark = "Disposal Entry for asset {0}".format(asset_name) + je.remark = "Scrap Entry for asset {0}".format(asset_name) - # je.append("accounts", { - # "account": accumulated_depreciation_account, - # "credit_in_account_currency": depreciation_amount, - # "reference_type": "Asset", - # "reference_name": asset.name - # }) - # - # je.append("accounts", { - # "account": depreciation_expense_account, - # "debit_in_account_currency": depreciation_amount, - # "reference_type": "Asset", - # "reference_name": asset.name - # }) + for entry in get_gl_entries_on_asset_disposal(asset): + entry.update({ + "reference_type": "Asset", + "reference_name": asset_name + }) + je.append("accounts", entry) je.flags.ignore_permissions = True - je.submit() \ No newline at end of file + je.submit() + + frappe.db.set_value("Asset", asset_name, "status", "Scrapped") + +@frappe.whitelist() +def get_gl_entries_on_asset_disposal(asset, selling_amount=0): + fixed_asset_account, accumulated_depr_account, depr_expense_account = get_depreciation_accounts(asset) + disposal_account, disposal_cost_center = get_disposal_account_and_cost_center(asset.company) + accumulated_depr_amount = flt(asset.gross_purchase_amount) - flt(asset.current_value) + + gl_entries = [ + { + "account": fixed_asset_account, + "credit_in_account_currency": asset.gross_purchase_amount, + "credit": asset.gross_purchase_amount + }, + { + "account": accumulated_depr_account, + "debit_in_account_currency": accumulated_depr_amount, + "debit": accumulated_depr_amount + } + ] + + profit_amount = flt(selling_amount) - flt(asset.current_value) + if flt(asset.current_value) and profit_amount: + debit_or_credit = "debit" if profit_amount < 0 else "credit" + gl_entries.append({ + "account": disposal_account, + "cost_center": disposal_cost_center, + debit_or_credit: abs(profit_amount), + debit_or_credit + "_in_account_currency": abs(profit_amount) + }) + + return gl_entries + +def get_disposal_account_and_cost_center(company): + disposal_account, disposal_cost_center = frappe.db.get_value("Company", company, + ["disposal_account", "disposal_cost_center"]) + if not disposal_account or not disposal_cost_center: + frappe.throw(_("Please set 'Asset Disposal Account' and 'Asset Disposal Cost Center' in Company {0}").format(company)) + return disposal_account, disposal_cost_center \ No newline at end of file diff --git a/erpnext/accounts/doctype/asset_category/asset_category.js b/erpnext/accounts/doctype/asset_category/asset_category.js index 84f0dfc76c..862a202091 100644 --- a/erpnext/accounts/doctype/asset_category/asset_category.js +++ b/erpnext/accounts/doctype/asset_category/asset_category.js @@ -1,8 +1,36 @@ // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Asset Category', { - refresh: function(frm) { +cur_frm.fields_dict['accounts'].grid.get_field('fixed_asset_account').get_query = function(doc, cdt, cdn) { + var d = locals[cdt][cdn]; + return { + "filters": { + "account_type": "Fixed Asset", + "root_type": "Asset", + "is_group": 0, + "company": d.company + } + }; +} - } -}); +cur_frm.fields_dict['accounts'].grid.get_field('accumulated_depreciation_account').get_query = function(doc, cdt, cdn) { + var d = locals[cdt][cdn]; + return { + "filters": { + "root_type": "Asset", + "is_group": 0, + "company": d.company + } + }; +} + +cur_frm.fields_dict['accounts'].grid.get_field('depreciation_expense_account').get_query = function(doc, cdt, cdn) { + var d = locals[cdt][cdn]; + return { + "filters": { + "root_type": "Expense", + "is_group": 0, + "company": d.company + } + }; +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/asset_category/asset_category.json b/erpnext/accounts/doctype/asset_category/asset_category.json index 9338e22e16..9d8bd00490 100644 --- a/erpnext/accounts/doctype/asset_category/asset_category.json +++ b/erpnext/accounts/doctype/asset_category/asset_category.json @@ -34,30 +34,6 @@ "set_only_once": 0, "unique": 0 }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "section_break_2", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "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, @@ -80,7 +56,7 @@ "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, - "reqd": 0, + "reqd": 1, "search_index": 0, "set_only_once": 0, "unique": 0 @@ -89,32 +65,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "fieldname": "useful_life", - "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Useful Life (Years)", - "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": "column_break_5", + "fieldname": "column_break_3", "fieldtype": "Column Break", "hidden": 0, "ignore_user_permissions": 0, @@ -138,17 +89,16 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "fieldname": "fixed_asset_account", - "fieldtype": "Link", + "fieldname": "number_of_depreciations", + "fieldtype": "Int", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, - "label": "Fixed Asset Account", + "label": "Number of Depreciations", "length": 0, "no_copy": 0, - "options": "Account", "permlevel": 0, "precision": "", "print_hide": 0, @@ -164,17 +114,16 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "fieldname": "accumulated_depreciation_account", - "fieldtype": "Link", + "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": "Accumulated Depreciation Account", + "label": "Number of Months in a Period", "length": 0, "no_copy": 0, - "options": "Account", "permlevel": 0, "precision": "", "print_hide": 0, @@ -190,24 +139,49 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "fieldname": "depreciation_expense_account", - "fieldtype": "Link", + "fieldname": "section_break_2", + "fieldtype": "Section Break", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, - "label": "Depreciation Expense Account", + "label": "Accounts", "length": 0, "no_copy": 0, - "options": "Account", "permlevel": 0, "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, - "reqd": 1, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "accounts", + "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Accounts", + "length": 0, + "no_copy": 0, + "options": "Asset Category Account", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, "search_index": 0, "set_only_once": 0, "unique": 0 @@ -222,7 +196,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2016-03-03 15:17:44.877003", + "modified": "2016-03-07 19:01:19.265210", "modified_by": "Administrator", "module": "Accounts", "name": "Asset Category", diff --git a/erpnext/accounts/doctype/asset_category/asset_category.py b/erpnext/accounts/doctype/asset_category/asset_category.py index 8f922d1d95..f139499256 100644 --- a/erpnext/accounts/doctype/asset_category/asset_category.py +++ b/erpnext/accounts/doctype/asset_category/asset_category.py @@ -4,7 +4,12 @@ from __future__ import unicode_literals import frappe +from frappe import _ from frappe.model.document import Document class AssetCategory(Document): - pass + def validate(self): + for field in ("depreciation_method", "number_of_depreciations", + "number_of_months_in_a_period", "accounts"): + if not self.get(field): + frappe.throw(_("{0} is mandatory").format(self.meta.get_label(field))) \ No newline at end of file diff --git a/erpnext/accounts/doctype/asset_category_account/__init__.py b/erpnext/accounts/doctype/asset_category_account/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/accounts/doctype/asset_category_account/asset_category_account.json b/erpnext/accounts/doctype/asset_category_account/asset_category_account.json new file mode 100644 index 0000000000..5144e8575b --- /dev/null +++ b/erpnext/accounts/doctype/asset_category_account/asset_category_account.json @@ -0,0 +1,136 @@ +{ + "allow_copy": 0, + "allow_import": 0, + "allow_rename": 0, + "creation": "2016-03-07 15:55:18.806409", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "fields": [ + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "company", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Company", + "length": 0, + "no_copy": 0, + "options": "Company", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "fixed_asset_account", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Fixed Asset Account", + "length": 0, + "no_copy": 0, + "options": "Account", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "accumulated_depreciation_account", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Accumulated Depreciation Account", + "length": 0, + "no_copy": 0, + "options": "Account", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "depreciation_expense_account", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Depreciation Expense Account", + "length": 0, + "no_copy": 0, + "options": "Account", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "in_create": 0, + "in_dialog": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2016-03-07 19:02:09.879979", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Asset Category Account", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "read_only": 0, + "read_only_onload": 0, + "sort_field": "modified", + "sort_order": "DESC" +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/asset_category_account/asset_category_account.py b/erpnext/accounts/doctype/asset_category_account/asset_category_account.py new file mode 100644 index 0000000000..67925f4fe8 --- /dev/null +++ b/erpnext/accounts/doctype/asset_category_account/asset_category_account.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +from frappe.model.document import Document + +class AssetCategoryAccount(Document): + pass diff --git a/erpnext/accounts/doctype/depreciation_schedule/depreciation_schedule.json b/erpnext/accounts/doctype/depreciation_schedule/depreciation_schedule.json index 89685ccf2a..50fba8878d 100644 --- a/erpnext/accounts/doctype/depreciation_schedule/depreciation_schedule.json +++ b/erpnext/accounts/doctype/depreciation_schedule/depreciation_schedule.json @@ -13,21 +13,21 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "fieldname": "depreciation_date", + "fieldname": "schedule_date", "fieldtype": "Date", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 1, - "label": "Depreciation Date", + "label": "Schedule Date", "length": 0, - "no_copy": 0, + "no_copy": 1, "permlevel": 0, "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, - "read_only": 0, + "read_only": 1, "report_hide": 0, "reqd": 1, "search_index": 0, @@ -47,6 +47,31 @@ "in_list_view": 1, "label": "Depreciation Amount", "length": 0, + "no_copy": 1, + "options": "Company:company:default_currency", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 1, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "column_break_3", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "length": 0, "no_copy": 0, "permlevel": 0, "precision": "", @@ -54,7 +79,7 @@ "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, - "reqd": 1, + "reqd": 0, "search_index": 0, "set_only_once": 0, "unique": 0 @@ -72,43 +97,19 @@ "in_list_view": 1, "label": "Accumulated Depreciation Amount", "length": 0, - "no_copy": 0, + "no_copy": 1, + "options": "Company:company:default_currency", "permlevel": 0, "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, - "read_only": 0, + "read_only": 1, "report_hide": 0, "reqd": 1, "search_index": 0, "set_only_once": 0, "unique": 0 }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "fieldname": "posted", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Posted", - "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, @@ -119,16 +120,16 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, - "in_list_view": 0, + "in_list_view": 1, "label": "Journal Entry", "length": 0, - "no_copy": 0, + "no_copy": 1, "options": "Journal Entry", "permlevel": 0, "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, - "read_only": 0, + "read_only": 1, "report_hide": 0, "reqd": 0, "search_index": 0, @@ -145,7 +146,7 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2016-03-03 14:50:39.794061", + "modified": "2016-03-09 12:21:27.938215", "modified_by": "Administrator", "module": "Accounts", "name": "Depreciation Schedule", diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json index 9277da5905..28b57bdd9c 100644 --- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json +++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json @@ -16,6 +16,7 @@ "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 1, "in_list_view": 1, "label": "Account", @@ -44,6 +45,7 @@ "fieldtype": "Data", "hidden": 1, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "label": "Account Type", @@ -68,6 +70,7 @@ "fieldtype": "Currency", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "label": "Account Balance", @@ -96,6 +99,7 @@ "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 1, "in_list_view": 0, "label": "Cost Center", @@ -124,6 +128,7 @@ "fieldtype": "Column Break", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "length": 0, @@ -146,6 +151,7 @@ "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "label": "Party Type", @@ -170,6 +176,7 @@ "fieldtype": "Dynamic Link", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 1, "label": "Party", @@ -194,6 +201,7 @@ "fieldtype": "Currency", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "label": "Party Balance", @@ -221,6 +229,7 @@ "fieldtype": "Section Break", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "label": "Currency", @@ -245,6 +254,7 @@ "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "label": "Account Currency", @@ -270,6 +280,7 @@ "fieldtype": "Column Break", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "length": 0, @@ -293,6 +304,7 @@ "fieldtype": "Float", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "label": "Exchange Rate", @@ -317,6 +329,7 @@ "fieldtype": "Section Break", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "label": "Amount", @@ -340,6 +353,7 @@ "fieldtype": "Currency", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 1, "label": "Debit", @@ -365,6 +379,7 @@ "fieldtype": "Currency", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "label": "Debit in Company Currency", @@ -392,6 +407,7 @@ "fieldtype": "Column Break", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "length": 0, @@ -414,6 +430,7 @@ "fieldtype": "Currency", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 1, "label": "Credit", @@ -439,6 +456,7 @@ "fieldtype": "Currency", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "label": "Credit in Company Currency", @@ -466,6 +484,7 @@ "fieldtype": "Section Break", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "label": "Reference", @@ -489,12 +508,13 @@ "fieldtype": "Select", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "label": "Reference Type", "length": 0, "no_copy": 0, - "options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim", + "options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset", "permlevel": 0, "precision": "", "print_hide": 0, @@ -514,6 +534,7 @@ "fieldtype": "Dynamic Link", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 1, "label": "Reference Name", @@ -539,6 +560,7 @@ "fieldtype": "Column Break", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "length": 0, @@ -561,6 +583,7 @@ "fieldtype": "Select", "hidden": 0, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "label": "Is Advance", @@ -587,6 +610,7 @@ "fieldtype": "Text", "hidden": 1, "ignore_user_permissions": 0, + "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, "label": "Against Account", @@ -614,12 +638,13 @@ "issingle": 0, "istable": 1, "max_attachments": 0, - "modified": "2015-12-02 04:14:37.571883", + "modified": "2016-03-07 19:10:27.135795", "modified_by": "Administrator", "module": "Accounts", "name": "Journal Entry Account", "owner": "Administrator", "permissions": [], "read_only": 0, - "read_only_onload": 0 + "read_only_onload": 0, + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 8ab76512f6..0143a4f220 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -210,6 +210,18 @@ cur_frm.set_query("expense_account", "items", function(doc) { } }); +cur_frm.set_query("asset", "items", function(doc, cdt, cdn) { + var d = locals[cdt][cdn]; + return { + filters: { + 'item_code': d.item_code, + 'docstatus': 1, + 'company': doc.company, + 'status': 'Available' + } + } +}); + cur_frm.cscript.expense_account = function(doc, cdt, cdn){ var d = locals[cdt][cdn]; if(d.idx == 1 && d.expense_account){ diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 5edfcb9758..06b2ecec47 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -253,19 +253,28 @@ class PurchaseInvoice(BuyingController): self.update_project() def post_asset_depreciation(self): - from erpnext.accounts.doctype.asset.asset import depreciate_asset for d in self.get("items"): if frappe.db.get_value("Item", d.item_code, "is_fixed_asset"): if not d.asset: frappe.throw(_("Row #{0}: Asset is mandatory against a Fixed Asset Item").format(d.idx)) else: asset = frappe.get_doc("Asset", d.asset) - if asset.start_depreciation_from_purchase_date: - for schedule in asset.get("schedules"): - if schedule.depreciation_date == asset.purchase_date: - depreciate_asset(asset.name, schedule.depreciation_amount, schedule.name) - break - + self.validate_asset(asset, d) + + frappe.db.set_value("Asset", asset.name, "purchase_invoice", + (self.name if self.docstatus==1 else None)) + + def validate_asset(self, asset, item_row): + super(PurchaseInvoice, self).validate_asset(asset, item_row) + + if getdate(asset.purchase_date) != getdate(self.posting_date): + frappe.throw(_("Purchase Date of asset {0} does not match with Purchase Invoice date") + .format(item_row.asset)) + + if asset.supplier != self.supplier: + frappe.throw(_("Supplier of asset {0} does not match with the supplier in the Purchase Invoice") + .format(item_row.asset)) + def make_gl_entries(self): auto_accounting_for_stock = \ cint(frappe.defaults.get_global_default("auto_accounting_for_stock")) @@ -432,6 +441,7 @@ class PurchaseInvoice(BuyingController): self.make_gl_entries_on_cancel() self.update_project() + self.post_asset_depreciation() def update_project(self): project_list = [] diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 89af72190d..265e6979f2 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -471,3 +471,15 @@ cur_frm.set_query("debit_to", function(doc) { } } }); + +cur_frm.set_query("asset", "items", function(doc, cdt, cdn) { + var d = locals[cdt][cdn]; + return { + filters: { + 'item_code': d.item_code, + 'docstatus': 1, + 'company': doc.company, + 'status': 'Available' + } + } +}); \ No newline at end of file diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 7b9701310b..97580671d1 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -13,6 +13,7 @@ from frappe.model.mapper import get_mapped_doc from erpnext.controllers.selling_controller import SellingController from erpnext.accounts.utils import get_account_currency from erpnext.stock.doctype.delivery_note.delivery_note import update_billed_amount_based_on_so +from erpnext.accounts.doctype.asset.depreciation import get_gl_entries_on_asset_disposal form_grid_templates = { "items": "templates/form_grid/item_grid.html" @@ -59,6 +60,7 @@ class SalesInvoice(SellingController): self.validate_advance_jv("Sales Order") self.add_remarks() self.validate_write_off_account() + self.set_income_account_for_fixed_assets() if cint(self.is_pos): self.validate_pos() @@ -447,6 +449,17 @@ class SalesInvoice(SellingController): msgprint(_("POS Profile required to make POS Entry"), raise_exception=True) return warehouse + + def set_income_account_for_fixed_assets(self): + disposal_account = None + for d in self.get("items"): + if frappe.db.get_value("Item", d.item_code, "is_fixed_asset"): + if not disposal_account: + disposal_account = frappe.db.get_value("Company", self.company, "disposal_account") + if not disposal_account: + frappe.throw(_("Please mention 'Gain/Loss Account on Asset Disposal' in Company")) + + d.income_account = disposal_account def on_update(self): if cint(self.is_pos) == 1: @@ -557,17 +570,31 @@ class SalesInvoice(SellingController): # income account gl entries for item in self.get("items"): if flt(item.base_net_amount): - account_currency = get_account_currency(item.income_account) - gl_entries.append( - self.get_gl_dict({ - "account": item.income_account, - "against": self.customer, - "credit": item.base_net_amount, - "credit_in_account_currency": item.base_net_amount \ - if account_currency==self.company_currency else item.net_amount, - "cost_center": item.cost_center - }, account_currency) - ) + if frappe.db.get_value("Item", item.item_code, "is_fixed_asset"): + if not item.asset: + frappe.throw(_("Row #{0}: Asset is mandatory against a Fixed Asset Item") + .format(item.idx)) + else: + asset = frappe.get_doc("Asset", item.asset) + self.validate_asset(asset, item) + + fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(asset, item.base_net_amount) + for gle in fixed_asset_gl_entries: + gl_entries.append(self.get_gl_dict(gle)) + + frappe.db.set_value("Asset", asset.name, "status", "Sold") + else: + account_currency = get_account_currency(item.income_account) + gl_entries.append( + self.get_gl_dict({ + "account": item.income_account, + "against": self.customer, + "credit": item.base_net_amount, + "credit_in_account_currency": item.base_net_amount \ + if account_currency==self.company_currency else item.net_amount, + "cost_center": item.cost_center + }, account_currency) + ) # expense account gl entries if cint(frappe.defaults.get_global_default("auto_accounting_for_stock")) \ diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index e14f0da9e0..3ab22c5443 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -458,7 +458,20 @@ class AccountsController(TransactionBase): # Note: not validating with gle account because we don't have the account # at quotation / sales order level and we shouldn't stop someone # from creating a sales invoice if sales order is already created - + + def validate_asset(self, asset, item_row): + if asset.company != self.company: + frappe.throw(_("Row #{0}: Asset {1} does not belong to company {2}") + .format(item_row.idx, item_row.asset, self.company)) + + elif asset.item_code != item_row.item_code: + frappe.throw(_("Row #{0}: Asset {1} does not linked to Item {2}") + .format(item_row.idx, item_row.asset, item_row.item_code)) + elif asset.docstatus != 1: + frappe.throw(_("Row #{0}: Asset {1} must be submitted").format(item_row.idx, item_row.asset)) + elif item_row.qty > 1: + frappe.throw(_("Row #{0}: Qty must be 1, as item is linked to an asset").format(item_row.idx)) + @frappe.whitelist() def get_tax_rate(account_head): return frappe.db.get_value("Account", account_head, "tax_rate") diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 0ccc82a0db..56c0d16ad2 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -137,7 +137,8 @@ scheduler_events = { "erpnext.support.doctype.issue.issue.auto_close_tickets", "erpnext.accounts.doctype.fiscal_year.fiscal_year.auto_create_fiscal_year", "erpnext.hr.doctype.employee.employee.send_birthday_reminders", - "erpnext.projects.doctype.task.task.set_tasks_as_overdue" + "erpnext.projects.doctype.task.task.set_tasks_as_overdue", + "erpnext.accounts.doctype.asset.depreciation.post_depreciation_entries" ] } diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 01a2d1d507..777fe1e121 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -136,8 +136,12 @@ erpnext.company.setup_queries = function(frm) { ["default_expense_account", {"root_type": "Expense"}], ["default_income_account", {"root_type": "Income"}], ["round_off_account", {"root_type": "Expense"}], + ["accumulated_depreciation_account", {"root_type": "Asset"}], + ["depreciation_expense_account", {"root_type": "Expense"}], + ["disposal_account", {"report_type": "Profit and Loss"}], ["cost_center", {}], - ["round_off_cost_center", {}] + ["round_off_cost_center", {}], + ["disposal_cost_center", {}] ], function(i, v) { erpnext.company.set_custom_query(frm, v); }); diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 3de72fdc3a..95b17c4e93 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -253,12 +253,11 @@ "no_copy": 0, "options": "Terms and Conditions", "permlevel": 0, - "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, - "reqd": 0, + "reqd": 1, "search_index": 0, "set_only_once": 0, "unique": 0 @@ -990,18 +989,17 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "default": "Straight Line", - "fieldname": "depreciation_method", - "fieldtype": "Select", + "fieldname": "accumulated_depreciation_account", + "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, - "label": "Depreciation Method", + "label": "Accumulated Depreciation Account", "length": 0, - "no_copy": 0, - "options": "Straight Line\nDouble Declining Balance", + "no_copy": 1, + "options": "Account", "permlevel": 0, "precision": "", "print_hide": 0, @@ -1017,16 +1015,16 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "fieldname": "accumulated_depreciation_account", + "fieldname": "depreciation_expense_account", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, - "label": "Accumulated Depreciation Account", + "label": "Depreciation Expense Account", "length": 0, - "no_copy": 0, + "no_copy": 1, "options": "Account", "permlevel": 0, "precision": "", @@ -1067,16 +1065,16 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "fieldname": "depreciation_expense_account", + "fieldname": "disposal_account", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, - "label": "Depreciation Expense Account", + "label": "Gain/Loss Account on Asset Disposal", "length": 0, - "no_copy": 0, + "no_copy": 1, "options": "Account", "permlevel": 0, "precision": "", @@ -1093,17 +1091,17 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "fieldname": "gain_loss_account", + "fieldname": "disposal_cost_center", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, - "label": "Gain/Loss Account on Asset Sale/Disposal", + "label": "Asset Disposal Cost Center", "length": 0, - "no_copy": 0, - "options": "Account", + "no_copy": 1, + "options": "Cost Center", "permlevel": 0, "precision": "", "print_hide": 0, @@ -1389,7 +1387,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2016-03-08 20:21:46.331870", + "modified": "2016-03-09 12:06:12.189968", "modified_by": "Administrator", "module": "Setup", "name": "Company", diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index e628d0dec5..99afe33eeb 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -251,6 +251,36 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "default": "", + "depends_on": "", + "description": "", + "fieldname": "is_fixed_asset", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Is Fixed Asset", + "length": 0, + "no_copy": 0, + "oldfieldname": "is_asset_item", + "oldfieldtype": "Select", + "options": "", + "permlevel": 0, + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_on_submit": 0, "bold": 0, @@ -600,36 +630,6 @@ "set_only_once": 0, "unique": 0 }, - { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "default": "", - "depends_on": "eval:doc.is_stock_item", - "description": "", - "fieldname": "is_fixed_asset", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "label": "Is Fixed Asset", - "length": 0, - "no_copy": 0, - "oldfieldname": "is_asset_item", - "oldfieldtype": "Select", - "options": "", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, { "allow_on_submit": 0, "bold": 0, diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 39ef7e2c7b..cc8886b50f 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -80,10 +80,12 @@ class Item(WebsiteGenerator): self.validate_variant_attributes() self.validate_website_image() self.make_thumbnail() + self.validate_fixed_asset_item() if not self.get("__islocal"): self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group") - self.old_website_item_groups = frappe.db.sql_list("""select item_group from `tabWebsite Item Group` + self.old_website_item_groups = frappe.db.sql_list("""select item_group + from `tabWebsite Item Group` where parentfield='website_item_groups' and parenttype='Item' and parent=%s""", self.name) def on_update(self): @@ -566,6 +568,10 @@ class Item(WebsiteGenerator): if variant: frappe.throw(_("Item variant {0} exists with same attributes") .format(variant), ItemVariantExistsError) + + def validate_fixed_asset_item(self): + if self.is_fixed_asset and self.is_stock_item: + frappe.throw(_("Fixed Asset Item must be a non-stock item")) def validate_end_of_life(item_code, end_of_life=None, disabled=None, verbose=1): if (not end_of_life) or (disabled is None):