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,10 +175,6 @@ 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",
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")]) 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":

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:
if not self.available_for_use_date:
frappe.throw(_("Row {0}: Depreciation Start Date is required").format(row.idx)) 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

@ -8,14 +8,10 @@ def execute():
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: