Fixed asset: status, restore, testcase and much more

This commit is contained in:
Nabin Hait
2016-03-11 15:42:23 +05:30
parent 4d668dbaa3
commit 7536349f17
13 changed files with 307 additions and 71 deletions

View File

@@ -17,10 +17,16 @@ frappe.ui.form.on('Asset', {
}, },
refresh: function(frm) { refresh: function(frm) {
if(frm.doc.docstatus==1 && frm.doc.status=='Available') { if (frm.doc.docstatus==1) {
cur_frm.add_custom_button("Scrap", function() { if (in_list(["Submittted", "Partially Depreciated", "Fully Depreciated"], frm.doc.status)) {
erpnext.asset.scrap_asset(frm); 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);
});
}
} }
} }
}); });
@@ -38,3 +44,17 @@ 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();
}
})
})
}

View File

@@ -90,7 +90,7 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"default": "Available", "default": "Draft",
"fieldname": "status", "fieldname": "status",
"fieldtype": "Select", "fieldtype": "Select",
"hidden": 0, "hidden": 0,
@@ -101,7 +101,7 @@
"label": "Status", "label": "Status",
"length": 0, "length": 0,
"no_copy": 1, "no_copy": 1,
"options": "Available\nSold\nScrapped", "options": "Draft\nSubmitted\nPartially Depreciated\nFully Depreciated\nSold\nScrapped",
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
@@ -240,6 +240,32 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 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, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@@ -356,7 +382,7 @@
"in_list_view": 0, "in_list_view": 0,
"label": "Next Depreciation Date", "label": "Next Depreciation Date",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 1,
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
@@ -458,7 +484,7 @@
"in_list_view": 0, "in_list_view": 0,
"label": "Current Value (After Depreciation)", "label": "Current Value (After Depreciation)",
"length": 0, "length": 0,
"no_copy": 0, "no_copy": 1,
"options": "Company:company:default_currency", "options": "Company:company:default_currency",
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
@@ -557,7 +583,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"modified": "2016-03-09 12:22:05.223886", "modified": "2016-03-11 12:23:37.114298",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Asset", "name": "Asset",

View File

@@ -10,14 +10,20 @@ from frappe.model.document import Document
class Asset(Document): class Asset(Document):
def validate(self): def validate(self):
self.set_status()
self.validate_fixed_asset_item() self.validate_fixed_asset_item()
self.validate_asset_values() self.validate_asset_values()
self.set_depreciation_settings() self.set_depreciation_settings()
self.make_depreciation_schedule() self.make_depreciation_schedule()
self.validate_depreciation_settings_in_company()
def on_submit(self):
self.set_status()
def on_cancel(self): def on_cancel(self):
self.validate_cancellation() self.validate_cancellation()
self.delete_depreciation_entries() self.delete_depreciation_entries()
self.set_status()
def validate_fixed_asset_item(self): def validate_fixed_asset_item(self):
item = frappe.get_doc("Item", self.item_code) item = frappe.get_doc("Item", self.item_code)
@@ -49,7 +55,7 @@ class Asset(Document):
def make_depreciation_schedule(self): def make_depreciation_schedule(self):
self.schedules = [] self.schedules = []
if not self.get("schedules") and self.status == "Available": if not self.get("schedules"):
accumulated_depreciation = 0 accumulated_depreciation = 0
value_after_depreciation = flt(self.current_value) value_after_depreciation = flt(self.current_value)
for n in xrange(self.number_of_depreciations): for n in xrange(self.number_of_depreciations):
@@ -71,8 +77,8 @@ class Asset(Document):
depreciation_amount = (flt(self.current_value) - depreciation_amount = (flt(self.current_value) -
flt(self.expected_value_after_useful_life)) / cint(self.number_of_depreciations) flt(self.expected_value_after_useful_life)) / cint(self.number_of_depreciations)
else: else:
factor = 200 / cint(self.number_of_depreciations) factor = 200.0 / cint(self.number_of_depreciations)
depreciation_amount = depreciable_value * factor / 100 depreciation_amount = flt(depreciable_value * factor / 100, 0)
value_after_depreciation = flt(depreciable_value) - depreciation_amount value_after_depreciation = flt(depreciable_value) - depreciation_amount
if value_after_depreciation < flt(self.expected_value_after_useful_life): if value_after_depreciation < flt(self.expected_value_after_useful_life):
@@ -81,9 +87,11 @@ class Asset(Document):
return depreciation_amount return depreciation_amount
def validate_cancellation(self): def validate_cancellation(self):
if self.status != "Available": if self.status not in ("Submitted", "Partially Depreciated", "Fully Depreciated"):
frappe.throw(_("Asset {0} cannot be cancelled, as it is already {1}") frappe.throw(_("Asset cannot be cancelled, as it is already {0}").format(self.status))
.format(self.name, self.status))
if self.purchase_invoice:
frappe.throw(_("Please cancel Purchase Invoice {0} first").format(self.purchase_invoice))
def delete_depreciation_entries(self): def delete_depreciation_entries(self):
total_depreciation_amount = 0 total_depreciation_amount = 0
@@ -95,3 +103,27 @@ class Asset(Document):
total_depreciation_amount += flt(d.depreciation_amount) total_depreciation_amount += flt(d.depreciation_amount)
self.db_set("current_value", (self.current_value - total_depreciation_amount)) self.db_set("current_value", (self.current_value - total_depreciation_amount))
def validate_depreciation_settings_in_company(self):
company = frappe.get_doc("Company", self.company)
for field in ("accumulated_depreciation_account", "depreciation_expense_account",
"disposal_account", "depreciation_cost_center"):
if not company.get(field):
frappe.throw(_("Please set {0} in Company {1}")
.format(company.meta.get_label(field), self.company))
def set_status(self, status=None):
if not status:
if self.docstatus == 0:
status = "Draft"
elif self.docstatus == 1:
status = "Submitted"
if self.journal_entry_for_scrap:
status = "Scrapped"
elif flt(self.current_value) <= flt(self.expected_value_after_useful_life):
status = "Fully Depreciated"
elif flt(self.current_value) < flt(self.gross_purchase_amount):
status = 'Partially Depreciated'
elif self.docstatus == 2:
status = "Cancelled"
frappe.db.set_value(self.doctype, self.name, "status", status)

View File

@@ -17,7 +17,8 @@ def get_depreciable_assets(date):
return frappe.db.sql_list("""select a.name return frappe.db.sql_list("""select a.name
from tabAsset a, `tabDepreciation Schedule` ds from tabAsset a, `tabDepreciation Schedule` ds
where a.name = ds.parent and a.docstatus=1 and ds.schedule_date<=%s 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): def make_depreciation_entry(asset_name, date=None):
if not date: if not date:
@@ -27,6 +28,8 @@ def make_depreciation_entry(asset_name, date=None):
fixed_asset_account, accumulated_depreciation_account, depreciation_expense_account = \ fixed_asset_account, accumulated_depreciation_account, depreciation_expense_account = \
get_depreciation_accounts(asset) get_depreciation_accounts(asset)
depreciation_cost_center = frappe.db.get_value("Company", asset.company, "depreciation_cost_center")
for d in asset.get("schedules"): for d in asset.get("schedules"):
if not d.journal_entry and getdate(d.schedule_date) <= getdate(date): if not d.journal_entry and getdate(d.schedule_date) <= getdate(date):
je = frappe.new_doc("Journal Entry") je = frappe.new_doc("Journal Entry")
@@ -46,7 +49,8 @@ def make_depreciation_entry(asset_name, date=None):
"account": depreciation_expense_account, "account": depreciation_expense_account,
"debit_in_account_currency": d.depreciation_amount, "debit_in_account_currency": d.depreciation_amount,
"reference_type": "Asset", "reference_type": "Asset",
"reference_name": asset.name "reference_name": asset.name,
"cost_center": depreciation_cost_center
}) })
je.flags.ignore_permissions = True je.flags.ignore_permissions = True
@@ -54,7 +58,9 @@ def make_depreciation_entry(asset_name, date=None):
d.db_set("journal_entry", je.name) d.db_set("journal_entry", je.name)
asset.current_value -= d.depreciation_amount 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): def get_depreciation_accounts(asset):
accounts = frappe.db.sql("""select fixed_asset_account, accumulated_depreciation_account, 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): def scrap_asset(asset_name):
asset = frappe.get_doc("Asset", asset_name) asset = frappe.get_doc("Asset", asset_name)
if asset.docstatus != 1 or asset.status != 'Available': if asset.docstatus != 1:
frappe.throw(_("Asset {0} must be submitted and available").format(asset.name)) 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 = frappe.new_doc("Journal Entry")
je.voucher_type = "Journal Entry" je.voucher_type = "Journal Entry"
@@ -103,12 +111,23 @@ def scrap_asset(asset_name):
je.flags.ignore_permissions = True je.flags.ignore_permissions = True
je.submit() 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() @frappe.whitelist()
def get_gl_entries_on_asset_disposal(asset, selling_amount=0): def get_gl_entries_on_asset_disposal(asset, selling_amount=0):
fixed_asset_account, accumulated_depr_account, depr_expense_account = get_depreciation_accounts(asset) 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) accumulated_depr_amount = flt(asset.gross_purchase_amount) - flt(asset.current_value)
gl_entries = [ 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" debit_or_credit = "debit" if profit_amount < 0 else "credit"
gl_entries.append({ gl_entries.append({
"account": disposal_account, "account": disposal_account,
"cost_center": disposal_cost_center, "cost_center": depreciation_cost_center,
debit_or_credit: abs(profit_amount), debit_or_credit: abs(profit_amount),
debit_or_credit + "_in_account_currency": 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 return gl_entries
def get_disposal_account_and_cost_center(company): def get_disposal_account_and_cost_center(company):
disposal_account, disposal_cost_center = frappe.db.get_value("Company", company, disposal_account, depreciation_cost_center = frappe.db.get_value("Company", company,
["disposal_account", "disposal_cost_center"]) ["disposal_account", "depreciation_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)) if not disposal_account:
return disposal_account, disposal_cost_center frappe.throw(_("Please set 'Asset Disposal Account' in Company {0}").format(company))
if not depreciation_cost_center:
frappe.throw(_("Please set 'Asset Depreciation Cost Center' in Company {0}").format(company))
return disposal_account, depreciation_cost_center

View File

@@ -5,9 +5,13 @@ from __future__ import unicode_literals
import frappe import frappe
import unittest 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): class TestAsset(unittest.TestCase):
def setUp(self): def setUp(self):
set_depreciation_settings_in_company()
create_asset() create_asset()
def test_fixed_asset_must_be_non_stock_item(self): def test_fixed_asset_must_be_non_stock_item(self):
@@ -15,11 +19,128 @@ class TestAsset(unittest.TestCase):
item.is_stock_item = 1 item.is_stock_item = 1
self.assertRaises(frappe.ValidationError, item.save) self.assertRaises(frappe.ValidationError, item.save)
def test_asset_purchase(self): def test_schedule_for_straight_line_method(self):
asset = create_asset() 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(): def create_asset():
if not frappe.db.exists("Asset Category", "Computers"): if not frappe.db.exists("Asset Category", "Computers"):
@@ -33,6 +154,7 @@ def create_asset():
"asset_name": "Macbook Pro 1", "asset_name": "Macbook Pro 1",
"asset_category": "Computers", "asset_category": "Computers",
"item_code": "Macbook Pro", "item_code": "Macbook Pro",
"company": "_Test Company",
"purchase_date": "2015-01-01", "purchase_date": "2015-01-01",
"next_depreciation_date": "2015-12-31", "next_depreciation_date": "2015-12-31",
"gross_purchase_amount": 100000, "gross_purchase_amount": 100000,
@@ -48,8 +170,8 @@ def create_asset():
def create_asset_category(): def create_asset_category():
asset_category = frappe.new_doc("Asset Category") asset_category = frappe.new_doc("Asset Category")
asset_category.asset_category_name = "Computers" asset_category.asset_category_name = "Computers"
asset_category.number_of_depreciations = 5 asset_category.number_of_depreciations = 3
asset_category.number_of_months_in_a_period = 12 asset_category.number_of_months_in_a_period = 3
asset_category.append("accounts", { asset_category.append("accounts", {
"company": "_Test Company", "company": "_Test Company",
"fixed_asset_account": "_Test Fixed Asset - _TC", "fixed_asset_account": "_Test Fixed Asset - _TC",
@@ -73,4 +195,10 @@ def create_fixed_asset_item():
except frappe.DuplicateEntryError: except frappe.DuplicateEntryError:
pass pass
def set_depreciation_settings_in_company():
company = frappe.get_doc("Company", "_Test Company")
company.accumulated_depreciation_account = "_Test Accumulated Depreciations - _TC"
company.depreciation_expense_account = "_Test Depreciations - _TC"
company.disposal_account = "_Test Gain/Loss on Asset Disposal - _TC"
company.depreciation_cost_center = "_Test Cost Center - _TC"
company.save()

View File

@@ -18,7 +18,7 @@ class TestAssetCategory(unittest.TestCase):
asset_category.append("accounts", { asset_category.append("accounts", {
"company": "_Test Company", "company": "_Test Company",
"fixed_asset_account": "_Test Fixed Asset - _TC", "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" "depreciation_expense_account": "_Test Depreciations - _TC"
}) })

View File

@@ -217,7 +217,7 @@ cur_frm.set_query("asset", "items", function(doc, cdt, cdn) {
'item_code': d.item_code, 'item_code': d.item_code,
'docstatus': 1, 'docstatus': 1,
'company': doc.company, 'company': doc.company,
'status': 'Available' 'status': 'Submitted'
} }
} }
}); });

View File

@@ -237,7 +237,7 @@ class PurchaseInvoice(BuyingController):
def on_submit(self): def on_submit(self):
self.check_prev_docstatus() self.check_prev_docstatus()
self.post_asset_depreciation() self.validate_asset()
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype, frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
self.company, self.base_grand_total) self.company, self.base_grand_total)
@@ -252,29 +252,30 @@ class PurchaseInvoice(BuyingController):
self.update_project() self.update_project()
def post_asset_depreciation(self): def validate_asset(self):
for d in self.get("items"): for d in self.get("items"):
if frappe.db.get_value("Item", d.item_code, "is_fixed_asset"): if frappe.db.get_value("Item", d.item_code, "is_fixed_asset"):
if not d.asset: if not d.asset:
frappe.throw(_("Row #{0}: Asset is mandatory against a Fixed Asset Item").format(d.idx)) frappe.throw(_("Row #{0}: Asset is mandatory against a Fixed Asset Item").format(d.idx))
else: else:
asset = frappe.get_doc("Asset", d.asset) 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", frappe.db.set_value("Asset", asset.name, "purchase_invoice",
(self.name if self.docstatus==1 else None)) (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): def make_gl_entries(self):
auto_accounting_for_stock = \ auto_accounting_for_stock = \
cint(frappe.defaults.get_global_default("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.make_gl_entries_on_cancel()
self.update_project() self.update_project()
self.post_asset_depreciation() self.validate_asset()
def update_project(self): def update_project(self):
project_list = [] project_list = []

View File

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

View File

@@ -485,6 +485,13 @@ class SalesInvoice(SellingController):
if d.delivery_note and frappe.db.get_value("Delivery Note", d.delivery_note, "docstatus") != 1: 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)) 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): def make_gl_entries(self, repost_future_gle=True):
gl_entries = self.get_gl_entries() gl_entries = self.get_gl_entries()
@@ -582,7 +589,7 @@ class SalesInvoice(SellingController):
for gle in fixed_asset_gl_entries: for gle in fixed_asset_gl_entries:
gl_entries.append(self.get_gl_dict(gle)) 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: else:
account_currency = get_account_currency(item.income_account) account_currency = get_account_currency(item.income_account)
gl_entries.append( gl_entries.append(

View File

@@ -141,7 +141,7 @@ erpnext.company.setup_queries = function(frm) {
["disposal_account", {"report_type": "Profit and Loss"}], ["disposal_account", {"report_type": "Profit and Loss"}],
["cost_center", {}], ["cost_center", {}],
["round_off_cost_center", {}], ["round_off_cost_center", {}],
["disposal_cost_center", {}] ["depreciation_cost_center", {}]
], function(i, v) { ], function(i, v) {
erpnext.company.set_custom_query(frm, v); erpnext.company.set_custom_query(frm, v);
}); });

View File

@@ -1091,14 +1091,14 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "disposal_cost_center", "fieldname": "depreciation_cost_center",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"ignore_xss_filter": 0, "ignore_xss_filter": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Asset Disposal Cost Center", "label": "Asset Depreciation Cost Center",
"length": 0, "length": 0,
"no_copy": 1, "no_copy": 1,
"options": "Cost Center", "options": "Cost Center",
@@ -1387,7 +1387,7 @@
"istable": 0, "istable": 0,
"max_attachments": 0, "max_attachments": 0,
"menu_index": 0, "menu_index": 0,
"modified": "2016-03-09 12:06:12.189968", "modified": "2016-03-10 04:34:43.440914",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Setup", "module": "Setup",
"name": "Company", "name": "Company",
@@ -1536,6 +1536,5 @@
], ],
"read_only": 0, "read_only": 0,
"read_only_onload": 0, "read_only_onload": 0,
"sort_order": "ASC", "sort_order": "ASC"
"version": 0
} }