From 7536349f17e6cdd7771225500b32262c7ac33ac9 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 11 Mar 2016 15:42:23 +0530 Subject: [PATCH] Fixed asset: status, restore, testcase and much more --- erpnext/accounts/doctype/asset/asset.js | 28 +++- erpnext/accounts/doctype/asset/asset.json | 36 ++++- erpnext/accounts/doctype/asset/asset.py | 46 +++++- .../accounts/doctype/asset/depreciation.py | 53 +++++-- erpnext/accounts/doctype/asset/test_asset.py | 142 +++++++++++++++++- .../asset_category/test_asset_category.py | 2 +- erpnext/accounts/doctype/gl_entry/gl_entry.py | 4 +- .../purchase_invoice/purchase_invoice.js | 2 +- .../purchase_invoice/purchase_invoice.py | 33 ++-- .../doctype/sales_invoice/sales_invoice.js | 12 +- .../doctype/sales_invoice/sales_invoice.py | 9 +- erpnext/setup/doctype/company/company.js | 2 +- erpnext/setup/doctype/company/company.json | 9 +- 13 files changed, 307 insertions(+), 71 deletions(-) diff --git a/erpnext/accounts/doctype/asset/asset.js b/erpnext/accounts/doctype/asset/asset.js index 91d41e5dd43..0b28a36ef99 100644 --- a/erpnext/accounts/doctype/asset/asset.js +++ b/erpnext/accounts/doctype/asset/asset.js @@ -17,10 +17,16 @@ frappe.ui.form.on('Asset', { }, refresh: function(frm) { - if(frm.doc.docstatus==1 && frm.doc.status=='Available') { - cur_frm.add_custom_button("Scrap", function() { - erpnext.asset.scrap_asset(frm); - }); + if (frm.doc.docstatus==1) { + if (in_list(["Submittted", "Partially Depreciated", "Fully Depreciated"], frm.doc.status)) { + cur_frm.add_custom_button("Scrap Asset", function() { + erpnext.asset.scrap_asset(frm); + }); + } else if (frm.doc.status=='Scrapped') { + cur_frm.add_custom_button("Restore Asset", function() { + erpnext.asset.restore_asset(frm); + }); + } } } }); @@ -37,4 +43,18 @@ erpnext.asset.scrap_asset = function(frm) { } }) }) +} + +erpnext.asset.restore_asset = function(frm) { + frappe.confirm(__("Do you really want to restore this scrapped asset?"), function () { + frappe.call({ + args: { + "asset_name": frm.doc.name + }, + method: "erpnext.accounts.doctype.asset.depreciation.restore_asset", + callback: function(r) { + cur_frm.reload_doc(); + } + }) + }) } \ No newline at end of file diff --git a/erpnext/accounts/doctype/asset/asset.json b/erpnext/accounts/doctype/asset/asset.json index 8c29c440fe6..6dfef0f386a 100644 --- a/erpnext/accounts/doctype/asset/asset.json +++ b/erpnext/accounts/doctype/asset/asset.json @@ -90,7 +90,7 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "default": "Available", + "default": "Draft", "fieldname": "status", "fieldtype": "Select", "hidden": 0, @@ -101,7 +101,7 @@ "label": "Status", "length": 0, "no_copy": 1, - "options": "Available\nSold\nScrapped", + "options": "Draft\nSubmitted\nPartially Depreciated\nFully Depreciated\nSold\nScrapped", "permlevel": 0, "precision": "", "print_hide": 0, @@ -240,6 +240,32 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "fieldname": "journal_entry_for_scrap", + "fieldtype": "Link", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Journal Entry for Scrap", + "length": 0, + "no_copy": 1, + "options": "Journal Entry", + "permlevel": 0, + "precision": "", + "print_hide": 1, + "print_hide_if_no_value": 0, + "read_only": 1, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_on_submit": 0, "bold": 0, @@ -356,7 +382,7 @@ "in_list_view": 0, "label": "Next Depreciation Date", "length": 0, - "no_copy": 0, + "no_copy": 1, "permlevel": 0, "precision": "", "print_hide": 0, @@ -458,7 +484,7 @@ "in_list_view": 0, "label": "Current Value (After Depreciation)", "length": 0, - "no_copy": 0, + "no_copy": 1, "options": "Company:company:default_currency", "permlevel": 0, "precision": "", @@ -557,7 +583,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2016-03-09 12:22:05.223886", + "modified": "2016-03-11 12:23:37.114298", "modified_by": "Administrator", "module": "Accounts", "name": "Asset", diff --git a/erpnext/accounts/doctype/asset/asset.py b/erpnext/accounts/doctype/asset/asset.py index 488ba6a53e9..05c3bc3d967 100644 --- a/erpnext/accounts/doctype/asset/asset.py +++ b/erpnext/accounts/doctype/asset/asset.py @@ -10,14 +10,20 @@ from frappe.model.document import Document class Asset(Document): def validate(self): + self.set_status() self.validate_fixed_asset_item() self.validate_asset_values() self.set_depreciation_settings() self.make_depreciation_schedule() + self.validate_depreciation_settings_in_company() + + def on_submit(self): + self.set_status() def on_cancel(self): self.validate_cancellation() self.delete_depreciation_entries() + self.set_status() def validate_fixed_asset_item(self): item = frappe.get_doc("Item", self.item_code) @@ -49,7 +55,7 @@ class Asset(Document): def make_depreciation_schedule(self): self.schedules = [] - if not self.get("schedules") and self.status == "Available": + if not self.get("schedules"): accumulated_depreciation = 0 value_after_depreciation = flt(self.current_value) for n in xrange(self.number_of_depreciations): @@ -71,8 +77,8 @@ class Asset(Document): depreciation_amount = (flt(self.current_value) - flt(self.expected_value_after_useful_life)) / cint(self.number_of_depreciations) else: - factor = 200 / cint(self.number_of_depreciations) - depreciation_amount = depreciable_value * factor / 100 + factor = 200.0 / cint(self.number_of_depreciations) + depreciation_amount = flt(depreciable_value * factor / 100, 0) value_after_depreciation = flt(depreciable_value) - depreciation_amount if value_after_depreciation < flt(self.expected_value_after_useful_life): @@ -81,9 +87,11 @@ class Asset(Document): 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)) + if self.status not in ("Submitted", "Partially Depreciated", "Fully Depreciated"): + frappe.throw(_("Asset cannot be cancelled, as it is already {0}").format(self.status)) + + if self.purchase_invoice: + frappe.throw(_("Please cancel Purchase Invoice {0} first").format(self.purchase_invoice)) def delete_depreciation_entries(self): total_depreciation_amount = 0 @@ -94,4 +102,28 @@ class Asset(Document): 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 + + def validate_depreciation_settings_in_company(self): + company = frappe.get_doc("Company", self.company) + for field in ("accumulated_depreciation_account", "depreciation_expense_account", + "disposal_account", "depreciation_cost_center"): + if not company.get(field): + frappe.throw(_("Please set {0} in Company {1}") + .format(company.meta.get_label(field), self.company)) + + def set_status(self, status=None): + if not status: + if self.docstatus == 0: + status = "Draft" + elif self.docstatus == 1: + status = "Submitted" + if self.journal_entry_for_scrap: + status = "Scrapped" + elif flt(self.current_value) <= flt(self.expected_value_after_useful_life): + status = "Fully Depreciated" + elif flt(self.current_value) < flt(self.gross_purchase_amount): + status = 'Partially Depreciated' + elif self.docstatus == 2: + status = "Cancelled" + + frappe.db.set_value(self.doctype, self.name, "status", status) \ No newline at end of file diff --git a/erpnext/accounts/doctype/asset/depreciation.py b/erpnext/accounts/doctype/asset/depreciation.py index bd2c74f297b..8889c81c459 100644 --- a/erpnext/accounts/doctype/asset/depreciation.py +++ b/erpnext/accounts/doctype/asset/depreciation.py @@ -17,7 +17,8 @@ def get_depreciable_assets(date): return frappe.db.sql_list("""select a.name from tabAsset a, `tabDepreciation Schedule` ds where a.name = ds.parent and a.docstatus=1 and ds.schedule_date<=%s - and a.status = 'Available' and ifnull(ds.journal_entry, '')=''""", date) + and a.status in ('Submitted', 'Partially Depreciated') + and ifnull(ds.journal_entry, '')=''""", date) def make_depreciation_entry(asset_name, date=None): if not date: @@ -26,6 +27,8 @@ def make_depreciation_entry(asset_name, date=None): asset = frappe.get_doc("Asset", asset_name) fixed_asset_account, accumulated_depreciation_account, depreciation_expense_account = \ get_depreciation_accounts(asset) + + depreciation_cost_center = frappe.db.get_value("Company", asset.company, "depreciation_cost_center") for d in asset.get("schedules"): if not d.journal_entry and getdate(d.schedule_date) <= getdate(date): @@ -34,7 +37,7 @@ def make_depreciation_entry(asset_name, date=None): 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, @@ -46,15 +49,18 @@ def make_depreciation_entry(asset_name, date=None): "account": depreciation_expense_account, "debit_in_account_currency": d.depreciation_amount, "reference_type": "Asset", - "reference_name": asset.name + "reference_name": asset.name, + "cost_center": depreciation_cost_center }) - + je.flags.ignore_permissions = True je.submit() d.db_set("journal_entry", je.name) asset.current_value -= d.depreciation_amount - frappe.db.set_value("Asset", asset_name, "current_value", asset.current_value) + + asset.db_set("current_value", asset.current_value) + asset.set_status() def get_depreciation_accounts(asset): accounts = frappe.db.sql("""select fixed_asset_account, accumulated_depreciation_account, @@ -84,8 +90,10 @@ def get_depreciation_accounts(asset): def scrap_asset(asset_name): asset = frappe.get_doc("Asset", asset_name) - if asset.docstatus != 1 or asset.status != 'Available': - frappe.throw(_("Asset {0} must be submitted and available").format(asset.name)) + if asset.docstatus != 1: + frappe.throw(_("Asset {0} must be submitted").format(asset.name)) + elif asset.status in ("Cancelled", "Sold", "Scrapped"): + frappe.throw(_("Asset {0} cannot be scrapped, as it is already {1}").format(asset.name, asset.status)) je = frappe.new_doc("Journal Entry") je.voucher_type = "Journal Entry" @@ -103,12 +111,23 @@ def scrap_asset(asset_name): je.flags.ignore_permissions = True je.submit() - frappe.db.set_value("Asset", asset_name, "status", "Scrapped") + frappe.db.set_value("Asset", asset_name, "journal_entry_for_scrap", je.name) + asset.set_status("Scrapped") +@frappe.whitelist() +def restore_asset(asset_name): + asset = frappe.get_doc("Asset", asset_name) + + je = asset.journal_entry_for_scrap + asset.db_set("journal_entry_for_scrap", None) + frappe.get_doc("Journal Entry", je).cancel() + + asset.set_status() + @frappe.whitelist() def get_gl_entries_on_asset_disposal(asset, selling_amount=0): fixed_asset_account, accumulated_depr_account, depr_expense_account = get_depreciation_accounts(asset) - disposal_account, disposal_cost_center = get_disposal_account_and_cost_center(asset.company) + disposal_account, depreciation_cost_center = get_disposal_account_and_cost_center(asset.company) accumulated_depr_amount = flt(asset.gross_purchase_amount) - flt(asset.current_value) gl_entries = [ @@ -129,7 +148,7 @@ def get_gl_entries_on_asset_disposal(asset, selling_amount=0): debit_or_credit = "debit" if profit_amount < 0 else "credit" gl_entries.append({ "account": disposal_account, - "cost_center": disposal_cost_center, + "cost_center": depreciation_cost_center, debit_or_credit: abs(profit_amount), debit_or_credit + "_in_account_currency": abs(profit_amount) }) @@ -137,8 +156,12 @@ def get_gl_entries_on_asset_disposal(asset, selling_amount=0): 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 + disposal_account, depreciation_cost_center = frappe.db.get_value("Company", company, + ["disposal_account", "depreciation_cost_center"]) + + if not disposal_account: + frappe.throw(_("Please set 'Asset Disposal Account' in Company {0}").format(company)) + if not depreciation_cost_center: + frappe.throw(_("Please set 'Asset Depreciation Cost Center' in Company {0}").format(company)) + + return disposal_account, depreciation_cost_center \ No newline at end of file diff --git a/erpnext/accounts/doctype/asset/test_asset.py b/erpnext/accounts/doctype/asset/test_asset.py index 031fcc2c367..99020340909 100644 --- a/erpnext/accounts/doctype/asset/test_asset.py +++ b/erpnext/accounts/doctype/asset/test_asset.py @@ -5,9 +5,13 @@ from __future__ import unicode_literals import frappe import unittest +from frappe.utils import cstr +from erpnext.accounts.doctype.asset.depreciation import post_depreciation_entries, scrap_asset, restore_asset +from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice class TestAsset(unittest.TestCase): def setUp(self): + set_depreciation_settings_in_company() create_asset() def test_fixed_asset_must_be_non_stock_item(self): @@ -15,11 +19,128 @@ class TestAsset(unittest.TestCase): item.is_stock_item = 1 self.assertRaises(frappe.ValidationError, item.save) - def test_asset_purchase(self): - asset = create_asset() + def test_schedule_for_straight_line_method(self): + asset = frappe.get_doc("Asset", "Macbook Pro 1") - self.assertEqual(asset.current_value, 100000) - + self.assertEqual(asset.status, "Draft") + + expected_schedules = [ + ["2015-12-31", 30000, 30000], + ["2016-03-31", 30000, 60000], + ["2016-06-30", 30000, 90000] + ] + + schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount] + for d in asset.get("schedules")] + + self.assertEqual(schedules, expected_schedules) + + + def test_schedule_for_double_declining_method(self): + asset = frappe.get_doc("Asset", "Macbook Pro 1") + asset.depreciation_method = "Double Declining Balance" + asset.save() + + expected_schedules = [ + ["2015-12-31", 66667, 66667], + ["2016-03-31", 22222, 88889], + ["2016-06-30", 1111, 90000] + ] + + schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount] + for d in asset.get("schedules")] + + self.assertEqual(schedules, expected_schedules) + + def test_depreciation(self): + asset = frappe.get_doc("Asset", "Macbook Pro 1") + asset.submit() + asset.load_from_db() + self.assertEqual(asset.status, "Submitted") + + post_depreciation_entries(date="2016-01-01") + asset.load_from_db() + + self.assertEqual(asset.status, "Partially Depreciated") + + expected_gle = ( + ("_Test Accumulated Depreciations - _TC", 0.0, 30000.0), + ("_Test Depreciations - _TC", 30000.0, 0.0) + ) + + gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry` + where against_voucher_type='Asset' and against_voucher = %s + order by account""", asset.name) + + self.assertEqual(gle, expected_gle) + self.assertEqual(asset.get("current_value"), 70000) + + + def test_scrap_asset(self): + asset = frappe.get_doc("Asset", "Macbook Pro 1") + asset.submit() + post_depreciation_entries(date="2016-01-01") + + scrap_asset("Macbook Pro 1") + + asset.load_from_db() + self.assertEqual(asset.status, "Scrapped") + self.assertTrue(asset.journal_entry_for_scrap) + + expected_gle = ( + ("_Test Accumulated Depreciations - _TC", 30000.0, 0.0), + ("_Test Fixed Asset - _TC", 0.0, 100000.0), + ("_Test Gain/Loss on Asset Disposal - _TC", 70000.0, 0.0) + ) + + gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry` + where voucher_type='Journal Entry' and voucher_no = %s + order by account""", asset.journal_entry_for_scrap) + + self.assertEqual(gle, expected_gle) + + restore_asset("Macbook Pro 1") + + asset.load_from_db() + self.assertFalse(asset.journal_entry_for_scrap) + self.assertEqual(asset.status, "Partially Depreciated") + + def test_asset_sale(self): + frappe.get_doc("Asset", "Macbook Pro 1").submit() + post_depreciation_entries(date="2016-01-01") + + si = create_sales_invoice(item_code="Macbook Pro", rate=25000, do_not_save=True) + si.get("items")[0].asset = "Macbook Pro 1" + si.submit() + + self.assertEqual(frappe.db.get_value("Asset", "Macbook Pro 1", "status"), "Sold") + + expected_gle = ( + ("_Test Accumulated Depreciations - _TC", 30000.0, 0.0), + ("_Test Fixed Asset - _TC", 0.0, 100000.0), + ("_Test Gain/Loss on Asset Disposal - _TC", 45000.0, 0.0), + ("Debtors - _TC", 25000.0, 0.0) + ) + + gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry` + where voucher_type='Sales Invoice' and voucher_no = %s + order by account""", si.name) + + self.assertEqual(gle, expected_gle) + + si.cancel() + + self.assertEqual(frappe.db.get_value("Asset", "Macbook Pro 1", "status"), "Partially Depreciated") + + def tearDown(self): + asset = frappe.get_doc("Asset", "Macbook Pro 1") + + if asset.docstatus == 1 and asset.status not in ("Scrapped", "Sold", "Draft", "Cancelled"): + asset.cancel() + + self.assertEqual(frappe.db.get_value("Asset", "Macbook Pro 1", "status"), "Cancelled") + + frappe.delete_doc("Asset", "Macbook Pro 1") def create_asset(): if not frappe.db.exists("Asset Category", "Computers"): @@ -33,6 +154,7 @@ def create_asset(): "asset_name": "Macbook Pro 1", "asset_category": "Computers", "item_code": "Macbook Pro", + "company": "_Test Company", "purchase_date": "2015-01-01", "next_depreciation_date": "2015-12-31", "gross_purchase_amount": 100000, @@ -48,8 +170,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 = 5 - asset_category.number_of_months_in_a_period = 12 + asset_category.number_of_depreciations = 3 + asset_category.number_of_months_in_a_period = 3 asset_category.append("accounts", { "company": "_Test Company", "fixed_asset_account": "_Test Fixed Asset - _TC", @@ -73,4 +195,10 @@ def create_fixed_asset_item(): except frappe.DuplicateEntryError: pass - \ No newline at end of file +def set_depreciation_settings_in_company(): + company = frappe.get_doc("Company", "_Test Company") + company.accumulated_depreciation_account = "_Test Accumulated Depreciations - _TC" + company.depreciation_expense_account = "_Test Depreciations - _TC" + company.disposal_account = "_Test Gain/Loss on Asset Disposal - _TC" + company.depreciation_cost_center = "_Test Cost Center - _TC" + company.save() \ No newline at end of file diff --git a/erpnext/accounts/doctype/asset_category/test_asset_category.py b/erpnext/accounts/doctype/asset_category/test_asset_category.py index 1fe4aae86fb..d119066189a 100644 --- a/erpnext/accounts/doctype/asset_category/test_asset_category.py +++ b/erpnext/accounts/doctype/asset_category/test_asset_category.py @@ -18,7 +18,7 @@ class TestAssetCategory(unittest.TestCase): asset_category.append("accounts", { "company": "_Test Company", "fixed_asset_account": "_Test Fixed Asset - _TC", - "accumulated_depreciation_account": "_Test Accoumulated Depreciations - _TC", + "accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC", "depreciation_expense_account": "_Test Depreciations - _TC" }) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index 9505528fd24..dd8d51e0249 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -88,8 +88,8 @@ class GLEntry(Document): "Cost Center", self.cost_center, "company") return self.cost_center_company[self.cost_center] - - if self.cost_center and _get_cost_center_company() != self.company: + + if self.cost_center and _get_cost_center_company() != self.company: frappe.throw(_("Cost Center {0} does not belong to Company {1}").format(self.cost_center, self.company)) def validate_party(self): diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 0143a4f2206..bcb0503c994 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -217,7 +217,7 @@ cur_frm.set_query("asset", "items", function(doc, cdt, cdn) { 'item_code': d.item_code, 'docstatus': 1, 'company': doc.company, - 'status': 'Available' + 'status': 'Submitted' } } }); diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 06b2ecec470..7d9745bee38 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -237,7 +237,7 @@ class PurchaseInvoice(BuyingController): def on_submit(self): self.check_prev_docstatus() - self.post_asset_depreciation() + self.validate_asset() frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype, self.company, self.base_grand_total) @@ -252,29 +252,30 @@ class PurchaseInvoice(BuyingController): self.update_project() - def post_asset_depreciation(self): + def validate_asset(self): for d in self.get("items"): if frappe.db.get_value("Item", d.item_code, "is_fixed_asset"): if not d.asset: frappe.throw(_("Row #{0}: Asset is mandatory against a Fixed Asset Item").format(d.idx)) else: asset = frappe.get_doc("Asset", d.asset) - self.validate_asset(asset, d) + + super(PurchaseInvoice, self).validate_asset(asset, d) + + if getdate(asset.purchase_date) != getdate(self.posting_date): + frappe.throw(_("Purchase Date of asset {0} does not match with Purchase Invoice date") + .format(d.asset)) + + if asset.supplier != self.supplier: + frappe.throw(_("Supplier of asset {0} does not match with the supplier in the Purchase Invoice").format(d.asset)) + + if asset.status != "Available": + 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)) - - 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")) @@ -441,7 +442,7 @@ class PurchaseInvoice(BuyingController): self.make_gl_entries_on_cancel() self.update_project() - self.post_asset_depreciation() + self.validate_asset() 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 265e6979f2d..f6f746a97ff 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -475,11 +475,11 @@ 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' - } + filters: [ + ["Asset", "item_code", "=", d.item_code], + ["Asset", "docstatus", "=", 1], + ["Asset", "status", "in", ["Submitted", "Partially Depreciated", "Fully Depreciated"]], + ["Asset", "company", "=", doc.company] + ] } }); \ 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 97580671d1f..9ffe8a97898 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -484,6 +484,13 @@ class SalesInvoice(SellingController): if d.delivery_note and frappe.db.get_value("Delivery Note", d.delivery_note, "docstatus") != 1: throw(_("Delivery Note {0} is not submitted").format(d.delivery_note)) + + def validate_asset(self, asset, item_row): + super(SalesInvoice, self).validate_asset(asset, item_row) + + if self.docstatus == 1 and asset.status in ("Scrapped", "Cancelled", "Sold"): + frappe.throw(_("Row #{0}: Asset {1} cannot be submitted, it is already {2}") + .format(item_row.idx, asset.name, asset.status)) def make_gl_entries(self, repost_future_gle=True): gl_entries = self.get_gl_entries() @@ -582,7 +589,7 @@ class SalesInvoice(SellingController): for gle in fixed_asset_gl_entries: gl_entries.append(self.get_gl_dict(gle)) - frappe.db.set_value("Asset", asset.name, "status", "Sold") + asset.set_status("Sold" if self.docstatus==1 else None) else: account_currency = get_account_currency(item.income_account) gl_entries.append( diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 777fe1e121f..dfa6c0ac192 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -141,7 +141,7 @@ erpnext.company.setup_queries = function(frm) { ["disposal_account", {"report_type": "Profit and Loss"}], ["cost_center", {}], ["round_off_cost_center", {}], - ["disposal_cost_center", {}] + ["depreciation_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 be63aac6c3c..9d6785d93d7 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -1091,14 +1091,14 @@ "allow_on_submit": 0, "bold": 0, "collapsible": 0, - "fieldname": "disposal_cost_center", + "fieldname": "depreciation_cost_center", "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, - "label": "Asset Disposal Cost Center", + "label": "Asset Depreciation Cost Center", "length": 0, "no_copy": 1, "options": "Cost Center", @@ -1387,7 +1387,7 @@ "istable": 0, "max_attachments": 0, "menu_index": 0, - "modified": "2016-03-09 12:06:12.189968", + "modified": "2016-03-10 04:34:43.440914", "modified_by": "Administrator", "module": "Setup", "name": "Company", @@ -1536,6 +1536,5 @@ ], "read_only": 0, "read_only_onload": 0, - "sort_order": "ASC", - "version": 0 + "sort_order": "ASC" } \ No newline at end of file