feat(Asset Capitalization): Submission and Cancellation

This commit is contained in:
Saif Ur Rehman 2021-09-13 23:01:52 +05:00
parent 3b9bc8e4ef
commit 7a5d75b68d
7 changed files with 340 additions and 84 deletions

View File

@ -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"):

View File

@ -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"];

View File

@ -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)

View File

@ -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();
} }
} }

View File

@ -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

View File

@ -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",

View File

@ -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)