Fixed Asset Refactor Review fixes (#19666)

* fix: fixed asset item creation ux fixes

* fix: auto creation of asset ux fixes

* fix: [LCV] incorrect condition when checking assets linked with PR

* fix: bulk update assets

* refac: remove company level cwip enabling
* cwip can be enabled only on category level

* fix: #19649
This commit is contained in:
Saqib 2019-11-22 16:32:34 +05:30 committed by Nabin Hait
parent b5c296da9e
commit 1919af2ff1
13 changed files with 562 additions and 567 deletions

View File

@ -237,7 +237,7 @@ class PurchaseInvoice(BuyingController):
item.expense_account = warehouse_account[item.warehouse]["account"] item.expense_account = warehouse_account[item.warehouse]["account"]
else: else:
item.expense_account = stock_not_billed_account item.expense_account = stock_not_billed_account
elif item.is_fixed_asset and not is_cwip_accounting_enabled(self.company, asset_category): elif item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category):
item.expense_account = get_asset_category_account('fixed_asset_account', item=item.item_code, item.expense_account = get_asset_category_account('fixed_asset_account', item=item.item_code,
company = self.company) company = self.company)
elif item.is_fixed_asset and item.pr_detail: elif item.is_fixed_asset and item.pr_detail:
@ -408,7 +408,7 @@ class PurchaseInvoice(BuyingController):
for item in self.get("items"): for item in self.get("items"):
if item.item_code and item.is_fixed_asset: if item.item_code and item.is_fixed_asset:
asset_category = frappe.get_cached_value("Item", item.item_code, "asset_category") asset_category = frappe.get_cached_value("Item", item.item_code, "asset_category")
if is_cwip_accounting_enabled(self.company, asset_category): if is_cwip_accounting_enabled(asset_category):
return 1 return 1
return 0 return 0
@ -504,8 +504,7 @@ class PurchaseInvoice(BuyingController):
"credit": flt(item.rm_supp_cost) "credit": flt(item.rm_supp_cost)
}, warehouse_account[self.supplier_warehouse]["account_currency"], item=item)) }, warehouse_account[self.supplier_warehouse]["account_currency"], item=item))
elif not item.is_fixed_asset or (item.is_fixed_asset and not is_cwip_accounting_enabled(self.company, elif not item.is_fixed_asset or (item.is_fixed_asset and not is_cwip_accounting_enabled(asset_category)):
asset_category)):
expense_account = (item.expense_account expense_account = (item.expense_account
if (not item.enable_deferred_expense or self.is_return) else item.deferred_expense_account) if (not item.enable_deferred_expense or self.is_return) else item.deferred_expense_account)

View File

@ -175,11 +175,7 @@ def validate_account_for_perpetual_inventory(gl_map):
StockValueAndAccountBalanceOutOfSync, title=_('Account Balance Out Of Sync')) StockValueAndAccountBalanceOutOfSync, title=_('Account Balance Out Of Sync'))
def validate_cwip_accounts(gl_map): def validate_cwip_accounts(gl_map):
cwip_enabled = cint(frappe.get_cached_value("Company", cwip_enabled = any([cint(ac.enable_cwip_accounting) for ac in frappe.db.get_all("Asset Category","enable_cwip_accounting")])
gl_map[0].company, "enable_cwip_accounting"))
if not cwip_enabled:
cwip_enabled = any([cint(ac.enable_cwip_accounting) for ac in frappe.db.get_all("Asset Category","enable_cwip_accounting")])
if cwip_enabled and gl_map[0].voucher_type == "Journal Entry": if cwip_enabled and gl_map[0].voucher_type == "Journal Entry":
cwip_accounts = [d[0] for d in frappe.db.sql("""select name from tabAccount cwip_accounts = [d[0] for d in frappe.db.sql("""select name from tabAccount

View File

@ -31,8 +31,7 @@ class Asset(AccountsController):
self.validate_in_use_date() self.validate_in_use_date()
self.set_status() self.set_status()
self.make_asset_movement() self.make_asset_movement()
if not self.booked_fixed_asset and is_cwip_accounting_enabled(self.company, if not self.booked_fixed_asset and is_cwip_accounting_enabled(self.asset_category):
self.asset_category):
self.make_gl_entries() self.make_gl_entries()
def before_cancel(self): def before_cancel(self):
@ -99,7 +98,7 @@ class Asset(AccountsController):
if not flt(self.gross_purchase_amount): if not flt(self.gross_purchase_amount):
frappe.throw(_("Gross Purchase Amount is mandatory"), frappe.MandatoryError) frappe.throw(_("Gross Purchase Amount is mandatory"), frappe.MandatoryError)
if is_cwip_accounting_enabled(self.company, self.asset_category): if is_cwip_accounting_enabled(self.asset_category):
if not self.is_existing_asset and not (self.purchase_receipt or self.purchase_invoice): if not self.is_existing_asset and not (self.purchase_receipt or self.purchase_invoice):
frappe.throw(_("Please create purchase receipt or purchase invoice for the item {0}"). frappe.throw(_("Please create purchase receipt or purchase invoice for the item {0}").
format(self.item_code)) format(self.item_code))
@ -295,7 +294,9 @@ class Asset(AccountsController):
.format(row.idx)) .format(row.idx))
if not row.depreciation_start_date: if not row.depreciation_start_date:
frappe.throw(_("Row {0}: Depreciation Start Date is required").format(row.idx)) if not self.available_for_use_date:
frappe.throw(_("Row {0}: Depreciation Start Date is required").format(row.idx))
row.depreciation_start_date = self.available_for_use_date
if not self.is_existing_asset: if not self.is_existing_asset:
self.opening_accumulated_depreciation = 0 self.opening_accumulated_depreciation = 0
@ -514,7 +515,7 @@ def update_maintenance_status():
asset.set_status('Out of Order') asset.set_status('Out of Order')
def make_post_gl_entry(): def make_post_gl_entry():
if not is_cwip_accounting_enabled(self.company, self.asset_category): if not is_cwip_accounting_enabled(self.asset_category):
return return
assets = frappe.db.sql_list(""" select name from `tabAsset` assets = frappe.db.sql_list(""" select name from `tabAsset`
@ -683,12 +684,7 @@ def make_asset_movement(assets):
if asset_movement.get('assets'): if asset_movement.get('assets'):
return asset_movement.as_dict() return asset_movement.as_dict()
def is_cwip_accounting_enabled(company, asset_category=None): def is_cwip_accounting_enabled(asset_category):
enable_cwip_in_company = cint(frappe.db.get_value("Company", company, "enable_cwip_accounting"))
if enable_cwip_in_company or not asset_category:
return enable_cwip_in_company
return cint(frappe.db.get_value("Asset Category", asset_category, "enable_cwip_accounting")) return cint(frappe.db.get_value("Asset Category", asset_category, "enable_cwip_accounting"))
def get_pro_rata_amt(row, depreciation_amount, from_date, to_date): def get_pro_rata_amt(row, depreciation_amount, from_date, to_date):

File diff suppressed because it is too large Load Diff

View File

@ -11,7 +11,6 @@ from frappe.model.document import Document
class AssetCategory(Document): class AssetCategory(Document):
def validate(self): def validate(self):
self.validate_finance_books() self.validate_finance_books()
self.validate_enable_cwip_accounting()
def validate_finance_books(self): def validate_finance_books(self):
for d in self.finance_books: for d in self.finance_books:
@ -19,15 +18,6 @@ class AssetCategory(Document):
if cint(d.get(frappe.scrub(field)))<1: if cint(d.get(frappe.scrub(field)))<1:
frappe.throw(_("Row {0}: {1} must be greater than 0").format(d.idx, field), frappe.MandatoryError) frappe.throw(_("Row {0}: {1} must be greater than 0").format(d.idx, field), frappe.MandatoryError)
def validate_enable_cwip_accounting(self):
if self.enable_cwip_accounting :
for d in self.accounts:
cwip = frappe.db.get_value("Company",d.company_name,"enable_cwip_accounting")
if cwip:
frappe.throw(_
("CWIP is enabled globally in Company {1}. To enable it in Asset Category, first disable it in {1} ").format(
frappe.bold(d.idx), frappe.bold(d.company_name)))
@frappe.whitelist() @frappe.whitelist()
def get_asset_category_account(fieldname, item=None, asset=None, account=None, asset_category = None, company = None): def get_asset_category_account(fieldname, item=None, asset=None, account=None, asset_category = None, company = None):
if item and frappe.db.get_value("Item", item, "is_fixed_asset"): if item and frappe.db.get_value("Item", item, "is_fixed_asset"):

View File

@ -60,7 +60,8 @@
{ {
"fieldname": "date", "fieldname": "date",
"fieldtype": "Date", "fieldtype": "Date",
"label": "Date" "label": "Date",
"reqd": 1
}, },
{ {
"fieldname": "current_asset_value", "fieldname": "current_asset_value",
@ -110,7 +111,7 @@
} }
], ],
"is_submittable": 1, "is_submittable": 1,
"modified": "2019-05-26 09:46:23.613412", "modified": "2019-11-22 14:09:25.800375",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Assets", "module": "Assets",
"name": "Asset Value Adjustment", "name": "Asset Value Adjustment",

View File

@ -5,12 +5,13 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils import flt, getdate, cint, date_diff from frappe.utils import flt, getdate, cint, date_diff, formatdate
from erpnext.assets.doctype.asset.depreciation import get_depreciation_accounts from erpnext.assets.doctype.asset.depreciation import get_depreciation_accounts
from frappe.model.document import Document from frappe.model.document import Document
class AssetValueAdjustment(Document): class AssetValueAdjustment(Document):
def validate(self): def validate(self):
self.validate_date()
self.set_difference_amount() self.set_difference_amount()
self.set_current_asset_value() self.set_current_asset_value()
@ -24,6 +25,12 @@ class AssetValueAdjustment(Document):
self.reschedule_depreciations(self.current_asset_value) self.reschedule_depreciations(self.current_asset_value)
def validate_date(self):
asset_purchase_date = frappe.db.get_value('Asset', self.asset, 'purchase_date')
if getdate(self.date) < getdate(asset_purchase_date):
frappe.throw(_("Asset Value Adjustment cannot be posted before Asset's purchase date <b>{0}</b>.")
.format(formatdate(asset_purchase_date)), title="Incorrect Date")
def set_difference_amount(self): def set_difference_amount(self):
self.difference_amount = flt(self.current_asset_value - self.new_asset_value) self.difference_amount = flt(self.current_asset_value - self.new_asset_value)

View File

@ -577,6 +577,7 @@ class BuyingController(StockController):
def auto_make_assets(self, asset_items): def auto_make_assets(self, asset_items):
items_data = get_asset_item_details(asset_items) items_data = get_asset_item_details(asset_items)
messages = []
for d in self.items: for d in self.items:
if d.is_fixed_asset: if d.is_fixed_asset:
@ -589,12 +590,16 @@ class BuyingController(StockController):
for qty in range(cint(d.qty)): for qty in range(cint(d.qty)):
self.make_asset(d) self.make_asset(d)
is_plural = 's' if cint(d.qty) != 1 else '' is_plural = 's' if cint(d.qty) != 1 else ''
frappe.msgprint(_('{0} Asset{2} Created for {1}').format(cint(d.qty), d.item_code, is_plural)) messages.append(_('{0} Asset{2} Created for <b>{1}</b>').format(cint(d.qty), d.item_code, is_plural))
else: else:
frappe.throw(_("Asset Naming Series is mandatory for the auto creation for item {0}").format(d.item_code)) frappe.throw(_("Row {1}: Asset Naming Series is mandatory for the auto creation for item {0}")
.format(d.item_code, d.idx))
else: else:
frappe.msgprint(_("Assets not created. You will have to create asset manually.")) messages.append(_("Assets not created for <b>{0}</b>. You will have to create asset manually.")
.format(d.item_code))
for message in messages:
frappe.msgprint(message, title="Success")
def make_asset(self, row): def make_asset(self, row):
if not row.asset_location: if not row.asset_location:

View File

@ -7,15 +7,11 @@ def execute():
'''Get 'Disable CWIP Accounting value' from Asset Settings, set it in 'Enable Capital Work in Progress Accounting' field '''Get 'Disable CWIP Accounting value' from Asset Settings, set it in 'Enable Capital Work in Progress Accounting' field
in Company, delete Asset Settings ''' in Company, delete Asset Settings '''
if frappe.db.exists("DocType","Asset Settings"): if frappe.db.exists("DocType", "Asset Settings"):
frappe.reload_doctype("Company") frappe.reload_doctype("Asset Category")
cwip_value = frappe.db.get_single_value("Asset Settings","disable_cwip_accounting") cwip_value = frappe.db.get_single_value("Asset Settings", "disable_cwip_accounting")
companies = [x['name'] for x in frappe.get_all("Company", "name")] frappe.db.sql("""UPDATE `tabAsset Category` SET enable_cwip_accounting = %s""", cint(cwip_value))
for company in companies:
enable_cwip_accounting = cint(not cint(cwip_value))
frappe.db.set_value("Company", company, "enable_cwip_accounting", enable_cwip_accounting)
frappe.db.sql( frappe.db.sql("""DELETE FROM `tabSingles` where doctype = 'Asset Settings'""")
""" DELETE FROM `tabSingles` where doctype = 'Asset Settings' """) frappe.delete_doc_if_exists("DocType", "Asset Settings")
frappe.delete_doc_if_exists("DocType","Asset Settings")

View File

@ -72,7 +72,6 @@
"stock_received_but_not_billed", "stock_received_but_not_billed",
"expenses_included_in_valuation", "expenses_included_in_valuation",
"fixed_asset_depreciation_settings", "fixed_asset_depreciation_settings",
"enable_cwip_accounting",
"accumulated_depreciation_account", "accumulated_depreciation_account",
"depreciation_expense_account", "depreciation_expense_account",
"series_for_depreciation_entry", "series_for_depreciation_entry",
@ -721,18 +720,12 @@
"fieldtype": "Link", "fieldtype": "Link",
"label": "Default Buying Terms", "label": "Default Buying Terms",
"options": "Terms and Conditions" "options": "Terms and Conditions"
},
{
"default": "0",
"fieldname": "enable_cwip_accounting",
"fieldtype": "Check",
"label": "Enable Capital Work in Progress Accounting"
} }
], ],
"icon": "fa fa-building", "icon": "fa fa-building",
"idx": 1, "idx": 1,
"image_field": "company_logo", "image_field": "company_logo",
"modified": "2019-10-09 14:42:04.440974", "modified": "2019-11-22 13:04:47.470768",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Setup", "module": "Setup",
"name": "Company", "name": "Company",

View File

@ -140,6 +140,7 @@ frappe.ui.form.on("Item", {
// set serial no to false & toggles its visibility // set serial no to false & toggles its visibility
frm.set_value('has_serial_no', 0); frm.set_value('has_serial_no', 0);
frm.toggle_enable(['has_serial_no', 'serial_no_series'], !frm.doc.is_fixed_asset); frm.toggle_enable(['has_serial_no', 'serial_no_series'], !frm.doc.is_fixed_asset);
frm.toggle_reqd(['asset_category'], frm.doc.is_fixed_asset);
frm.toggle_display(['has_serial_no', 'serial_no_series'], !frm.doc.is_fixed_asset); frm.toggle_display(['has_serial_no', 'serial_no_series'], !frm.doc.is_fixed_asset);
frm.call({ frm.call({
@ -150,6 +151,8 @@ frappe.ui.form.on("Item", {
frm.trigger("set_asset_naming_series"); frm.trigger("set_asset_naming_series");
} }
}); });
frm.trigger('auto_create_assets');
}, },
set_asset_naming_series: function(frm) { set_asset_naming_series: function(frm) {
@ -159,8 +162,8 @@ frappe.ui.form.on("Item", {
}, },
auto_create_assets: function(frm) { auto_create_assets: function(frm) {
frm.toggle_reqd(['asset_category', 'asset_naming_series'], frm.doc.auto_create_assets); frm.toggle_reqd(['asset_naming_series'], frm.doc.auto_create_assets);
frm.toggle_display(['asset_category', 'asset_naming_series'], frm.doc.auto_create_assets); frm.toggle_display(['asset_naming_series'], frm.doc.auto_create_assets);
}, },
page_name: frappe.utils.warn_page_name_change, page_name: frappe.utils.warn_page_name_change,

View File

@ -138,8 +138,8 @@ class LandedCostVoucher(Document):
if item.is_fixed_asset: if item.is_fixed_asset:
receipt_document_type = 'purchase_invoice' if item.receipt_document_type == 'Purchase Invoice' \ receipt_document_type = 'purchase_invoice' if item.receipt_document_type == 'Purchase Invoice' \
else 'purchase_receipt' else 'purchase_receipt'
docs = frappe.db.get_all('Asset', filters={ receipt_document_type: item.receipt_document }, docs = frappe.db.get_all('Asset', filters={ receipt_document_type: item.receipt_document,
fields=['name', 'docstatus']) 'item_code': item.item_code }, fields=['name', 'docstatus'])
if not docs or len(docs) != item.qty: if not docs or len(docs) != item.qty:
frappe.throw(_('There are not enough asset created or linked to {0}. \ frappe.throw(_('There are not enough asset created or linked to {0}. \
Please create or link {1} Assets with respective document.').format(item.receipt_document, item.qty)) Please create or link {1} Assets with respective document.').format(item.receipt_document, item.qty))
@ -148,8 +148,7 @@ class LandedCostVoucher(Document):
if d.docstatus == 1: if d.docstatus == 1:
frappe.throw(_('{2} <b>{0}</b> has submitted Assets.\ frappe.throw(_('{2} <b>{0}</b> has submitted Assets.\
Remove Item <b>{1}</b> from table to continue.').format( Remove Item <b>{1}</b> from table to continue.').format(
item.receipt_document, item.item_code, item.receipt_document_type) item.receipt_document, item.item_code, item.receipt_document_type))
)
def update_rate_in_serial_no_for_non_asset_items(self, receipt_document): def update_rate_in_serial_no_for_non_asset_items(self, receipt_document):
for item in receipt_document.get("items"): for item in receipt_document.get("items"):

View File

@ -82,12 +82,22 @@ class PurchaseReceipt(BuyingController):
self.validate_with_previous_doc() self.validate_with_previous_doc()
self.validate_uom_is_integer("uom", ["qty", "received_qty"]) self.validate_uom_is_integer("uom", ["qty", "received_qty"])
self.validate_uom_is_integer("stock_uom", "stock_qty") self.validate_uom_is_integer("stock_uom", "stock_qty")
self.validate_cwip_accounts()
self.check_on_hold_or_closed_status() self.check_on_hold_or_closed_status()
if getdate(self.posting_date) > getdate(nowdate()): if getdate(self.posting_date) > getdate(nowdate()):
throw(_("Posting Date cannot be future date")) throw(_("Posting Date cannot be future date"))
def validate_cwip_accounts(self):
for item in self.get('items'):
if item.is_fixed_asset and is_cwip_accounting_enabled(item.asset_category):
# check cwip accounts before making auto assets
# Improves UX by not giving messages of "Assets Created" before throwing error of not finding arbnb account
arbnb_account = self.get_company_default("asset_received_but_not_billed")
cwip_account = get_asset_account("capital_work_in_progress_account", company = self.company)
break
def validate_with_previous_doc(self): def validate_with_previous_doc(self):
super(PurchaseReceipt, self).validate_with_previous_doc({ super(PurchaseReceipt, self).validate_with_previous_doc({
"Purchase Order": { "Purchase Order": {
@ -343,7 +353,7 @@ class PurchaseReceipt(BuyingController):
def get_asset_gl_entry(self, gl_entries): def get_asset_gl_entry(self, gl_entries):
for item in self.get("items"): for item in self.get("items"):
if item.is_fixed_asset: if item.is_fixed_asset:
if is_cwip_accounting_enabled(self.company, item.asset_category): if is_cwip_accounting_enabled(item.asset_category):
self.add_asset_gl_entries(item, gl_entries) self.add_asset_gl_entries(item, gl_entries)
if flt(item.landed_cost_voucher_amount): if flt(item.landed_cost_voucher_amount):
self.add_lcv_gl_entries(item, gl_entries) self.add_lcv_gl_entries(item, gl_entries)
@ -386,7 +396,7 @@ class PurchaseReceipt(BuyingController):
def add_lcv_gl_entries(self, item, gl_entries): def add_lcv_gl_entries(self, item, gl_entries):
expenses_included_in_asset_valuation = self.get_company_default("expenses_included_in_asset_valuation") expenses_included_in_asset_valuation = self.get_company_default("expenses_included_in_asset_valuation")
if not is_cwip_accounting_enabled(self.company, item.asset_category): if not is_cwip_accounting_enabled(item.asset_category):
asset_account = get_asset_category_account(asset_category=item.asset_category, \ asset_account = get_asset_category_account(asset_category=item.asset_category, \
fieldname='fixed_asset_account', company=self.company) fieldname='fixed_asset_account', company=self.company)
else: else: