feat(Asset Capitalization): Submission and Cancellation
This commit is contained in:
parent
3b9bc8e4ef
commit
7a5d75b68d
@ -37,7 +37,6 @@ from erpnext.assets.doctype.asset.depreciation import (
|
|||||||
get_disposal_account_and_cost_center,
|
get_disposal_account_and_cost_center,
|
||||||
get_gl_entries_on_asset_disposal,
|
get_gl_entries_on_asset_disposal,
|
||||||
get_gl_entries_on_asset_regain,
|
get_gl_entries_on_asset_regain,
|
||||||
post_depreciation_entries,
|
|
||||||
)
|
)
|
||||||
from erpnext.controllers.selling_controller import SellingController
|
from erpnext.controllers.selling_controller import SellingController
|
||||||
from erpnext.healthcare.utils import manage_invoice_submit_cancel
|
from erpnext.healthcare.utils import manage_invoice_submit_cancel
|
||||||
@ -166,7 +165,7 @@ class SalesInvoice(SellingController):
|
|||||||
if self.update_stock:
|
if self.update_stock:
|
||||||
frappe.throw(_("'Update Stock' cannot be checked for fixed asset sale"))
|
frappe.throw(_("'Update Stock' cannot be checked for fixed asset sale"))
|
||||||
|
|
||||||
elif asset.status in ("Scrapped", "Cancelled") or (asset.status == "Sold" and not self.is_return):
|
elif asset.status in ("Scrapped", "Cancelled", "Capitalized", "Decapitalized") or (asset.status == "Sold" and not self.is_return):
|
||||||
frappe.throw(_("Row #{0}: Asset {1} cannot be submitted, it is already {2}").format(d.idx, d.asset, asset.status))
|
frappe.throw(_("Row #{0}: Asset {1} cannot be submitted, it is already {2}").format(d.idx, d.asset, asset.status))
|
||||||
|
|
||||||
def validate_item_cost_centers(self):
|
def validate_item_cost_centers(self):
|
||||||
@ -1007,77 +1006,6 @@ class SalesInvoice(SellingController):
|
|||||||
self.check_finance_books(item, asset)
|
self.check_finance_books(item, asset)
|
||||||
return asset
|
return asset
|
||||||
|
|
||||||
def check_finance_books(self, item, asset):
|
|
||||||
if (len(asset.finance_books) > 1 and not item.finance_book
|
|
||||||
and asset.finance_books[0].finance_book):
|
|
||||||
frappe.throw(_("Select finance book for the item {0} at row {1}")
|
|
||||||
.format(item.item_code, item.idx))
|
|
||||||
|
|
||||||
def depreciate_asset(self, asset):
|
|
||||||
asset.flags.ignore_validate_update_after_submit = True
|
|
||||||
asset.prepare_depreciation_data(self.posting_date)
|
|
||||||
asset.save()
|
|
||||||
|
|
||||||
post_depreciation_entries(self.posting_date)
|
|
||||||
|
|
||||||
def reset_depreciation_schedule(self, asset):
|
|
||||||
asset.flags.ignore_validate_update_after_submit = True
|
|
||||||
|
|
||||||
# recreate original depreciation schedule of the asset
|
|
||||||
asset.prepare_depreciation_data()
|
|
||||||
|
|
||||||
self.modify_depreciation_schedule_for_asset_repairs(asset)
|
|
||||||
asset.save()
|
|
||||||
|
|
||||||
self.delete_depreciation_entry_made_after_sale(asset)
|
|
||||||
|
|
||||||
def modify_depreciation_schedule_for_asset_repairs(self, asset):
|
|
||||||
asset_repairs = frappe.get_all(
|
|
||||||
'Asset Repair',
|
|
||||||
filters = {'asset': asset.name},
|
|
||||||
fields = ['name', 'increase_in_asset_life']
|
|
||||||
)
|
|
||||||
|
|
||||||
for repair in asset_repairs:
|
|
||||||
if repair.increase_in_asset_life:
|
|
||||||
asset_repair = frappe.get_doc('Asset Repair', repair.name)
|
|
||||||
asset_repair.modify_depreciation_schedule()
|
|
||||||
asset.prepare_depreciation_data()
|
|
||||||
|
|
||||||
def delete_depreciation_entry_made_after_sale(self, asset):
|
|
||||||
from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry
|
|
||||||
|
|
||||||
posting_date_of_original_invoice = self.get_posting_date_of_sales_invoice()
|
|
||||||
|
|
||||||
row = -1
|
|
||||||
finance_book = asset.get('schedules')[0].get('finance_book')
|
|
||||||
for schedule in asset.get('schedules'):
|
|
||||||
if schedule.finance_book != finance_book:
|
|
||||||
row = 0
|
|
||||||
finance_book = schedule.finance_book
|
|
||||||
else:
|
|
||||||
row += 1
|
|
||||||
|
|
||||||
if schedule.schedule_date == posting_date_of_original_invoice:
|
|
||||||
if not self.sale_was_made_on_original_schedule_date(asset, schedule, row, posting_date_of_original_invoice):
|
|
||||||
reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry)
|
|
||||||
reverse_journal_entry.posting_date = nowdate()
|
|
||||||
reverse_journal_entry.submit()
|
|
||||||
|
|
||||||
def get_posting_date_of_sales_invoice(self):
|
|
||||||
return frappe.db.get_value('Sales Invoice', self.return_against, 'posting_date')
|
|
||||||
|
|
||||||
# if the invoice had been posted on the date the depreciation was initially supposed to happen, the depreciation shouldn't be undone
|
|
||||||
def sale_was_made_on_original_schedule_date(self, asset, schedule, row, posting_date_of_original_invoice):
|
|
||||||
for finance_book in asset.get('finance_books'):
|
|
||||||
if schedule.finance_book == finance_book.finance_book:
|
|
||||||
orginal_schedule_date = add_months(finance_book.depreciation_start_date,
|
|
||||||
row * cint(finance_book.frequency_of_depreciation))
|
|
||||||
|
|
||||||
if orginal_schedule_date == posting_date_of_original_invoice:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def enable_discount_accounting(self):
|
def enable_discount_accounting(self):
|
||||||
if not hasattr(self, "_enable_discount_accounting"):
|
if not hasattr(self, "_enable_discount_accounting"):
|
||||||
|
@ -10,6 +10,9 @@ frappe.listview_settings['Asset'] = {
|
|||||||
} else if (doc.status === "Sold") {
|
} else if (doc.status === "Sold") {
|
||||||
return [__("Sold"), "green", "status,=,Sold"];
|
return [__("Sold"), "green", "status,=,Sold"];
|
||||||
|
|
||||||
|
} else if (["Capitalized", "Decapitalized"].includes(doc.status)) {
|
||||||
|
return [__(doc.status), "grey", "status,=," + doc.status];
|
||||||
|
|
||||||
} else if (doc.status === "Scrapped") {
|
} else if (doc.status === "Scrapped") {
|
||||||
return [__("Scrapped"), "grey", "status,=,Scrapped"];
|
return [__("Scrapped"), "grey", "status,=,Scrapped"];
|
||||||
|
|
||||||
|
@ -13,7 +13,7 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def post_depreciation_entries(date=None):
|
def post_depreciation_entries(date=None, commit=True):
|
||||||
# Return if automatic booking of asset depreciation is disabled
|
# Return if automatic booking of asset depreciation is disabled
|
||||||
if not cint(frappe.db.get_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically")):
|
if not cint(frappe.db.get_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically")):
|
||||||
return
|
return
|
||||||
@ -22,7 +22,8 @@ def post_depreciation_entries(date=None):
|
|||||||
date = today()
|
date = today()
|
||||||
for asset in get_depreciable_assets(date):
|
for asset in get_depreciable_assets(date):
|
||||||
make_depreciation_entry(asset, date)
|
make_depreciation_entry(asset, date)
|
||||||
frappe.db.commit()
|
if commit:
|
||||||
|
frappe.db.commit()
|
||||||
|
|
||||||
def get_depreciable_assets(date):
|
def get_depreciable_assets(date):
|
||||||
return frappe.db.sql_list("""select a.name
|
return frappe.db.sql_list("""select a.name
|
||||||
@ -140,7 +141,7 @@ def scrap_asset(asset_name):
|
|||||||
|
|
||||||
if asset.docstatus != 1:
|
if asset.docstatus != 1:
|
||||||
frappe.throw(_("Asset {0} must be submitted").format(asset.name))
|
frappe.throw(_("Asset {0} must be submitted").format(asset.name))
|
||||||
elif asset.status in ("Cancelled", "Sold", "Scrapped"):
|
elif asset.status in ("Cancelled", "Sold", "Scrapped", "Capitalized", "Decapitalized"):
|
||||||
frappe.throw(_("Asset {0} cannot be scrapped, as it is already {1}").format(asset.name, asset.status))
|
frappe.throw(_("Asset {0} cannot be scrapped, as it is already {1}").format(asset.name, asset.status))
|
||||||
|
|
||||||
depreciation_series = frappe.get_cached_value('Company', asset.company, "series_for_depreciation_entry")
|
depreciation_series = frappe.get_cached_value('Company', asset.company, "series_for_depreciation_entry")
|
||||||
@ -269,3 +270,25 @@ def get_disposal_account_and_cost_center(company):
|
|||||||
frappe.throw(_("Please set 'Asset Depreciation Cost Center' in Company {0}").format(company))
|
frappe.throw(_("Please set 'Asset Depreciation Cost Center' in Company {0}").format(company))
|
||||||
|
|
||||||
return disposal_account, depreciation_cost_center
|
return disposal_account, depreciation_cost_center
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_value_after_depreciation_on_disposal_date(asset, disposal_date, finance_book=None):
|
||||||
|
asset_doc = frappe.get_doc("Asset", asset)
|
||||||
|
|
||||||
|
if asset_doc.calculate_depreciation:
|
||||||
|
asset_doc.prepare_depreciation_data(getdate(disposal_date))
|
||||||
|
|
||||||
|
finance_book_id = 1
|
||||||
|
if finance_book:
|
||||||
|
for fb in asset_doc.finance_books:
|
||||||
|
if fb.finance_book == finance_book:
|
||||||
|
finance_book_id = fb.idx
|
||||||
|
break
|
||||||
|
|
||||||
|
asset_schedules = [sch for sch in asset_doc.schedules if cint(sch.finance_book_id) == finance_book_id]
|
||||||
|
accumulated_depr_amount = asset_schedules[-1].accumulated_depreciation_amount
|
||||||
|
|
||||||
|
return flt(flt(asset_doc.gross_purchase_amount) - accumulated_depr_amount, asset_doc.precision('gross_purchase_amount'))
|
||||||
|
else:
|
||||||
|
return flt(asset_doc.value_after_depreciation)
|
||||||
|
@ -15,6 +15,10 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s
|
|||||||
|
|
||||||
refresh() {
|
refresh() {
|
||||||
erpnext.hide_company();
|
erpnext.hide_company();
|
||||||
|
this.show_general_ledger();
|
||||||
|
if (this.frm.doc.stock_items || !this.frm.doc.target_is_fixed_asset) {
|
||||||
|
this.show_stock_ledger();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setup_queries() {
|
setup_queries() {
|
||||||
@ -33,7 +37,7 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s
|
|||||||
filters['item_code'] = me.frm.doc.target_item_code;
|
filters['item_code'] = me.frm.doc.target_item_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
filters['status'] = ["not in", ["Draft", "Scrapped", "Sold"]]
|
filters['status'] = ["not in", ["Draft", "Scrapped", "Sold", "Capitalized", "Decapitalized"]]
|
||||||
filters['docstatus'] = 1;
|
filters['docstatus'] = 1;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -43,7 +47,7 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s
|
|||||||
|
|
||||||
me.frm.set_query("asset", "asset_items", function() {
|
me.frm.set_query("asset", "asset_items", function() {
|
||||||
var filters = {
|
var filters = {
|
||||||
'status': ["not in", ["Draft", "Scrapped", "Sold"]],
|
'status': ["not in", ["Draft", "Scrapped", "Sold", "Capitalized", "Decapitalized"]],
|
||||||
'docstatus': 1
|
'docstatus': 1
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -127,6 +131,7 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s
|
|||||||
posting_date() {
|
posting_date() {
|
||||||
if (this.frm.doc.posting_date) {
|
if (this.frm.doc.posting_date) {
|
||||||
this.get_all_item_warehouse_details();
|
this.get_all_item_warehouse_details();
|
||||||
|
this.get_all_asset_values();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,8 +2,9 @@
|
|||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
|
# import erpnext
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from erpnext.controllers.accounts_controller import AccountsController
|
from erpnext.controllers.stock_controller import StockController
|
||||||
from frappe.utils import cint, flt
|
from frappe.utils import cint, flt
|
||||||
from erpnext.stock.get_item_details import get_item_warehouse, get_default_expense_account, get_default_cost_center
|
from erpnext.stock.get_item_details import get_item_warehouse, get_default_expense_account, get_default_cost_center
|
||||||
from erpnext.stock.doctype.item.item import get_item_defaults
|
from erpnext.stock.doctype.item.item import get_item_defaults
|
||||||
@ -13,6 +14,9 @@ from erpnext.stock.utils import get_incoming_rate
|
|||||||
from erpnext.stock.stock_ledger import get_previous_sle
|
from erpnext.stock.stock_ledger import get_previous_sle
|
||||||
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
|
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
|
||||||
from erpnext.assets.doctype.asset_value_adjustment.asset_value_adjustment import get_current_asset_value
|
from erpnext.assets.doctype.asset_value_adjustment.asset_value_adjustment import get_current_asset_value
|
||||||
|
from erpnext.stock import get_warehouse_account_map
|
||||||
|
from erpnext.assets.doctype.asset.depreciation import get_gl_entries_on_asset_disposal, get_gl_entries_on_asset_regain,\
|
||||||
|
get_value_after_depreciation_on_disposal_date
|
||||||
from six import string_types
|
from six import string_types
|
||||||
import json
|
import json
|
||||||
|
|
||||||
@ -21,7 +25,7 @@ force_fields = ['target_item_name', 'target_asset_name', 'item_name', 'asset_nam
|
|||||||
'target_stock_uom', 'stock_uom', 'target_fixed_asset_account', 'fixed_asset_account']
|
'target_stock_uom', 'stock_uom', 'target_fixed_asset_account', 'fixed_asset_account']
|
||||||
|
|
||||||
|
|
||||||
class AssetCapitalization(AccountsController):
|
class AssetCapitalization(StockController):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_posting_time()
|
self.validate_posting_time()
|
||||||
self.set_missing_values(for_validate=True)
|
self.set_missing_values(for_validate=True)
|
||||||
@ -36,6 +40,18 @@ class AssetCapitalization(AccountsController):
|
|||||||
self.calculate_totals()
|
self.calculate_totals()
|
||||||
self.set_title()
|
self.set_title()
|
||||||
|
|
||||||
|
def before_submit(self):
|
||||||
|
self.validate_source_mandatory()
|
||||||
|
|
||||||
|
def on_submit(self):
|
||||||
|
self.update_stock_ledger()
|
||||||
|
self.make_gl_entries()
|
||||||
|
|
||||||
|
def on_cancel(self):
|
||||||
|
self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry', 'Repost Item Valuation')
|
||||||
|
self.update_stock_ledger()
|
||||||
|
self.make_gl_entries()
|
||||||
|
|
||||||
def set_entry_type(self):
|
def set_entry_type(self):
|
||||||
self.entry_type = "Capitalization" if self.target_is_fixed_asset else "Decapitalization"
|
self.entry_type = "Capitalization" if self.target_is_fixed_asset else "Decapitalization"
|
||||||
|
|
||||||
@ -104,11 +120,15 @@ class AssetCapitalization(AccountsController):
|
|||||||
self.target_warehouse = None
|
self.target_warehouse = None
|
||||||
if not target_item.is_fixed_asset:
|
if not target_item.is_fixed_asset:
|
||||||
self.target_asset = None
|
self.target_asset = None
|
||||||
|
self.target_fixed_asset_account = None
|
||||||
if not target_item.has_batch_no:
|
if not target_item.has_batch_no:
|
||||||
self.target_batch_no = None
|
self.target_batch_no = None
|
||||||
if not target_item.has_serial_no:
|
if not target_item.has_serial_no:
|
||||||
self.target_serial_no = ""
|
self.target_serial_no = ""
|
||||||
|
|
||||||
|
if target_item.is_stock_item and not self.target_warehouse:
|
||||||
|
frappe.throw(_("Target Warehouse is mandatory for Decapitalization"))
|
||||||
|
|
||||||
self.validate_item(target_item)
|
self.validate_item(target_item)
|
||||||
|
|
||||||
def validate_target_asset(self):
|
def validate_target_asset(self):
|
||||||
@ -165,6 +185,13 @@ class AssetCapitalization(AccountsController):
|
|||||||
if not d.cost_center:
|
if not d.cost_center:
|
||||||
d.cost_center = frappe.get_cached_value("Company", self.company, "cost_center")
|
d.cost_center = frappe.get_cached_value("Company", self.company, "cost_center")
|
||||||
|
|
||||||
|
def validate_source_mandatory(self):
|
||||||
|
if not self.target_is_fixed_asset and not self.get('asset_items'):
|
||||||
|
frappe.throw(_("Consumed Asset Items is mandatory for Decapitalization"))
|
||||||
|
|
||||||
|
if not self.get('stock_items') and not self.get('asset_items'):
|
||||||
|
frappe.throw(_("Consumed Stock Items or Consumed Asset Items is mandatory for Capitalization"))
|
||||||
|
|
||||||
def validate_item(self, item):
|
def validate_item(self, item):
|
||||||
from erpnext.stock.doctype.item.item import validate_end_of_life
|
from erpnext.stock.doctype.item.item import validate_end_of_life
|
||||||
validate_end_of_life(item.name, item.end_of_life, item.disabled)
|
validate_end_of_life(item.name, item.end_of_life, item.disabled)
|
||||||
@ -173,7 +200,7 @@ class AssetCapitalization(AccountsController):
|
|||||||
return frappe.db.get_value("Asset", asset, ["name", "item_code", "company", "status", "docstatus"], as_dict=1)
|
return frappe.db.get_value("Asset", asset, ["name", "item_code", "company", "status", "docstatus"], as_dict=1)
|
||||||
|
|
||||||
def validate_asset(self, asset):
|
def validate_asset(self, asset):
|
||||||
if asset.status in ("Draft", "Scrapped", "Sold"):
|
if asset.status in ("Draft", "Scrapped", "Sold", "Capitalized", "Decapitalized"):
|
||||||
frappe.throw(_("Asset {0} is {1}").format(asset.name, asset.status))
|
frappe.throw(_("Asset {0} is {1}").format(asset.name, asset.status))
|
||||||
|
|
||||||
if asset.docstatus == 0:
|
if asset.docstatus == 0:
|
||||||
@ -196,7 +223,10 @@ class AssetCapitalization(AccountsController):
|
|||||||
def set_asset_values(self):
|
def set_asset_values(self):
|
||||||
for d in self.asset_items:
|
for d in self.asset_items:
|
||||||
if d.asset:
|
if d.asset:
|
||||||
d.asset_value = flt(get_current_asset_value(d.asset, d.get('finance_book') or self.finance_book))
|
finance_book = d.get('finance_book') or self.get('finance_book')
|
||||||
|
d.current_asset_value = flt(get_current_asset_value(d.asset, finance_book=finance_book))
|
||||||
|
d.asset_value = get_value_after_depreciation_on_disposal_date(d.asset, self.posting_date,
|
||||||
|
finance_book=finance_book)
|
||||||
|
|
||||||
def get_args_for_incoming_rate(self, item):
|
def get_args_for_incoming_rate(self, item):
|
||||||
return frappe._dict({
|
return frappe._dict({
|
||||||
@ -240,6 +270,180 @@ class AssetCapitalization(AccountsController):
|
|||||||
self.target_qty = flt(self.target_qty, self.precision('target_qty'))
|
self.target_qty = flt(self.target_qty, self.precision('target_qty'))
|
||||||
self.target_incoming_rate = self.total_value / self.target_qty
|
self.target_incoming_rate = self.total_value / self.target_qty
|
||||||
|
|
||||||
|
def update_stock_ledger(self):
|
||||||
|
sl_entries = []
|
||||||
|
|
||||||
|
for d in self.stock_items:
|
||||||
|
sle = self.get_sl_entries(d, {
|
||||||
|
"actual_qty": -flt(d.stock_qty),
|
||||||
|
})
|
||||||
|
sl_entries.append(sle)
|
||||||
|
|
||||||
|
if not frappe.db.get_value("Item", self.target_item_code, "is_fixed_asset", cache=1):
|
||||||
|
sle = self.get_sl_entries(self, {
|
||||||
|
"item_code": self.target_item_code,
|
||||||
|
"warehouse": self.target_warehouse,
|
||||||
|
"batch_no": self.target_batch_no,
|
||||||
|
"serial_no": self.target_serial_no,
|
||||||
|
"actual_qty": flt(self.target_qty),
|
||||||
|
"incoming_rate": flt(self.target_incoming_rate)
|
||||||
|
})
|
||||||
|
sl_entries.append(sle)
|
||||||
|
|
||||||
|
# reverse sl entries if cancel
|
||||||
|
if self.docstatus == 2:
|
||||||
|
sl_entries.reverse()
|
||||||
|
|
||||||
|
if sl_entries:
|
||||||
|
self.make_sl_entries(sl_entries)
|
||||||
|
|
||||||
|
def make_gl_entries(self, gl_entries=None, from_repost=False):
|
||||||
|
from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries
|
||||||
|
|
||||||
|
if not gl_entries:
|
||||||
|
gl_entries = self.get_gl_entries()
|
||||||
|
|
||||||
|
if self.docstatus == 1:
|
||||||
|
if gl_entries:
|
||||||
|
make_gl_entries(gl_entries, from_repost=from_repost)
|
||||||
|
elif self.docstatus == 2:
|
||||||
|
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
|
||||||
|
|
||||||
|
def get_gl_entries(self, warehouse_account=None, default_expense_account=None, default_cost_center=None):
|
||||||
|
# Stock GL Entries
|
||||||
|
gl_entries = []
|
||||||
|
|
||||||
|
if not warehouse_account:
|
||||||
|
warehouse_account = get_warehouse_account_map(self.company)
|
||||||
|
|
||||||
|
precision = self.get_debit_field_precision()
|
||||||
|
sle_map = self.get_stock_ledger_details()
|
||||||
|
|
||||||
|
if self.target_is_fixed_asset:
|
||||||
|
target_account = self.target_fixed_asset_account
|
||||||
|
else:
|
||||||
|
target_account = warehouse_account[self.target_warehouse]["account"]
|
||||||
|
|
||||||
|
target_against = set()
|
||||||
|
|
||||||
|
# Consumed Stock Items
|
||||||
|
total_consumed_stock_value = 0
|
||||||
|
for item_row in self.stock_items:
|
||||||
|
sle_list = sle_map.get(item_row.name)
|
||||||
|
if sle_list:
|
||||||
|
for sle in sle_list:
|
||||||
|
stock_value_difference = flt(sle.stock_value_difference, precision)
|
||||||
|
total_consumed_stock_value += -1 * sle.stock_value_difference
|
||||||
|
|
||||||
|
account = warehouse_account[sle.warehouse]["account"]
|
||||||
|
target_against.add(account)
|
||||||
|
|
||||||
|
gl_entries.append(self.get_gl_dict({
|
||||||
|
"account": account,
|
||||||
|
"against": target_account,
|
||||||
|
"cost_center": item_row.cost_center,
|
||||||
|
"project": item_row.get('project') or self.get('project'),
|
||||||
|
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
||||||
|
"credit": -1 * stock_value_difference,
|
||||||
|
}, warehouse_account[sle.warehouse]["account_currency"], item=item_row))
|
||||||
|
|
||||||
|
# Consumed Assets
|
||||||
|
for item in self.asset_items:
|
||||||
|
asset = self.get_asset(item)
|
||||||
|
|
||||||
|
if self.docstatus == 2:
|
||||||
|
fixed_asset_gl_entries = get_gl_entries_on_asset_regain(asset,
|
||||||
|
item.asset_value, item.get('finance_book') or self.get('finance_book'))
|
||||||
|
asset.db_set("disposal_date", None)
|
||||||
|
|
||||||
|
self.set_consumed_asset_status(asset)
|
||||||
|
|
||||||
|
if asset.calculate_depreciation:
|
||||||
|
self.reset_depreciation_schedule(asset)
|
||||||
|
else:
|
||||||
|
if asset.calculate_depreciation:
|
||||||
|
self.depreciate_asset(asset)
|
||||||
|
|
||||||
|
asset.reload()
|
||||||
|
fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(asset,
|
||||||
|
item.asset_value, item.get('finance_book') or self.get('finance_book'))
|
||||||
|
asset.db_set("disposal_date", self.posting_date)
|
||||||
|
|
||||||
|
self.set_consumed_asset_status(asset)
|
||||||
|
|
||||||
|
for gle in fixed_asset_gl_entries:
|
||||||
|
gle["against"] = target_account
|
||||||
|
gl_entries.append(self.get_gl_dict(gle, item=item))
|
||||||
|
|
||||||
|
# Service Expenses
|
||||||
|
total_service_expenses = 0
|
||||||
|
for item_row in self.service_items:
|
||||||
|
expense_amount = flt(item_row.amount, precision)
|
||||||
|
total_service_expenses += expense_amount
|
||||||
|
target_against.add(item_row.expense_account)
|
||||||
|
|
||||||
|
gl_entries.append(self.get_gl_dict({
|
||||||
|
"account": item_row.expense_account,
|
||||||
|
"against": target_account,
|
||||||
|
"cost_center": item_row.cost_center,
|
||||||
|
"project": item_row.get('project') or self.get('project'),
|
||||||
|
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
||||||
|
"credit": expense_amount,
|
||||||
|
}, item=item_row))
|
||||||
|
|
||||||
|
target_against = ", ".join(target_against)
|
||||||
|
total_target_stock_value = 0
|
||||||
|
total_target_asset_value = 0
|
||||||
|
|
||||||
|
if self.target_is_fixed_asset:
|
||||||
|
# Target Asset Item
|
||||||
|
total_target_asset_value = flt(self.total_value, precision)
|
||||||
|
gl_entries.append(self.get_gl_dict({
|
||||||
|
"account": self.target_fixed_asset_account,
|
||||||
|
"against": target_against,
|
||||||
|
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
||||||
|
"debit": total_target_asset_value,
|
||||||
|
"cost_center": self.get('cost_center')
|
||||||
|
}, item=self))
|
||||||
|
|
||||||
|
if self.docstatus == 1:
|
||||||
|
asset_doc = frappe.get_doc("Asset", self.target_asset)
|
||||||
|
asset_doc.purchase_date = self.posting_date
|
||||||
|
asset_doc.gross_purchase_amount = total_target_asset_value
|
||||||
|
asset_doc.purchase_receipt_amount = total_target_asset_value
|
||||||
|
asset_doc.prepare_depreciation_data()
|
||||||
|
asset_doc.flags.ignore_validate_update_after_submit = True
|
||||||
|
asset_doc.save()
|
||||||
|
else:
|
||||||
|
# Target Stock Item
|
||||||
|
sle_list = sle_map.get(self.name)
|
||||||
|
for sle in sle_list:
|
||||||
|
stock_value_difference = flt(sle.stock_value_difference, precision)
|
||||||
|
total_target_stock_value += sle.stock_value_difference
|
||||||
|
account = warehouse_account[sle.warehouse]["account"]
|
||||||
|
|
||||||
|
gl_entries.append(self.get_gl_dict({
|
||||||
|
"account": account,
|
||||||
|
"against": target_against,
|
||||||
|
"cost_center": self.cost_center,
|
||||||
|
"project": self.get('project'),
|
||||||
|
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
||||||
|
"debit": stock_value_difference,
|
||||||
|
}, warehouse_account[sle.warehouse]["account_currency"], item=self))
|
||||||
|
|
||||||
|
return gl_entries
|
||||||
|
|
||||||
|
def get_asset(self, item):
|
||||||
|
asset = frappe.get_doc("Asset", item.asset)
|
||||||
|
self.check_finance_books(item, asset)
|
||||||
|
return asset
|
||||||
|
|
||||||
|
def set_consumed_asset_status(self, asset):
|
||||||
|
if self.docstatus == 1:
|
||||||
|
asset.set_status("Capitalized" if self.target_is_fixed_asset else "Decapitalized")
|
||||||
|
else:
|
||||||
|
asset.set_status()
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_target_item_details(item_code=None, company=None):
|
def get_target_item_details(item_code=None, company=None):
|
||||||
@ -395,8 +599,11 @@ def get_consumed_asset_details(args, get_asset_value=True):
|
|||||||
|
|
||||||
if get_asset_value:
|
if get_asset_value:
|
||||||
if args.asset:
|
if args.asset:
|
||||||
out.asset_value = flt(get_current_asset_value(args.asset, finance_book=args.finance_book))
|
out.current_asset_value = flt(get_current_asset_value(args.asset, finance_book=args.finance_book))
|
||||||
|
out.asset_value = get_value_after_depreciation_on_disposal_date(args.asset, args.posting_date,
|
||||||
|
finance_book=args.finance_book)
|
||||||
else:
|
else:
|
||||||
|
out.current_asset_value = 0
|
||||||
out.asset_value = 0
|
out.asset_value = 0
|
||||||
|
|
||||||
# Account
|
# Account
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
"item_code",
|
"item_code",
|
||||||
"item_name",
|
"item_name",
|
||||||
"section_break_6",
|
"section_break_6",
|
||||||
|
"current_asset_value",
|
||||||
"asset_value",
|
"asset_value",
|
||||||
"column_break_9",
|
"column_break_9",
|
||||||
"accounting_dimensions_section",
|
"accounting_dimensions_section",
|
||||||
@ -102,12 +103,20 @@
|
|||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Finance Book",
|
"label": "Finance Book",
|
||||||
"options": "Finance Book"
|
"options": "Finance Book"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "current_asset_value",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Current Asset Value",
|
||||||
|
"options": "Company:company:default_currency",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-09-08 23:42:25.143272",
|
"modified": "2021-09-12 14:30:02.915132",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Assets",
|
"module": "Assets",
|
||||||
"name": "Asset Capitalization Asset Item",
|
"name": "Asset Capitalization Asset Item",
|
||||||
|
@ -54,6 +54,7 @@ from erpnext.stock.get_item_details import (
|
|||||||
get_item_tax_map,
|
get_item_tax_map,
|
||||||
get_item_warehouse,
|
get_item_warehouse,
|
||||||
)
|
)
|
||||||
|
from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries
|
||||||
from erpnext.utilities.transaction_base import TransactionBase
|
from erpnext.utilities.transaction_base import TransactionBase
|
||||||
|
|
||||||
|
|
||||||
@ -1457,6 +1458,86 @@ class AccountsController(TransactionBase):
|
|||||||
jv.save()
|
jv.save()
|
||||||
jv.submit()
|
jv.submit()
|
||||||
|
|
||||||
|
def check_finance_books(self, item, asset):
|
||||||
|
if (len(asset.finance_books) > 1 and not item.get('finance_book') and not self.get('finance_book')
|
||||||
|
and asset.finance_books[0].finance_book):
|
||||||
|
frappe.throw(_("Select finance book for the item {0} at row {1}")
|
||||||
|
.format(item.item_code, item.idx))
|
||||||
|
|
||||||
|
def depreciate_asset(self, asset):
|
||||||
|
asset.flags.ignore_validate_update_after_submit = True
|
||||||
|
asset.prepare_depreciation_data(self.posting_date)
|
||||||
|
asset.save()
|
||||||
|
|
||||||
|
post_depreciation_entries(self.posting_date, commit=False)
|
||||||
|
|
||||||
|
def reset_depreciation_schedule(self, asset):
|
||||||
|
asset.flags.ignore_validate_update_after_submit = True
|
||||||
|
|
||||||
|
# recreate original depreciation schedule of the asset
|
||||||
|
asset.prepare_depreciation_data()
|
||||||
|
|
||||||
|
self.modify_depreciation_schedule_for_asset_repairs(asset)
|
||||||
|
asset.save()
|
||||||
|
|
||||||
|
self.delete_depreciation_entry_made_after_disposal(asset)
|
||||||
|
|
||||||
|
def modify_depreciation_schedule_for_asset_repairs(self, asset):
|
||||||
|
asset_repairs = frappe.get_all(
|
||||||
|
'Asset Repair',
|
||||||
|
filters={'asset': asset.name},
|
||||||
|
fields=['name', 'increase_in_asset_life']
|
||||||
|
)
|
||||||
|
|
||||||
|
for repair in asset_repairs:
|
||||||
|
if repair.increase_in_asset_life:
|
||||||
|
asset_repair = frappe.get_doc('Asset Repair', repair.name)
|
||||||
|
asset_repair.modify_depreciation_schedule()
|
||||||
|
asset.prepare_depreciation_data()
|
||||||
|
|
||||||
|
def delete_depreciation_entry_made_after_disposal(self, asset):
|
||||||
|
from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry
|
||||||
|
|
||||||
|
posting_date_of_original_invoice = self.get_posting_date_of_disposal_entry()
|
||||||
|
|
||||||
|
row = -1
|
||||||
|
finance_book = asset.get('schedules')[0].get('finance_book')
|
||||||
|
for schedule in asset.get('schedules'):
|
||||||
|
if schedule.finance_book != finance_book:
|
||||||
|
row = 0
|
||||||
|
finance_book = schedule.finance_book
|
||||||
|
else:
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
if schedule.schedule_date == posting_date_of_original_invoice:
|
||||||
|
if not self.disposal_was_made_on_original_schedule_date(asset, schedule, row,
|
||||||
|
posting_date_of_original_invoice):
|
||||||
|
reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry)
|
||||||
|
reverse_journal_entry.posting_date = nowdate()
|
||||||
|
|
||||||
|
for d in reverse_journal_entry.accounts:
|
||||||
|
d.reference_type = "Asset"
|
||||||
|
d.reference_name = asset.name
|
||||||
|
|
||||||
|
reverse_journal_entry.submit()
|
||||||
|
|
||||||
|
def get_posting_date_of_disposal_entry(self):
|
||||||
|
if self.doctype == "Sales Invoice" and self.return_against:
|
||||||
|
return frappe.db.get_value('Sales Invoice', self.return_against, 'posting_date')
|
||||||
|
else:
|
||||||
|
return self.posting_date
|
||||||
|
|
||||||
|
# if the invoice had been posted on the date the depreciation was initially supposed to happen, the depreciation shouldn't be undone
|
||||||
|
def disposal_was_made_on_original_schedule_date(self, asset, schedule, row, posting_date_of_original_disposal):
|
||||||
|
for finance_book in asset.get('finance_books'):
|
||||||
|
if schedule.finance_book == finance_book.finance_book:
|
||||||
|
orginal_schedule_date = add_months(finance_book.depreciation_start_date,
|
||||||
|
row * cint(finance_book.frequency_of_depreciation))
|
||||||
|
|
||||||
|
if orginal_schedule_date == posting_date_of_original_disposal:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_tax_rate(account_head):
|
def get_tax_rate(account_head):
|
||||||
return frappe.db.get_value("Account", account_head, ["tax_rate", "account_name"], as_dict=True)
|
return frappe.db.get_value("Account", account_head, ["tax_rate", "account_name"], as_dict=True)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user