chore: rebase from develop

This commit is contained in:
Khushi Rawat
2025-01-14 01:46:08 +05:30
parent 44e45b55d4
commit 5f21d7ea1d
20 changed files with 1072 additions and 1297 deletions

View File

@@ -89,7 +89,7 @@
"label": "Entry Type",
"oldfieldname": "voucher_type",
"oldfieldtype": "Select",
"options": "Journal Entry\nInter Company Journal Entry\nBank Entry\nCash Entry\nCredit Card Entry\nDebit Note\nCredit Note\nContra Entry\nExcise Entry\nWrite Off Entry\nOpening Entry\nDepreciation Entry\nExchange Rate Revaluation\nExchange Gain Or Loss\nDeferred Revenue\nDeferred Expense",
"options": "Journal Entry\nInter Company Journal Entry\nBank Entry\nCash Entry\nCredit Card Entry\nDebit Note\nCredit Note\nContra Entry\nExcise Entry\nWrite Off Entry\nOpening Entry\nDepreciation Entry\nAsset Disposal\nExchange Rate Revaluation\nExchange Gain Or Loss\nDeferred Revenue\nDeferred Expense",
"reqd": 1,
"search_index": 1
},
@@ -557,7 +557,7 @@
"table_fieldname": "payment_entries"
}
],
"modified": "2024-07-18 15:32:29.413598",
"modified": "2024-12-26 15:32:20.730666",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry",

View File

@@ -100,6 +100,7 @@ class JournalEntry(AccountsController):
"Write Off Entry",
"Opening Entry",
"Depreciation Entry",
"Asset Disposal",
"Exchange Rate Revaluation",
"Exchange Gain Or Loss",
"Deferred Revenue",
@@ -377,7 +378,11 @@ class JournalEntry(AccountsController):
self.remove(d)
def update_asset_value(self):
if self.flags.planned_depr_entry or self.voucher_type != "Depreciation Entry":
self.update_asset_on_depreciation()
self.update_asset_on_disposal()
def update_asset_on_depreciation(self):
if self.voucher_type != "Depreciation Entry":
return
for d in self.get("accounts"):
@@ -387,24 +392,61 @@ class JournalEntry(AccountsController):
and d.account_type == "Depreciation"
and d.debit
):
asset = frappe.get_doc("Asset", d.reference_name)
asset = frappe.get_cached_doc("Asset", d.reference_name)
if asset.calculate_depreciation:
fb_idx = 1
if self.finance_book:
for fb_row in asset.get("finance_books"):
if fb_row.finance_book == self.finance_book:
fb_idx = fb_row.idx
break
fb_row = asset.get("finance_books")[fb_idx - 1]
fb_row.value_after_depreciation -= d.debit
fb_row.db_update()
self.update_journal_entry_link_on_depr_schedule(asset, d)
self.update_value_after_depreciation(asset, d.debit)
else:
asset.db_set("value_after_depreciation", asset.value_after_depreciation - d.debit)
asset.set_status()
asset.set_total_booked_depreciations()
def update_value_after_depreciation(self, asset, depr_amount):
fb_idx = 1
if self.finance_book:
for fb_row in asset.get("finance_books"):
if fb_row.finance_book == self.finance_book:
fb_idx = fb_row.idx
break
fb_row = asset.get("finance_books")[fb_idx - 1]
fb_row.value_after_depreciation -= depr_amount
frappe.db.set_value(
"Asset Finance Book", fb_row.name, "value_after_depreciation", fb_row.value_after_depreciation
)
def update_journal_entry_link_on_depr_schedule(self, asset, je_row):
depr_schedule = get_depr_schedule(asset.name, "Active", self.finance_book)
for d in depr_schedule or []:
if (
d.schedule_date == self.posting_date
and not d.journal_entry
and d.depreciation_amount == flt(je_row.debit)
):
frappe.db.set_value("Depreciation Schedule", d.name, "journal_entry", self.name)
def update_asset_on_disposal(self):
if self.voucher_type == "Asset Disposal":
disposed_assets = []
for d in self.get("accounts"):
if (
d.reference_type == "Asset"
and d.reference_name
and d.reference_name not in disposed_assets
):
frappe.db.set_value(
"Asset",
d.reference_name,
{
"disposal_date": self.posting_date,
"journal_entry_for_scrap": self.name,
},
)
asset_doc = frappe.get_doc("Asset", d.reference_name)
asset_doc.set_status()
disposed_assets.append(d.reference_name)
def update_inter_company_jv(self):
if self.voucher_type == "Inter Company Journal Entry" and self.inter_company_journal_entry_reference:
frappe.db.set_value(

View File

@@ -39,7 +39,7 @@ from erpnext.assets.doctype.asset.depreciation import (
get_gl_entries_on_asset_disposal,
get_gl_entries_on_asset_regain,
reset_depreciation_schedule,
reverse_depreciation_entry_made_after_disposal,
reverse_depreciation_entry_made_on_disposal,
)
from erpnext.assets.doctype.asset_activity.asset_activity import add_asset_activity
from erpnext.controllers.accounts_controller import validate_account_head
@@ -368,21 +368,34 @@ class SalesInvoice(SellingController):
validate_docs_for_deferred_accounting([self.name], [])
def validate_fixed_asset(self):
for d in self.get("items"):
if d.is_fixed_asset and d.meta.get_field("asset") and d.asset:
asset = frappe.get_doc("Asset", d.asset)
if self.doctype == "Sales Invoice" and self.docstatus == 1:
if self.update_stock:
frappe.throw(_("'Update Stock' cannot be checked for fixed asset sale"))
if self.doctype != "Sales Invoice":
return
elif asset.status in ("Scrapped", "Cancelled", "Capitalized") 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
for d in self.get("items"):
if d.is_fixed_asset:
if d.asset:
if not self.is_return:
asset_status = frappe.db.get_value("Asset", d.asset, "status")
if self.update_stock:
frappe.throw(_("'Update Stock' cannot be checked for fixed asset sale"))
elif asset_status in ("Scrapped", "Cancelled", "Capitalized"):
frappe.throw(
_("Row #{0}: Asset {1} cannot be sold, it is already {2}").format(
d.idx, d.asset, asset_status
)
)
elif asset_status == "Sold" and not self.is_return:
frappe.throw(_("Row #{0}: Asset {1} is already sold").format(d.idx, d.asset))
elif not self.return_against:
frappe.throw(
_("Row #{0}: Return Against is required for returning asset").format(d.idx)
)
else:
frappe.throw(
_("Row #{0}: You must select an Asset for Item {1}.").format(d.idx, d.item_code),
title=_("Missing Asset"),
)
def validate_item_cost_centers(self):
for item in self.items:
@@ -464,6 +477,8 @@ class SalesInvoice(SellingController):
self.update_stock_reservation_entries()
self.update_stock_ledger()
self.process_asset_depreciation()
# this sequence because outstanding may get -ve
self.make_gl_entries()
@@ -583,6 +598,8 @@ class SalesInvoice(SellingController):
if self.update_stock == 1:
self.update_stock_ledger()
self.process_asset_depreciation()
self.make_gl_entries_on_cancel()
if self.update_stock == 1:
@@ -1253,6 +1270,90 @@ class SalesInvoice(SellingController):
):
throw(_("Delivery Note {0} is not submitted").format(d.delivery_note))
def process_asset_depreciation(self):
if (self.is_return and self.docstatus == 2) or (not self.is_return and self.docstatus == 1):
self.depreciate_asset_on_sale()
else:
self.restore_asset()
self.update_asset()
def depreciate_asset_on_sale(self):
"""
Depreciate asset on sale or cancellation of return sales invoice
"""
disposal_date = self.get_disposal_date()
for d in self.get("items"):
if d.asset:
asset = frappe.get_doc("Asset", d.asset)
if asset.calculate_depreciation and asset.status != "Fully Depreciated":
depreciate_asset(asset, disposal_date, self.get_note_for_asset_sale(asset))
def get_note_for_asset_sale(self, asset):
return _("This schedule was created when Asset {0} was {1} through Sales Invoice {2}.").format(
get_link_to_form(asset.doctype, asset.name),
_("returned") if self.is_return else _("sold"),
get_link_to_form(self.doctype, self.get("name")),
)
def restore_asset(self):
"""
Restore asset on return or cancellation of original sales invoice
"""
for d in self.get("items"):
if d.asset:
asset = frappe.get_cached_doc("Asset", d.asset)
if asset.calculate_depreciation:
reverse_depreciation_entry_made_on_disposal(asset)
note = self.get_note_for_asset_return(asset)
reset_depreciation_schedule(asset, note)
def get_note_for_asset_return(self, asset):
asset_link = get_link_to_form(asset.doctype, asset.name)
invoice_link = get_link_to_form(self.doctype, self.get("name"))
if self.is_return:
return _(
"This schedule was created when Asset {0} was returned through Sales Invoice {1}."
).format(asset_link, invoice_link)
else:
return _(
"This schedule was created when Asset {0} was restored due to Sales Invoice {1} cancellation."
).format(asset_link, invoice_link)
def update_asset(self):
"""
Update asset status, disposal date and asset activity on sale or return sales invoice
"""
def _update_asset(asset, disposal_date, note, asset_status=None):
frappe.db.set_value("Asset", d.asset, "disposal_date", disposal_date)
add_asset_activity(asset.name, note)
asset.set_status(asset_status)
disposal_date = self.get_disposal_date()
for d in self.get("items"):
if d.asset:
asset = frappe.get_cached_doc("Asset", d.asset)
if (self.is_return and self.docstatus == 1) or (not self.is_return and self.docstatus == 2):
note = _("Asset returned") if self.is_return else _("Asset sold")
asset_status, disposal_date = None, None
else:
note = _("Asset sold") if not self.is_return else _("Return invoice of asset cancelled")
asset_status = "Sold"
_update_asset(asset, disposal_date, note, asset_status)
def get_disposal_date(self):
if self.is_return:
disposal_date = frappe.db.get_value("Sales Invoice", self.return_against, "posting_date")
else:
disposal_date = self.posting_date
return disposal_date
def make_gl_entries(self, gl_entries=None, from_repost=False):
from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries
@@ -1426,64 +1527,8 @@ class SalesInvoice(SellingController):
if self.is_internal_transfer():
continue
if item.is_fixed_asset:
asset = self.get_asset(item)
if self.is_return:
fixed_asset_gl_entries = get_gl_entries_on_asset_regain(
asset,
item.base_net_amount,
item.finance_book,
self.get("doctype"),
self.get("name"),
self.get("posting_date"),
)
asset.db_set("disposal_date", None)
add_asset_activity(asset.name, _("Asset returned"))
if asset.calculate_depreciation:
posting_date = frappe.db.get_value(
"Sales Invoice", self.return_against, "posting_date"
)
reverse_depreciation_entry_made_after_disposal(asset, posting_date)
notes = _(
"This schedule was created when Asset {0} was returned through Sales Invoice {1}."
).format(
get_link_to_form(asset.doctype, asset.name),
get_link_to_form(self.doctype, self.get("name")),
)
reset_depreciation_schedule(asset, self.posting_date, notes)
asset.reload()
else:
if asset.calculate_depreciation:
if not asset.status == "Fully Depreciated":
notes = _(
"This schedule was created when Asset {0} was sold through Sales Invoice {1}."
).format(
get_link_to_form(asset.doctype, asset.name),
get_link_to_form(self.doctype, self.get("name")),
)
depreciate_asset(asset, self.posting_date, notes)
asset.reload()
fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(
asset,
item.base_net_amount,
item.finance_book,
self.get("doctype"),
self.get("name"),
self.get("posting_date"),
)
asset.db_set("disposal_date", self.posting_date)
add_asset_activity(asset.name, _("Asset sold"))
for gle in fixed_asset_gl_entries:
gle["against"] = self.customer
gl_entries.append(self.get_gl_dict(gle, item=item))
self.set_asset_status(asset)
if item.is_fixed_asset and item.asset:
self.get_gl_entries_for_fixed_asset(item, gl_entries)
else:
income_account = (
item.income_account
@@ -1518,17 +1563,31 @@ class SalesInvoice(SellingController):
if cint(self.update_stock) and erpnext.is_perpetual_inventory_enabled(self.company):
gl_entries += super().get_gl_entries()
def get_asset(self, item):
if item.get("asset"):
asset = frappe.get_doc("Asset", item.asset)
def get_gl_entries_for_fixed_asset(self, item, gl_entries):
asset = frappe.get_cached_doc("Asset", item.asset)
if self.is_return:
fixed_asset_gl_entries = get_gl_entries_on_asset_regain(
asset,
item.base_net_amount,
item.finance_book,
self.get("doctype"),
self.get("name"),
self.get("posting_date"),
)
else:
frappe.throw(
_("Row #{0}: You must select an Asset for Item {1}.").format(item.idx, item.item_name),
title=_("Missing Asset"),
fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(
asset,
item.base_net_amount,
item.finance_book,
self.get("doctype"),
self.get("name"),
self.get("posting_date"),
)
self.check_finance_books(item, asset)
return asset
for gle in fixed_asset_gl_entries:
gle["against"] = self.customer
gl_entries.append(self.get_gl_dict(gle, item=item))
@property
def enable_discount_accounting(self):
@@ -1539,12 +1598,6 @@ class SalesInvoice(SellingController):
return self._enable_discount_accounting
def set_asset_status(self, asset):
if self.is_return:
asset.set_status()
else:
asset.set_status("Sold" if self.docstatus == 1 else None)
def make_loyalty_point_redemption_gle(self, gl_entries):
if cint(self.redeem_loyalty_points and self.loyalty_points and not self.is_consolidated):
gl_entries.append(

View File

@@ -103,14 +103,26 @@ frappe.ui.form.on("Asset", {
},
__("Manage")
);
} else if (frm.doc.status == "Scrapped") {
frm.add_custom_button(
__("Restore Asset"),
__("Repair Asset"),
function () {
erpnext.asset.restore_asset(frm);
frm.trigger("create_asset_repair");
},
__("Manage")
);
frm.add_custom_button(
__("Split Asset"),
function () {
frm.trigger("split_asset");
},
__("Manage")
);
} else if (frm.doc.status == "Scrapped") {
frm.add_custom_button(__("Restore Asset"), function () {
erpnext.asset.restore_asset(frm);
}).addClass("btn-primary");
}
if (frm.doc.maintenance_required && !frm.doc.maintenance_schedule) {
@@ -123,23 +135,7 @@ frappe.ui.form.on("Asset", {
);
}
frm.add_custom_button(
__("Repair Asset"),
function () {
frm.trigger("create_asset_repair");
},
__("Manage")
);
frm.add_custom_button(
__("Split Asset"),
function () {
frm.trigger("split_asset");
},
__("Manage")
);
if (frm.doc.status != "Fully Depreciated") {
if (in_list(["Submitted", "Partially Depreciated"], frm.doc.status)) {
frm.add_custom_button(
__("Adjust Asset Value"),
function () {

View File

@@ -33,8 +33,6 @@ from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_sched
convert_draft_asset_depr_schedules_into_active,
get_asset_depr_schedule_doc,
get_depr_schedule,
make_draft_asset_depr_schedules,
update_draft_asset_depr_schedules,
)
from erpnext.controllers.accounts_controller import AccountsController
@@ -148,22 +146,23 @@ class Asset(AccountsController):
schedule_doc = get_asset_depr_schedule_doc(self.name, "Draft", row.finance_book)
if not schedule_doc:
schedule_doc = frappe.new_doc("Asset Depreciation Schedule")
schedule_doc.prepare_draft_asset_depr_schedule_data(self, row)
schedule_doc.asset = self.name
schedule_doc.create_depreciation_schedule(row)
schedule_doc.save()
schedules.append(schedule_doc.name)
self.show_schedule_creation_message(schedules)
def set_depr_rate_and_value_after_depreciation(self):
self.value_after_depreciation = flt(self.gross_purchase_amount) - flt(
self.opening_accumulated_depreciation
)
if self.calculate_depreciation:
self.value_after_depreciation = 0
self.set_depreciation_rate()
for d in self.finance_books:
d.value_after_depreciation = self.value_after_depreciation
else:
self.finance_books = []
self.value_after_depreciation = flt(self.gross_purchase_amount) - flt(
self.opening_accumulated_depreciation
)
def show_schedule_creation_message(self, schedules):
if schedules:
@@ -845,41 +844,31 @@ class Asset(AccountsController):
)
def get_written_down_value_rate(self, args, rate_field_precision, on_validate):
if (
args.get("rate_of_depreciation")
and on_validate
and not self.flags.increase_in_asset_value_due_to_repair
):
if args.get("rate_of_depreciation") and on_validate:
return args.get("rate_of_depreciation")
if args.get("rate_of_depreciation") and not flt(args.get("expected_value_after_useful_life")):
return args.get("rate_of_depreciation")
if self.flags.increase_in_asset_value_due_to_repair:
value = flt(args.get("expected_value_after_useful_life")) / flt(
args.get("value_after_depreciation")
)
if flt(args.get("value_after_depreciation")):
current_asset_value = flt(args.get("value_after_depreciation"))
else:
value = flt(args.get("expected_value_after_useful_life")) / (
flt(self.gross_purchase_amount) - flt(self.opening_accumulated_depreciation)
)
current_asset_value = flt(self.gross_purchase_amount) - flt(self.opening_accumulated_depreciation)
depreciation_rate = math.pow(
value,
1.0
/ (
(
(
flt(args.get("total_number_of_depreciations"), 2)
- flt(self.opening_number_of_booked_depreciations)
)
* flt(args.get("frequency_of_depreciation"))
)
/ 12
),
value = flt(args.get("expected_value_after_useful_life")) / current_asset_value
pending_number_of_depreciations = (
flt(args.get("total_number_of_depreciations"), 2)
- flt(self.opening_number_of_booked_depreciations)
- flt(args.get("total_number_of_booked_depreciations"))
)
pending_years = (
pending_number_of_depreciations * flt(args.get("frequency_of_depreciation"))
+ cint(args.get("increase_in_asset_life"))
) / 12
return flt((100 * (1 - depreciation_rate)), rate_field_precision)
depreciation_rate = 100 * (1 - math.pow(value, 1.0 / pending_years))
return flt(depreciation_rate, rate_field_precision)
def has_gl_entries(doctype, docname, target_account):
@@ -1253,7 +1242,7 @@ def update_existing_asset(asset, remaining_qty, new_asset_name):
current_asset_depr_schedule_doc = get_asset_depr_schedule_doc(asset.name, "Active", row.finance_book)
new_asset_depr_schedule_doc = frappe.copy_doc(current_asset_depr_schedule_doc)
new_asset_depr_schedule_doc.set_draft_asset_depr_schedule_details(asset, row)
new_asset_depr_schedule_doc.fetch_asset_details(asset, row)
accumulated_depreciation = 0
@@ -1310,7 +1299,7 @@ def create_new_asset_after_split(asset, split_qty):
continue
new_asset_depr_schedule_doc = frappe.copy_doc(current_asset_depr_schedule_doc)
new_asset_depr_schedule_doc.set_draft_asset_depr_schedule_details(new_asset, row)
new_asset_depr_schedule_doc.fetch_asset_details(new_asset, row)
accumulated_depreciation = 0

View File

@@ -29,7 +29,7 @@ from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_sched
get_asset_depr_schedule_doc,
get_asset_depr_schedule_name,
get_temp_asset_depr_schedule_doc,
make_new_active_asset_depr_schedules_and_cancel_current_ones,
reschedule_depreciation,
)
@@ -136,10 +136,10 @@ def get_depreciable_asset_depr_schedules_data(date):
return res
def make_depreciation_entry_for_all_asset_depr_schedules(asset_doc, date=None):
def make_depreciation_entry_on_disposal(asset_doc, disposal_date=None):
for row in asset_doc.get("finance_books"):
asset_depr_schedule_name = get_asset_depr_schedule_name(asset_doc.name, "Active", row.finance_book)
make_depreciation_entry(asset_depr_schedule_name, date)
make_depreciation_entry(asset_depr_schedule_name, disposal_date)
def get_acc_frozen_upto():
@@ -244,10 +244,12 @@ def make_depreciation_entry(
except Exception as e:
depreciation_posting_error = e
asset.reload()
asset.set_status()
if not depreciation_posting_error:
asset.db_set("depr_entry_posting_status", "Successful")
asset_depr_schedule_doc.reload()
return asset_depr_schedule_doc
raise depreciation_posting_error
@@ -316,18 +318,10 @@ def _make_journal_entry_for_depreciation(
je.append("accounts", debit_entry)
je.flags.ignore_permissions = True
je.flags.planned_depr_entry = True
je.save()
depr_schedule.db_set("journal_entry", je.name)
if not je.meta.get_workflow():
je.submit()
asset.reload()
idx = cint(asset_depr_schedule_doc.finance_book_id)
row = asset.get("finance_books")[idx - 1]
row.value_after_depreciation -= depr_schedule.depreciation_amount
row.db_update()
def get_depreciation_accounts(asset_category, company):
@@ -433,194 +427,162 @@ def get_comma_separated_links(names, doctype):
@frappe.whitelist()
def scrap_asset(asset_name, scrap_date=None):
asset = frappe.get_doc("Asset", asset_name)
scrap_date = getdate(scrap_date) or getdate(today())
asset.db_set("disposal_date", scrap_date)
validate_asset_for_scrap(asset, scrap_date)
depreciate_asset(asset, scrap_date, get_note_for_scrap(asset))
asset.reload()
create_journal_entry_for_scrap(asset, scrap_date)
def validate_asset_for_scrap(asset, scrap_date):
if asset.docstatus != 1:
frappe.throw(_("Asset {0} must be submitted").format(asset.name))
elif asset.status in ("Cancelled", "Sold", "Scrapped", "Capitalized"):
frappe.throw(_("Asset {0} cannot be scrapped, as it is already {1}").format(asset.name, asset.status))
today_date = getdate(today())
date = getdate(scrap_date) or today_date
purchase_date = getdate(asset.purchase_date)
validate_scrap_date(asset, scrap_date)
validate_scrap_date(date, today_date, purchase_date, asset.calculate_depreciation, asset_name)
notes = _("This schedule was created when Asset {0} was scrapped.").format(
def validate_scrap_date(asset, scrap_date):
if scrap_date > getdate():
frappe.throw(_("Future date is not allowed"))
elif scrap_date < getdate(asset.purchase_date):
frappe.throw(_("Scrap date cannot be before purchase date"))
if asset.calculate_depreciation:
last_booked_depreciation_date = get_last_depreciation_date(asset.name)
if (
last_booked_depreciation_date
and scrap_date < last_booked_depreciation_date
and scrap_date > getdate(asset.purchase_date)
):
frappe.throw(_("Asset cannot be scrapped before the last depreciation entry."))
def get_last_depreciation_date(asset_name):
depreciation = frappe.qb.DocType("Asset Depreciation Schedule")
depreciation_schedule = frappe.qb.DocType("Depreciation Schedule")
last_depreciation_date = (
frappe.qb.from_(depreciation)
.join(depreciation_schedule)
.on(depreciation.name == depreciation_schedule.parent)
.select(depreciation_schedule.schedule_date)
.where(depreciation.asset == asset_name)
.where(depreciation.docstatus == 1)
.where(depreciation_schedule.journal_entry != "")
.orderby(depreciation_schedule.schedule_date, order=Order.desc)
.limit(1)
.run()
)
return last_depreciation_date[0][0] if last_depreciation_date else None
def get_note_for_scrap(asset):
return _("This schedule was created when Asset {0} was scrapped.").format(
get_link_to_form(asset.doctype, asset.name)
)
if asset.status != "Fully Depreciated":
depreciate_asset(asset, date, notes)
asset.reload()
def create_journal_entry_for_scrap(asset, scrap_date):
depreciation_series = frappe.get_cached_value("Company", asset.company, "series_for_depreciation_entry")
je = frappe.new_doc("Journal Entry")
je.voucher_type = "Journal Entry"
je.voucher_type = "Asset Disposal"
je.naming_series = depreciation_series
je.posting_date = date
je.posting_date = scrap_date
je.company = asset.company
je.remark = f"Scrap Entry for asset {asset_name}"
je.remark = f"Scrap Entry for asset {asset.name}"
for entry in get_gl_entries_on_asset_disposal(asset, date):
entry.update({"reference_type": "Asset", "reference_name": asset_name})
for entry in get_gl_entries_on_asset_disposal(asset, scrap_date):
entry.update({"reference_type": "Asset", "reference_name": asset.name})
je.append("accounts", entry)
je.flags.ignore_permissions = True
je.submit()
je.save()
if not je.meta.get_workflow():
je.submit()
frappe.db.set_value("Asset", asset_name, "disposal_date", date)
frappe.db.set_value("Asset", asset_name, "journal_entry_for_scrap", je.name)
asset.set_status("Scrapped")
add_asset_activity(asset_name, _("Asset scrapped"))
frappe.msgprint(_("Asset scrapped via Journal Entry {0}").format(je.name))
def validate_scrap_date(scrap_date, today_date, purchase_date, calculate_depreciation, asset_name):
if scrap_date > today_date:
frappe.throw(_("Future date is not allowed"))
elif scrap_date < purchase_date:
frappe.throw(_("Scrap date cannot be before purchase date"))
if calculate_depreciation:
asset_depreciation_schedules = frappe.db.get_all(
"Asset Depreciation Schedule", filters={"asset": asset_name, "docstatus": 1}, fields=["name"]
)
for depreciation_schedule in asset_depreciation_schedules:
last_booked_depreciation_date = frappe.db.get_value(
"Depreciation Schedule",
{
"parent": depreciation_schedule["name"],
"docstatus": 1,
"journal_entry": ["!=", ""],
},
"schedule_date",
order_by="schedule_date desc",
)
if (
last_booked_depreciation_date
and scrap_date < last_booked_depreciation_date
and scrap_date > purchase_date
):
frappe.throw(_("Asset cannot be scrapped before the last depreciation entry."))
add_asset_activity(asset.name, _("Asset scrapped"))
frappe.msgprint(
_("Asset scrapped via Journal Entry {0}").format(get_link_to_form("Journal Entry", je.name))
)
@frappe.whitelist()
def restore_asset(asset_name):
asset = frappe.get_doc("Asset", asset_name)
reverse_depreciation_entry_made_on_disposal(asset)
reset_depreciation_schedule(asset, get_note_for_restore(asset))
cancel_journal_entry_for_scrap(asset)
asset.set_status()
add_asset_activity(asset_name, _("Asset restored"))
reverse_depreciation_entry_made_after_disposal(asset, asset.disposal_date)
je = asset.journal_entry_for_scrap
notes = _("This schedule was created when Asset {0} was restored.").format(
def get_note_for_restore(asset):
return _("This schedule was created when Asset {0} was restored.").format(
get_link_to_form(asset.doctype, asset.name)
)
reset_depreciation_schedule(asset, asset.disposal_date, notes)
asset.db_set("disposal_date", None)
asset.db_set("journal_entry_for_scrap", None)
frappe.get_doc("Journal Entry", je).cancel()
asset.set_status()
add_asset_activity(asset_name, _("Asset restored"))
def cancel_journal_entry_for_scrap(asset):
if asset.journal_entry_for_scrap:
je = asset.journal_entry_for_scrap
asset.db_set("disposal_date", None)
asset.db_set("journal_entry_for_scrap", None)
frappe.get_doc("Journal Entry", je).cancel()
def depreciate_asset(asset_doc, date, notes):
if not asset_doc.calculate_depreciation:
return
asset_doc.flags.ignore_validate_update_after_submit = True
make_new_active_asset_depr_schedules_and_cancel_current_ones(asset_doc, notes, date_of_disposal=date)
asset_doc.save()
make_depreciation_entry_for_all_asset_depr_schedules(asset_doc, date)
reschedule_depreciation(asset_doc, notes, disposal_date=date)
make_depreciation_entry_on_disposal(asset_doc, date)
# As per Income Tax Act (India), the asset should not be depreciated
# in the financial year in which it is sold/scraped
asset_doc.reload()
cancel_depreciation_entries(asset_doc, date)
# cancel_depreciation_entries(asset_doc, date)
@erpnext.allow_regional
def cancel_depreciation_entries(asset_doc, date):
# Cancel all depreciation entries for the current financial year
# if the asset is sold/scraped in the current financial year
# Overwritten via India Compliance app
pass
def reset_depreciation_schedule(asset_doc, date, notes):
if not asset_doc.calculate_depreciation:
return
asset_doc.flags.ignore_validate_update_after_submit = True
make_new_active_asset_depr_schedules_and_cancel_current_ones(asset_doc, notes, date_of_return=date)
modify_depreciation_schedule_for_asset_repairs(asset_doc, notes)
asset_doc.save()
def reset_depreciation_schedule(asset_doc, notes):
if asset_doc.calculate_depreciation:
reschedule_depreciation(asset_doc, notes)
def modify_depreciation_schedule_for_asset_repairs(asset, notes):
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()
make_new_active_asset_depr_schedules_and_cancel_current_ones(asset, notes)
def reverse_depreciation_entry_made_after_disposal(asset, date):
def reverse_depreciation_entry_made_on_disposal(asset):
for row in asset.get("finance_books"):
asset_depr_schedule_doc = get_asset_depr_schedule_doc(asset.name, "Active", row.finance_book)
if not asset_depr_schedule_doc or not asset_depr_schedule_doc.get("depreciation_schedule"):
schedule_doc = get_asset_depr_schedule_doc(asset.name, "Active", row.finance_book)
if not schedule_doc or not schedule_doc.get("depreciation_schedule"):
continue
for schedule_idx, schedule in enumerate(asset_depr_schedule_doc.get("depreciation_schedule")):
if schedule.schedule_date == date and schedule.journal_entry:
for schedule_idx, schedule in enumerate(schedule_doc.get("depreciation_schedule")):
if schedule.schedule_date == asset.disposal_date and schedule.journal_entry:
if not disposal_was_made_on_original_schedule_date(
schedule_idx, row, date
) or disposal_happens_in_the_future(date):
reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry)
reverse_journal_entry.posting_date = nowdate()
for account in reverse_journal_entry.accounts:
account.update(
{
"reference_type": "Asset",
"reference_name": asset.name,
}
)
frappe.flags.is_reverse_depr_entry = True
reverse_journal_entry.submit()
frappe.flags.is_reverse_depr_entry = False
asset_depr_schedule_doc.flags.ignore_validate_update_after_submit = True
asset.flags.ignore_validate_update_after_submit = True
schedule.journal_entry = None
depreciation_amount = get_depreciation_amount_in_je(reverse_journal_entry)
row.value_after_depreciation += depreciation_amount
asset_depr_schedule_doc.save()
asset.save()
schedule_idx, row, asset.disposal_date
) or disposal_happens_in_the_future(asset.disposal_date):
je = create_reverse_depreciation_entry(asset.name, schedule.journal_entry)
update_value_after_depreciation_on_asset_restore(schedule, row, je)
def get_depreciation_amount_in_je(journal_entry):
if journal_entry.accounts[0].debit_in_account_currency:
return journal_entry.accounts[0].debit_in_account_currency
else:
return journal_entry.accounts[0].credit_in_account_currency
# 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(schedule_idx, row, posting_date_of_disposal):
def disposal_was_made_on_original_schedule_date(schedule_idx, row, disposal_date):
"""
If asset is scrapped or sold on original schedule date,
then the depreciation entry should not be reversed.
"""
orginal_schedule_date = add_months(
row.depreciation_start_date, schedule_idx * cint(row.frequency_of_depreciation)
)
@@ -628,19 +590,57 @@ def disposal_was_made_on_original_schedule_date(schedule_idx, row, posting_date_
if is_last_day_of_the_month(row.depreciation_start_date):
orginal_schedule_date = get_last_day(orginal_schedule_date)
if orginal_schedule_date == posting_date_of_disposal:
if orginal_schedule_date == disposal_date:
return True
return False
def disposal_happens_in_the_future(posting_date_of_disposal):
if posting_date_of_disposal > getdate():
def disposal_happens_in_the_future(disposal_date):
if disposal_date > getdate():
return True
return False
def create_reverse_depreciation_entry(asset_name, journal_entry):
reverse_journal_entry = make_reverse_journal_entry(journal_entry)
reverse_journal_entry.posting_date = nowdate()
for account in reverse_journal_entry.accounts:
account.update(
{
"reference_type": "Asset",
"reference_name": asset_name,
}
)
frappe.flags.is_reverse_depr_entry = True
if not reverse_journal_entry.meta.get_workflow():
reverse_journal_entry.submit()
return reverse_journal_entry
else:
frappe.throw(
_("Please disable workflow temporarily for Journal Entry {0}").format(reverse_journal_entry.name)
)
def update_value_after_depreciation_on_asset_restore(schedule, row, journal_entry):
frappe.db.set_value("Depreciation Schedule", schedule.name, "journal_entry", None, update_modified=False)
depreciation_amount = get_depreciation_amount_in_je(journal_entry)
value_after_depreciation = flt(
row.value_after_depreciation + depreciation_amount, row.precision("value_after_depreciation")
)
row.db_set("value_after_depreciation", value_after_depreciation)
def get_depreciation_amount_in_je(journal_entry):
if journal_entry.accounts[0].debit_in_account_currency:
return journal_entry.accounts[0].debit_in_account_currency
else:
return journal_entry.accounts[0].credit_in_account_currency
def get_gl_entries_on_asset_regain(
asset, selling_amount=0, finance_book=None, voucher_type=None, voucher_no=None, date=None
):

View File

@@ -35,7 +35,7 @@ from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_sched
get_asset_depr_schedule_doc,
get_depr_schedule,
)
from erpnext.assets.doctype.asset_depreciation_schedule.utils import (
from erpnext.erpnext.assets.doctype.asset_depreciation_schedule.deppreciation_schedule_controller import (
get_depreciation_amount,
)
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (

View File

@@ -16,7 +16,7 @@ from erpnext.assets.doctype.asset.depreciation import (
get_gl_entries_on_asset_disposal,
get_value_after_depreciation_on_disposal_date,
reset_depreciation_schedule,
reverse_depreciation_entry_made_after_disposal,
reverse_depreciation_entry_made_on_disposal,
)
from erpnext.assets.doctype.asset_activity.asset_activity import add_asset_activity
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
@@ -623,13 +623,13 @@ class AssetCapitalization(StockController):
self.set_consumed_asset_status(asset)
if asset.calculate_depreciation:
reverse_depreciation_entry_made_after_disposal(asset, self.posting_date)
reverse_depreciation_entry_made_on_disposal(asset, self.posting_date)
notes = _(
"This schedule was created when Asset {0} was restored on Asset Capitalization {1}'s cancellation."
).format(
get_link_to_form(asset.doctype, asset.name), get_link_to_form(self.doctype, self.name)
)
reset_depreciation_schedule(asset, self.posting_date, notes)
reset_depreciation_schedule(asset, notes)
def set_consumed_asset_status(self, asset):
if self.docstatus == 1:

View File

@@ -3,7 +3,6 @@
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import (
add_days,
add_months,
@@ -20,13 +19,12 @@ from frappe.utils import (
)
import erpnext
from erpnext.accounts.utils import get_fiscal_year
from erpnext.assets.doctype.asset_depreciation_schedule.utils import (
get_depreciation_amount,
from erpnext.assets.doctype.asset_depreciation_schedule.deppreciation_schedule_controller import (
DepreciationScheduleController,
)
class AssetDepreciationSchedule(Document):
class AssetDepreciationSchedule(DepreciationScheduleController):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
@@ -61,15 +59,11 @@ class AssetDepreciationSchedule(Document):
value_after_depreciation: DF.Currency
# end: auto-generated types
def before_save(self):
if not self.finance_book_id:
self.prepare_draft_asset_depr_schedule_data_from_asset_name_and_fb_name(
self.asset, self.finance_book
)
self.update_shift_depr_schedule()
def validate(self):
self.validate_another_asset_depr_schedule_does_not_exist()
if not self.finance_book_id:
self.create_depreciation_schedule()
self.update_shift_depr_schedule()
def validate_another_asset_depr_schedule_does_not_exist(self):
finance_book_filter = ["finance_book", "is", "not set"]
@@ -102,7 +96,8 @@ class AssetDepreciationSchedule(Document):
def on_submit(self):
self.db_set("status", "Active")
def before_cancel(self):
def on_cancel(self):
self.db_set("status", "Cancelled")
if not self.flags.should_not_cancel_depreciation_entries:
self.cancel_depreciation_entries()
@@ -111,9 +106,6 @@ class AssetDepreciationSchedule(Document):
if d.journal_entry:
frappe.get_doc("Journal Entry", d.journal_entry).cancel()
def on_cancel(self):
self.db_set("status", "Cancelled")
def update_shift_depr_schedule(self):
if not self.shift_based or self.docstatus != 0:
return
@@ -124,594 +116,50 @@ class AssetDepreciationSchedule(Document):
self.make_depr_schedule(asset_doc, fb_row)
self.set_accumulated_depreciation(asset_doc, fb_row)
def prepare_draft_asset_depr_schedule_data_from_asset_name_and_fb_name(self, asset_name, fb_name):
asset_doc = frappe.get_doc("Asset", asset_name)
def get_finance_book_row(self, fb_row=None):
if fb_row:
self.fb_row = fb_row
return
finance_book_filter = ["finance_book", "is", "not set"]
if fb_name:
finance_book_filter = ["finance_book", "=", fb_name]
if self.finance_book:
finance_book_filter = ["finance_book", "=", self.finance_book]
asset_finance_book_name = frappe.db.get_value(
doctype="Asset Finance Book",
filters=[["parent", "=", asset_name], finance_book_filter],
filters=[["parent", "=", self.asset], finance_book_filter],
)
asset_finance_book_doc = frappe.get_doc("Asset Finance Book", asset_finance_book_name)
self.fb_row = frappe.get_doc("Asset Finance Book", asset_finance_book_name)
self.prepare_draft_asset_depr_schedule_data(asset_doc, asset_finance_book_doc)
def prepare_draft_asset_depr_schedule_data(
self,
asset_doc,
row,
date_of_disposal=None,
date_of_return=None,
update_asset_finance_book_row=True,
):
self.set_draft_asset_depr_schedule_details(asset_doc, row)
self.make_depr_schedule(asset_doc, row, date_of_disposal, update_asset_finance_book_row)
self.set_accumulated_depreciation(asset_doc, row, date_of_disposal, date_of_return)
def set_draft_asset_depr_schedule_details(self, asset_doc, row):
self.asset = asset_doc.name
self.finance_book = row.finance_book
self.finance_book_id = row.idx
self.opening_accumulated_depreciation = asset_doc.opening_accumulated_depreciation or 0
self.opening_number_of_booked_depreciations = asset_doc.opening_number_of_booked_depreciations or 0
self.gross_purchase_amount = asset_doc.gross_purchase_amount
self.depreciation_method = row.depreciation_method
self.total_number_of_depreciations = row.total_number_of_depreciations
self.frequency_of_depreciation = row.frequency_of_depreciation
self.rate_of_depreciation = row.rate_of_depreciation
self.expected_value_after_useful_life = row.expected_value_after_useful_life
self.daily_prorata_based = row.daily_prorata_based
self.shift_based = row.shift_based
def fetch_asset_details(self):
self.asset = self.asset_doc.name
self.finance_book = self.fb_row.get("finance_book")
self.finance_book_id = self.fb_row.idx
self.opening_accumulated_depreciation = self.asset_doc.opening_accumulated_depreciation or 0
self.opening_number_of_booked_depreciations = (
self.asset_doc.opening_number_of_booked_depreciations or 0
)
self.gross_purchase_amount = self.asset_doc.gross_purchase_amount
self.depreciation_method = self.fb_row.depreciation_method
self.total_number_of_depreciations = self.fb_row.total_number_of_depreciations
self.frequency_of_depreciation = self.fb_row.frequency_of_depreciation
self.rate_of_depreciation = self.fb_row.get("rate_of_depreciation")
self.expected_value_after_useful_life = self.fb_row.get("expected_value_after_useful_life")
self.daily_prorata_based = self.fb_row.get("daily_prorata_based")
self.shift_based = self.fb_row.get("shift_based")
self.status = "Draft"
def make_depr_schedule(
self,
asset_doc,
row,
date_of_disposal=None,
update_asset_finance_book_row=True,
value_after_depreciation=None,
):
start = self.clear_depr_schedule()
self._make_depr_schedule(
asset_doc, row, start, date_of_disposal, update_asset_finance_book_row, value_after_depreciation
)
def clear_depr_schedule(self):
"""
Clears the depreciation schedule preserving the depreciation entries that have been booked.
"""
start = 0
num_of_depreciations_completed = 0
depr_schedule = []
self.schedules_before_clearing = self.get("depreciation_schedule")
for schedule in self.get("depreciation_schedule"):
if schedule.journal_entry:
num_of_depreciations_completed += 1
depr_schedule.append(schedule)
else:
start = num_of_depreciations_completed
break
self.depreciation_schedule = depr_schedule
return start
def _make_depr_schedule(
self,
asset_doc,
row,
start,
date_of_disposal,
update_asset_finance_book_row,
value_after_depreciation,
):
row, value_after_depreciation = self.get_value_after_depreciation(
asset_doc, row, value_after_depreciation, update_asset_finance_book_row
)
final_number_of_depreciations, has_pro_rata = self.get_final_number_of_depreciations(asset_doc, row)
has_wdv_or_dd_non_yearly_pro_rata = self.is_wdv_or_dd_non_yearly_pro_rata(asset_doc, row)
should_get_last_day = is_last_day_of_the_month(row.depreciation_start_date)
skip_row = False
depreciation_amount = 0
prev_per_day_depr = True
self.current_fiscal_year_end_date = None
yearly_opening_wdv = value_after_depreciation
pending_months = self.get_number_of_pending_months(asset_doc, row, start)
for n in range(start, final_number_of_depreciations):
# If depreciation is already completed (for double declining balance)
if skip_row:
continue
if self.has_fiscal_year_changed(row, n):
yearly_opening_wdv = value_after_depreciation
prev_depreciation_amount = self.get_prev_depreciation_amount(n)
depreciation_amount, prev_per_day_depr = get_depreciation_amount(
self,
asset_doc,
value_after_depreciation,
yearly_opening_wdv,
row,
n,
prev_depreciation_amount,
has_wdv_or_dd_non_yearly_pro_rata,
pending_months,
prev_per_day_depr,
)
schedule_date = self.get_next_schedule_date(
row, n, has_pro_rata, should_get_last_day, final_number_of_depreciations
)
# if asset is being sold or scrapped
if date_of_disposal and getdate(schedule_date) >= getdate(date_of_disposal):
self.get_depreciation_amount_for_disposal(
asset_doc, row, n, schedule_date, date_of_disposal, depreciation_amount
)
break
if n == 0:
# Get pro rata amount for first row if available for use date is mid of the month
depreciation_amount = self.get_depreciation_amount_for_first_row(
asset_doc, row, n, depreciation_amount, has_pro_rata, has_wdv_or_dd_non_yearly_pro_rata
)
elif has_pro_rata and n == cint(final_number_of_depreciations) - 1: # for the last row
depreciation_amount, schedule_date = self.get_depreciation_amount_for_last_row(
asset_doc, row, n, depreciation_amount, schedule_date, has_wdv_or_dd_non_yearly_pro_rata
)
if not depreciation_amount:
break
value_after_depreciation = flt(
value_after_depreciation - flt(depreciation_amount, asset_doc.precision("gross_purchase_amount")),
asset_doc.precision("gross_purchase_amount"),
)
# Adjust depreciation amount in the last period based on the expected value after useful life
depreciation_amount, skip_row = self.adjust_depr_amount_for_salvage_value(
row, depreciation_amount, value_after_depreciation, final_number_of_depreciations, n, skip_row
)
if flt(depreciation_amount, asset_doc.precision("gross_purchase_amount")) > 0:
self.add_depr_schedule_row(schedule_date, depreciation_amount, n)
def get_value_after_depreciation(
self, asset_doc, row, value_after_depreciation, update_asset_finance_book_row
):
if not value_after_depreciation:
value_after_depreciation = _get_value_after_depreciation_for_making_schedule(asset_doc, row)
row.value_after_depreciation = value_after_depreciation
if update_asset_finance_book_row:
row.db_update()
return row, value_after_depreciation
def get_final_number_of_depreciations(self, asset_doc, row):
final_number_of_depreciations = cint(row.total_number_of_depreciations) - cint(
self.opening_number_of_booked_depreciations
)
has_pro_rata = _check_is_pro_rata(asset_doc, row)
if has_pro_rata:
final_number_of_depreciations += 1
if row.increase_in_asset_life:
final_number_of_depreciations = (
self.get_final_number_of_depreciations_considering_increase_in_asset_life(
asset_doc, row, final_number_of_depreciations
)
)
return final_number_of_depreciations, has_pro_rata
def get_final_number_of_depreciations_considering_increase_in_asset_life(
self, asset_doc, row, final_number_of_depreciations
):
# final schedule date after increasing asset life
self.final_schedule_date = add_months(
asset_doc.available_for_use_date,
(row.total_number_of_depreciations * cint(row.frequency_of_depreciation))
+ row.increase_in_asset_life,
)
number_of_pending_depreciations = cint(row.total_number_of_depreciations) - cint(
asset_doc.opening_number_of_booked_depreciations
)
schedule_date = add_months(
row.depreciation_start_date,
number_of_pending_depreciations * cint(row.frequency_of_depreciation),
)
if self.final_schedule_date > schedule_date:
final_number_of_depreciations += 1
return final_number_of_depreciations
def is_wdv_or_dd_non_yearly_pro_rata(self, asset_doc, row):
has_wdv_or_dd_non_yearly_pro_rata = False
if (
row.depreciation_method in ("Written Down Value", "Double Declining Balance")
and cint(row.frequency_of_depreciation) != 12
):
has_wdv_or_dd_non_yearly_pro_rata = _check_is_pro_rata(asset_doc, row, wdv_or_dd_non_yearly=True)
return has_wdv_or_dd_non_yearly_pro_rata
def get_number_of_pending_months(self, asset_doc, row, start):
total_months = cint(row.total_number_of_depreciations) * cint(row.frequency_of_depreciation) + cint(
row.increase_in_asset_life
)
depr_booked_for_months = 0
last_depr_date = None
if start > 0:
last_depr_date = self.depreciation_schedule[start - 1].schedule_date
elif asset_doc.opening_number_of_booked_depreciations > 0:
last_depr_date = add_months(row.depreciation_start_date, -1 * row.frequency_of_depreciation)
if last_depr_date:
depr_booked_for_months = date_diff(last_depr_date, asset_doc.available_for_use_date) / (365 / 12)
return total_months - depr_booked_for_months
def has_fiscal_year_changed(self, row, row_no):
fiscal_year_changed = False
schedule_date = get_last_day(
add_months(row.depreciation_start_date, row_no * cint(row.frequency_of_depreciation))
)
if not self.current_fiscal_year_end_date:
self.current_fiscal_year_end_date = get_fiscal_year(row.depreciation_start_date)[2]
fiscal_year_changed = True
elif getdate(schedule_date) > getdate(self.current_fiscal_year_end_date):
self.current_fiscal_year_end_date = add_years(self.current_fiscal_year_end_date, 1)
fiscal_year_changed = True
return fiscal_year_changed
def get_prev_depreciation_amount(self, n):
prev_depreciation_amount = 0
if n > 0 and len(self.get("depreciation_schedule")) > n - 1:
prev_depreciation_amount = self.get("depreciation_schedule")[n - 1].depreciation_amount
return prev_depreciation_amount
def get_next_schedule_date(
self, row, n, has_pro_rata, should_get_last_day, final_number_of_depreciations=None
):
schedule_date = add_months(row.depreciation_start_date, n * cint(row.frequency_of_depreciation))
if should_get_last_day:
schedule_date = get_last_day(schedule_date)
return schedule_date
def get_depreciation_amount_for_disposal(
self, asset_doc, row, row_no, schedule_date, date_of_disposal, depreciation_amount
):
if self.depreciation_schedule: # if there are already booked depreciations
from_date = add_days(self.depreciation_schedule[-1].schedule_date, 1)
else:
from_date = _get_modified_available_for_use_date_for_existing_assets(asset_doc, row)
if is_last_day_of_the_month(getdate(asset_doc.available_for_use_date)):
from_date = get_last_day(from_date)
depreciation_amount, days, months = _get_pro_rata_amt(
row,
depreciation_amount,
from_date,
date_of_disposal,
original_schedule_date=schedule_date,
)
depreciation_amount = flt(depreciation_amount, asset_doc.precision("gross_purchase_amount"))
if depreciation_amount > 0:
self.add_depr_schedule_row(date_of_disposal, depreciation_amount, row_no)
def get_adjusted_depreciation_amount(
self, depreciation_amount_without_pro_rata, depreciation_amount_for_last_row
):
# to ensure that final accumulated depreciation amount is accurate
if not self.opening_accumulated_depreciation:
depreciation_amount_for_first_row = self.get("depreciation_schedule")[0].depreciation_amount
if (
depreciation_amount_for_first_row + depreciation_amount_for_last_row
!= depreciation_amount_without_pro_rata
):
depreciation_amount_for_last_row = (
depreciation_amount_without_pro_rata - depreciation_amount_for_first_row
)
return depreciation_amount_for_last_row
def get_depreciation_amount_for_first_row(
self, asset_doc, row, n, depreciation_amount, has_pro_rata, has_wdv_or_dd_non_yearly_pro_rata
):
"""
For the first row, if available for use date is mid of the month, then pro rata amount is needed
"""
pro_rata_amount_applicable = False
if (
(has_pro_rata or has_wdv_or_dd_non_yearly_pro_rata)
and not self.opening_accumulated_depreciation
and not self.flags.wdv_it_act_applied
): # if not existing asset
from_date = asset_doc.available_for_use_date
pro_rata_amount_applicable = True
elif has_wdv_or_dd_non_yearly_pro_rata and self.opening_accumulated_depreciation: # if existing asset
from_date = _get_modified_available_for_use_date_for_existing_assets(asset_doc, row)
pro_rata_amount_applicable = True
if pro_rata_amount_applicable:
depreciation_amount, days, months = _get_pro_rata_amt(
row,
depreciation_amount,
from_date,
row.depreciation_start_date,
has_wdv_or_dd_non_yearly_pro_rata,
)
self.validate_depreciation_amount_for_low_value_assets(asset_doc, row, depreciation_amount)
return depreciation_amount
def get_depreciation_amount_for_last_row(
self, asset_doc, row, n, depreciation_amount, schedule_date, has_wdv_or_dd_non_yearly_pro_rata
):
if not row.increase_in_asset_life:
# In case of increase_in_asset_life, the asset.to_date is already set on asset_repair submission
self.final_schedule_date = add_months(
asset_doc.available_for_use_date,
(n + self.opening_number_of_booked_depreciations) * cint(row.frequency_of_depreciation),
)
if is_last_day_of_the_month(getdate(asset_doc.available_for_use_date)):
self.final_schedule_date = get_last_day(self.final_schedule_date)
if self.opening_accumulated_depreciation:
depreciation_amount, days, months = _get_pro_rata_amt(
row,
depreciation_amount,
schedule_date,
self.final_schedule_date,
has_wdv_or_dd_non_yearly_pro_rata,
)
else:
# if not existing asset, remaining amount of first row is depreciated in the last row
if not row.increase_in_asset_life:
depreciation_amount -= self.get("depreciation_schedule")[0].depreciation_amount
days = date_diff(self.final_schedule_date, schedule_date) + 1
schedule_date = add_days(schedule_date, days - 1)
return depreciation_amount, schedule_date
def adjust_depr_amount_for_salvage_value(
self,
row,
depreciation_amount,
value_after_depreciation,
final_number_of_depreciations,
row_no,
skip_row,
):
if (
row_no == cint(final_number_of_depreciations) - 1
and flt(value_after_depreciation) != flt(row.expected_value_after_useful_life)
) or flt(value_after_depreciation) < flt(row.expected_value_after_useful_life):
depreciation_amount += flt(value_after_depreciation) - flt(row.expected_value_after_useful_life)
depreciation_amount = flt(depreciation_amount, row.precision("depreciation_amount"))
skip_row = True
return depreciation_amount, skip_row
def validate_depreciation_amount_for_low_value_assets(self, asset_doc, row, depreciation_amount):
"""
If gross purchase amount is too low, then depreciation amount
can come zero sometimes based on the frequency and number of depreciations.
"""
if flt(depreciation_amount, asset_doc.precision("gross_purchase_amount")) <= 0:
frappe.throw(
_("Gross Purchase Amount {0} cannot be depreciated over {1} cycles.").format(
frappe.bold(asset_doc.gross_purchase_amount),
frappe.bold(row.total_number_of_depreciations),
)
)
def add_depr_schedule_row(self, schedule_date, depreciation_amount, schedule_idx):
if self.shift_based:
shift = (
self.schedules_before_clearing[schedule_idx].shift
if self.schedules_before_clearing and len(self.schedules_before_clearing) > schedule_idx
else frappe.get_cached_value("Asset Shift Factor", {"default": 1}, "shift_name")
)
else:
shift = None
self.append(
"depreciation_schedule",
{
"schedule_date": schedule_date,
"depreciation_amount": depreciation_amount,
"shift": shift,
},
)
def set_accumulated_depreciation(
self,
asset_doc,
row,
date_of_disposal=None,
date_of_return=None,
):
accumulated_depreciation = flt(self.opening_accumulated_depreciation)
value_after_depreciation = flt(row.value_after_depreciation)
for i, d in enumerate(self.get("depreciation_schedule")):
if d.journal_entry:
accumulated_depreciation = d.accumulated_depreciation_amount
continue
value_after_depreciation = flt(
value_after_depreciation - flt(d.depreciation_amount), d.precision("depreciation_amount")
)
# for the last row, if depreciation method = Straight Line
if (
self.depreciation_method in ("Straight Line", "Manual")
and i == len(self.get("depreciation_schedule")) - 1
and not date_of_disposal
and not date_of_return
and not row.shift_based
):
d.depreciation_amount += flt(
value_after_depreciation - flt(row.expected_value_after_useful_life),
d.precision("depreciation_amount"),
)
accumulated_depreciation += d.depreciation_amount
d.accumulated_depreciation_amount = flt(
accumulated_depreciation, d.precision("accumulated_depreciation_amount")
)
def _get_value_after_depreciation_for_making_schedule(asset_doc, fb_row):
if asset_doc.docstatus == 1 and fb_row.value_after_depreciation:
value_after_depreciation = flt(fb_row.value_after_depreciation)
else:
value_after_depreciation = flt(asset_doc.gross_purchase_amount) - flt(
asset_doc.opening_accumulated_depreciation
)
return value_after_depreciation
# if it returns True, depreciation_amount will not be equal for the first and last rows
def _check_is_pro_rata(asset_doc, row, wdv_or_dd_non_yearly=False):
has_pro_rata = False
# if not existing asset, from_date = available_for_use_date
# otherwise, if opening_number_of_booked_depreciations = 2, available_for_use_date = 01/01/2020 and frequency_of_depreciation = 12
# from_date = 01/01/2022
if row.depreciation_method in ("Straight Line", "Manual"):
prev_depreciation_start_date = get_last_day(
add_months(
row.depreciation_start_date,
(row.frequency_of_depreciation * -1) * asset_doc.opening_number_of_booked_depreciations,
)
)
from_date = asset_doc.available_for_use_date
days = date_diff(prev_depreciation_start_date, from_date) + 1
total_days = get_total_days(prev_depreciation_start_date, row.frequency_of_depreciation)
else:
from_date = _get_modified_available_for_use_date_for_existing_assets(asset_doc, row)
days = date_diff(row.depreciation_start_date, from_date) + 1
total_days = get_total_days(row.depreciation_start_date, row.frequency_of_depreciation)
if days <= 0:
frappe.throw(
_(
"""Error: This asset already has {0} depreciation periods booked.
The `depreciation start` date must be at least {1} periods after the `available for use` date.
Please correct the dates accordingly."""
).format(
asset_doc.opening_number_of_booked_depreciations,
asset_doc.opening_number_of_booked_depreciations,
)
)
if days < total_days:
has_pro_rata = True
return has_pro_rata
def _get_modified_available_for_use_date_for_existing_assets(asset_doc, row):
"""
if Asset has opening booked depreciations = 3,
frequency of depreciation = 3,
available for use date = 17-07-2023,
depreciation start date = 30-06-2024
then from date should be 01-04-2024
"""
if asset_doc.opening_number_of_booked_depreciations > 0:
from_date = add_months(
asset_doc.available_for_use_date,
(asset_doc.opening_number_of_booked_depreciations * row.frequency_of_depreciation) - 1,
)
if is_last_day_of_the_month(row.depreciation_start_date):
return add_days(get_last_day(from_date), 1)
# get from date when depreciation start date is not last day of the month
months_difference = month_diff(row.depreciation_start_date, from_date) - 1
return add_days(add_months(row.depreciation_start_date, -1 * months_difference), 1)
else:
return asset_doc.available_for_use_date
def _get_pro_rata_amt(
row,
depreciation_amount,
from_date,
to_date,
has_wdv_or_dd_non_yearly_pro_rata=False,
original_schedule_date=None,
):
days = date_diff(to_date, from_date) + 1
months = month_diff(to_date, from_date)
if has_wdv_or_dd_non_yearly_pro_rata:
total_days = get_total_days(original_schedule_date or to_date, 12)
else:
total_days = get_total_days(original_schedule_date or to_date, row.frequency_of_depreciation)
return (depreciation_amount * flt(days)) / flt(total_days), days, months
def get_total_days(date, frequency):
period_start_date = add_months(date, cint(frequency) * -1)
if is_last_day_of_the_month(date):
period_start_date = get_last_day(period_start_date)
return date_diff(date, period_start_date)
def make_draft_asset_depr_schedules(asset_doc):
asset_depr_schedules_names = []
for row in asset_doc.get("finance_books"):
name = make_draft_asset_depr_schedule(asset_doc, row)
asset_depr_schedules_names.append(name)
return asset_depr_schedules_names
def make_draft_asset_depr_schedule(asset_doc, row):
asset_depr_schedule_doc = frappe.new_doc("Asset Depreciation Schedule")
asset_depr_schedule_doc.prepare_draft_asset_depr_schedule_data(asset_doc, row)
asset_depr_schedule_doc.create_depreciation_schedule(asset_doc, row)
asset_depr_schedule_doc.insert()
return asset_depr_schedule_doc.name
def update_draft_asset_depr_schedules(asset_doc):
for row in asset_doc.get("finance_books"):
asset_depr_schedule_doc = get_asset_depr_schedule_doc(asset_doc.name, "Draft", row.finance_book)
if not asset_depr_schedule_doc:
continue
asset_depr_schedule_doc.prepare_draft_asset_depr_schedule_data(asset_doc, row)
asset_depr_schedule_doc.save()
def convert_draft_asset_depr_schedules_into_active(asset_doc):
for row in asset_doc.get("finance_books"):
asset_depr_schedule_doc = get_asset_depr_schedule_doc(asset_doc.name, "Draft", row.finance_book)
@@ -732,74 +180,63 @@ def cancel_asset_depr_schedules(asset_doc):
asset_depr_schedule_doc.cancel()
def make_new_active_asset_depr_schedules_and_cancel_current_ones(
asset_doc,
notes,
date_of_disposal=None,
date_of_return=None,
value_after_depreciation=None,
difference_amount=None,
):
def reschedule_depreciation(asset_doc, notes, disposal_date=None):
for row in asset_doc.get("finance_books"):
current_asset_depr_schedule_doc = get_asset_depr_schedule_doc(
asset_doc.name, "Active", row.finance_book
current_schedule = get_asset_depr_schedule_doc(asset_doc.name, None, row.finance_book)
if current_schedule:
if current_schedule.docstatus == 1:
new_schedule = frappe.copy_doc(current_schedule)
elif current_schedule.docstatus == 0:
new_schedule = current_schedule
else:
new_schedule = frappe.new_doc("Asset Depreciation Schedule")
new_schedule.asset = asset_doc.name
set_modified_depreciation_rate(asset_doc, row, new_schedule)
new_schedule.create_depreciation_schedule(row, disposal_date)
new_schedule.notes = notes
if current_schedule and current_schedule.docstatus == 1:
current_schedule.flags.should_not_cancel_depreciation_entries = True
current_schedule.cancel()
new_schedule.submit()
def set_modified_depreciation_rate(asset_doc, row, new_schedule):
if row.depreciation_method in (
"Written Down Value",
"Double Declining Balance",
):
new_rate_of_depreciation = flt(
asset_doc.get_depreciation_rate(row), row.precision("rate_of_depreciation")
)
if not current_asset_depr_schedule_doc:
frappe.throw(
_("Asset Depreciation Schedule not found for Asset {0} and Finance Book {1}").format(
get_link_to_form("Asset", asset_doc.name), row.finance_book
)
)
row.db_set("rate_of_depreciation", new_rate_of_depreciation)
new_schedule.rate_of_depreciation = new_rate_of_depreciation
new_asset_depr_schedule_doc = frappe.copy_doc(current_asset_depr_schedule_doc)
if asset_doc.flags.decrease_in_asset_value_due_to_value_adjustment and not value_after_depreciation:
value_after_depreciation = row.value_after_depreciation - difference_amount
if asset_doc.flags.increase_in_asset_value_due_to_repair and row.depreciation_method in (
"Written Down Value",
"Double Declining Balance",
):
new_rate_of_depreciation = flt(
asset_doc.get_depreciation_rate(row), row.precision("rate_of_depreciation")
)
row.rate_of_depreciation = new_rate_of_depreciation
new_asset_depr_schedule_doc.rate_of_depreciation = new_rate_of_depreciation
new_asset_depr_schedule_doc.make_depr_schedule(
asset_doc, row, date_of_disposal, value_after_depreciation=value_after_depreciation
)
new_asset_depr_schedule_doc.set_accumulated_depreciation(
asset_doc, row, date_of_disposal, date_of_return
)
new_asset_depr_schedule_doc.notes = notes
current_asset_depr_schedule_doc.flags.should_not_cancel_depreciation_entries = True
current_asset_depr_schedule_doc.cancel()
new_asset_depr_schedule_doc.submit()
def get_temp_asset_depr_schedule_doc(
asset_doc,
row,
date_of_disposal=None,
disposal_date=None,
date_of_return=None,
update_asset_finance_book_row=False,
new_depr_schedule=None,
):
current_asset_depr_schedule_doc = get_asset_depr_schedule_doc(asset_doc.name, "Active", row.finance_book)
current_schedule = get_asset_depr_schedule_doc(asset_doc.name, "Active", row.finance_book)
if not current_asset_depr_schedule_doc:
if not current_schedule:
frappe.throw(
_("Asset Depreciation Schedule not found for Asset {0} and Finance Book {1}").format(
get_link_to_form("Asset", asset_doc.name), row.finance_book
)
)
temp_asset_depr_schedule_doc = frappe.copy_doc(current_asset_depr_schedule_doc)
temp_asset_depr_schedule_doc = frappe.copy_doc(current_schedule)
if new_depr_schedule:
temp_asset_depr_schedule_doc.depreciation_schedule = []
@@ -816,10 +253,10 @@ def get_temp_asset_depr_schedule_doc(
},
)
temp_asset_depr_schedule_doc.prepare_draft_asset_depr_schedule_data(
temp_asset_depr_schedule_doc.create_depreciation_schedule(
asset_doc,
row,
date_of_disposal,
disposal_date,
date_of_return,
update_asset_finance_book_row,
)
@@ -838,7 +275,7 @@ def get_depr_schedule(asset_name, status, finance_book=None):
@frappe.whitelist()
def get_asset_depr_schedule_doc(asset_name, status, finance_book=None):
def get_asset_depr_schedule_doc(asset_name, status=None, finance_book=None):
asset_depr_schedule = get_asset_depr_schedule_name(asset_name, status, finance_book)
if not asset_depr_schedule:
@@ -849,16 +286,17 @@ def get_asset_depr_schedule_doc(asset_name, status, finance_book=None):
return asset_depr_schedule_doc
def get_asset_depr_schedule_name(asset_name, status, finance_book=None):
if isinstance(status, str):
status = [status]
def get_asset_depr_schedule_name(asset_name, status=None, finance_book=None):
filters = [
["asset", "=", asset_name],
["status", "in", status],
["docstatus", "<", 2],
]
if status:
if isinstance(status, str):
status = [status]
filters.append(["status", "in", status])
if finance_book:
filters.append(["finance_book", "=", finance_book])
else:

View File

@@ -0,0 +1,449 @@
import frappe
from frappe import _
from frappe.utils import (
add_days,
add_months,
add_years,
cint,
date_diff,
flt,
get_last_day,
getdate,
is_last_day_of_the_month,
month_diff,
nowdate,
)
import erpnext
from erpnext.accounts.utils import get_fiscal_year
from erpnext.assets.doctype.asset_depreciation_schedule.depreciation_methods import (
StraightLineMethod,
WDVMethod,
)
class DepreciationScheduleController(StraightLineMethod, WDVMethod):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def create_depreciation_schedule(self, fb_row=None, disposal_date=None):
self.disposal_date = disposal_date
self.asset_doc = frappe.get_doc("Asset", self.asset)
self.get_finance_book_row(fb_row)
self.fetch_asset_details()
self.clear()
self.create()
self.set_accumulated_depreciation()
def clear(self):
self.first_non_depreciated_row_idx = 0
num_of_depreciations_completed = 0
depr_schedule = []
self.schedules_before_clearing = self.get("depreciation_schedule")
for schedule in self.get("depreciation_schedule"):
if schedule.journal_entry:
num_of_depreciations_completed += 1
depr_schedule.append(schedule)
else:
self.first_non_depreciated_row_idx = num_of_depreciations_completed
break
self.depreciation_schedule = depr_schedule
def create(self):
self.initialize_variables()
for row_idx in range(self.first_non_depreciated_row_idx, self.final_number_of_depreciations):
# If depreciation is already completed (for double declining balance)
if self.skip_row:
continue
if self.has_fiscal_year_changed(row_idx):
self.yearly_opening_wdv = self.pending_depreciation_amount
self.get_prev_depreciation_amount(row_idx)
self.schedule_date = self.get_next_schedule_date(row_idx)
self.depreciation_amount = self.get_depreciation_amount(row_idx)
print(row_idx, self.schedule_date, self.depreciation_amount)
# if asset is being sold or scrapped
if self.disposal_date and getdate(self.schedule_date) >= getdate(self.disposal_date):
self.set_depreciation_amount_for_disposal(row_idx)
break
if row_idx == 0:
self.set_depreciation_amount_for_first_row(row_idx)
elif (
self.has_pro_rata and row_idx == cint(self.final_number_of_depreciations) - 1
): # for the last row
self.set_depreciation_amount_for_last_row(row_idx)
self.depreciation_amount = flt(
self.depreciation_amount, self.asset_doc.precision("gross_purchase_amount")
)
if not self.depreciation_amount:
break
self.pending_depreciation_amount = flt(
self.pending_depreciation_amount - self.depreciation_amount,
self.asset_doc.precision("gross_purchase_amount"),
)
self.adjust_depr_amount_for_salvage_value(row_idx)
if flt(self.depreciation_amount, self.asset_doc.precision("gross_purchase_amount")) > 0:
self.add_depr_schedule_row(row_idx)
def initialize_variables(self):
self.pending_depreciation_amount = self.fb_row.value_after_depreciation
self.should_get_last_day = is_last_day_of_the_month(self.fb_row.depreciation_start_date)
self.skip_row = False
self.depreciation_amount = 0
self.prev_per_day_depr = True
self.current_fiscal_year_end_date = None
self.yearly_opening_wdv = self.pending_depreciation_amount
self.get_number_of_pending_months()
self.get_final_number_of_depreciations()
self.is_wdv_or_dd_non_yearly_pro_rata()
self.get_total_pending_days_or_years()
def get_final_number_of_depreciations(self):
self.final_number_of_depreciations = cint(self.fb_row.total_number_of_depreciations) - cint(
self.opening_number_of_booked_depreciations
)
self._check_is_pro_rata()
if self.has_pro_rata:
self.final_number_of_depreciations += 1
self.set_final_number_of_depreciations_considering_increase_in_asset_life()
def set_final_number_of_depreciations_considering_increase_in_asset_life(self):
# final schedule date after increasing asset life
self.final_schedule_date = add_months(
self.asset_doc.available_for_use_date,
(self.fb_row.total_number_of_depreciations * cint(self.fb_row.frequency_of_depreciation))
+ cint(self.fb_row.increase_in_asset_life),
)
number_of_pending_depreciations = cint(self.fb_row.total_number_of_depreciations) - cint(
self.asset_doc.opening_number_of_booked_depreciations
)
schedule_date = add_months(
self.fb_row.depreciation_start_date,
number_of_pending_depreciations * cint(self.fb_row.frequency_of_depreciation),
)
if self.final_schedule_date > getdate(schedule_date):
months = month_diff(self.final_schedule_date, schedule_date)
self.final_number_of_depreciations += months // cint(self.fb_row.frequency_of_depreciation) + 1
def is_wdv_or_dd_non_yearly_pro_rata(self):
if (
self.fb_row.depreciation_method in ("Written Down Value", "Double Declining Balance")
and cint(self.fb_row.frequency_of_depreciation) != 12
):
self._check_is_pro_rata()
def _check_is_pro_rata(self):
self.has_pro_rata = False
# if not existing asset, from_date = available_for_use_date
# otherwise, if opening_number_of_booked_depreciations = 2, available_for_use_date = 01/01/2020 and frequency_of_depreciation = 12
# from_date = 01/01/2022
if self.fb_row.depreciation_method in ("Straight Line", "Manual"):
prev_depreciation_start_date = get_last_day(
add_months(
self.fb_row.depreciation_start_date,
(self.fb_row.frequency_of_depreciation * -1)
* self.asset_doc.opening_number_of_booked_depreciations,
)
)
from_date = self.asset_doc.available_for_use_date
days = date_diff(prev_depreciation_start_date, from_date) + 1
total_days = self.get_total_days(prev_depreciation_start_date)
else:
from_date = self._get_modified_available_for_use_date_for_existing_assets()
days = date_diff(self.fb_row.depreciation_start_date, from_date) + 1
total_days = self.get_total_days(self.fb_row.depreciation_start_date)
if days <= 0:
frappe.throw(
_(
"""Error: This asset already has {0} depreciation periods booked.
The `depreciation start` date must be at least {1} periods after the `available for use` date.
Please correct the dates accordingly."""
).format(
self.asset_doc.opening_number_of_booked_depreciations,
self.asset_doc.opening_number_of_booked_depreciations,
)
)
if days < total_days:
self.has_pro_rata = True
self.has_wdv_or_dd_non_yearly_pro_rata = True
def _get_modified_available_for_use_date_for_existing_assets(self):
"""
if Asset has opening booked depreciations = 3,
frequency of depreciation = 3,
available for use date = 17-07-2023,
depreciation start date = 30-06-2024
then from date should be 01-04-2024
"""
if self.asset_doc.opening_number_of_booked_depreciations > 0:
from_date = add_months(
self.asset_doc.available_for_use_date,
(
self.asset_doc.opening_number_of_booked_depreciations
* self.fb_row.frequency_of_depreciation
)
- 1,
)
if is_last_day_of_the_month(self.fb_row.depreciation_start_date):
return add_days(get_last_day(from_date), 1)
# get from date when depreciation start date is not last day of the month
months_difference = month_diff(self.fb_row.depreciation_start_date, from_date) - 1
return add_days(add_months(self.fb_row.depreciation_start_date, -1 * months_difference), 1)
else:
return self.asset_doc.available_for_use_date
def get_total_days(self, date):
period_start_date = add_months(date, cint(self.fb_row.frequency_of_depreciation) * -1)
if is_last_day_of_the_month(date):
period_start_date = get_last_day(period_start_date)
return date_diff(date, period_start_date)
def _get_pro_rata_amt(self, from_date, to_date, original_schedule_date=None):
days = date_diff(to_date, from_date) + 1
months = month_diff(to_date, from_date)
total_days = self.get_total_days(original_schedule_date or to_date)
return (self.depreciation_amount * flt(days)) / flt(total_days), days, months
def get_number_of_pending_months(self):
total_months = cint(self.fb_row.total_number_of_depreciations) * cint(
self.fb_row.frequency_of_depreciation
) + cint(self.fb_row.increase_in_asset_life)
depr_booked_for_months = 0
last_depr_date = self.get_last_booked_depreciation_date()
if last_depr_date:
depr_booked_for_months = date_diff(last_depr_date, self.asset_doc.available_for_use_date) / (
365 / 12
)
self.pending_months = total_months - depr_booked_for_months
def get_last_booked_depreciation_date(self):
last_depr_date = None
if self.first_non_depreciated_row_idx > 0:
last_depr_date = self.depreciation_schedule[self.first_non_depreciated_row_idx - 1].schedule_date
elif self.asset_doc.opening_number_of_booked_depreciations > 0:
last_depr_date = add_months(
self.fb_row.depreciation_start_date, -1 * self.fb_row.frequency_of_depreciation
)
return last_depr_date
def get_total_pending_days_or_years(self):
if cint(frappe.db.get_single_value("Accounts Settings", "calculate_depr_using_total_days")):
last_depr_date = self.get_last_booked_depreciation_date()
self.total_pending_days = date_diff(self.final_schedule_date, last_depr_date) + 1
else:
self.total_pending_years = self.pending_months / 12
def has_fiscal_year_changed(self, row_idx):
self.fiscal_year_changed = False
schedule_date = get_last_day(
add_months(
self.fb_row.depreciation_start_date, row_idx * cint(self.fb_row.frequency_of_depreciation)
)
)
if not self.current_fiscal_year_end_date:
self.current_fiscal_year_end_date = get_fiscal_year(self.fb_row.depreciation_start_date)[2]
self.fiscal_year_changed = True
elif getdate(schedule_date) > getdate(self.current_fiscal_year_end_date):
self.current_fiscal_year_end_date = add_years(self.current_fiscal_year_end_date, 1)
self.fiscal_year_changed = True
def get_prev_depreciation_amount(self, row_idx):
self.prev_depreciation_amount = 0
if row_idx > 0 and len(self.get("depreciation_schedule")) > row_idx - 1:
self.prev_depreciation_amount = self.get("depreciation_schedule")[row_idx - 1].depreciation_amount
def get_next_schedule_date(self, row_idx):
schedule_date = add_months(
self.fb_row.depreciation_start_date, row_idx * cint(self.fb_row.frequency_of_depreciation)
)
if self.should_get_last_day:
schedule_date = get_last_day(schedule_date)
return schedule_date
def set_depreciation_amount_for_disposal(self, row_idx):
if self.depreciation_schedule: # if there are already booked depreciations
from_date = add_days(self.depreciation_schedule[-1].schedule_date, 1)
else:
from_date = self._get_modified_available_for_use_date_for_existing_assets()
if is_last_day_of_the_month(getdate(self.asset_doc.available_for_use_date)):
from_date = get_last_day(from_date)
self.depreciation_amount, days, months = self._get_pro_rata_amt(
from_date,
self.disposal_date,
original_schedule_date=self.schedule_date,
)
self.depreciation_amount = flt(
self.depreciation_amount, self.asset_doc.precision("gross_purchase_amount")
)
if self.depreciation_amount > 0:
self.schedule_date = self.disposal_date
self.add_depr_schedule_row(row_idx)
def set_depreciation_amount_for_first_row(self, row_idx):
"""
For the first row, if available for use date is mid of the month, then pro rata amount is needed
"""
pro_rata_amount_applicable = False
if (
self.has_pro_rata
and not self.opening_accumulated_depreciation
and not self.flags.wdv_it_act_applied
): # if not existing asset
from_date = self.asset_doc.available_for_use_date
pro_rata_amount_applicable = True
elif self.has_pro_rata and self.opening_accumulated_depreciation: # if existing asset
from_date = self._get_modified_available_for_use_date_for_existing_assets()
pro_rata_amount_applicable = True
if pro_rata_amount_applicable:
self.depreciation_amount, days, months = self._get_pro_rata_amt(
from_date,
self.fb_row.depreciation_start_date,
)
self.validate_depreciation_amount_for_low_value_assets()
def set_depreciation_amount_for_last_row(self, row_idx):
if not self.fb_row.increase_in_asset_life:
self.final_schedule_date = add_months(
self.asset_doc.available_for_use_date,
(row_idx + self.opening_number_of_booked_depreciations)
* cint(self.fb_row.frequency_of_depreciation),
)
if is_last_day_of_the_month(getdate(self.asset_doc.available_for_use_date)):
self.final_schedule_date = get_last_day(self.final_schedule_date)
if self.opening_accumulated_depreciation:
self.depreciation_amount, days, months = self._get_pro_rata_amt(
self.schedule_date,
self.final_schedule_date,
)
else:
if not self.fb_row.increase_in_asset_life:
self.depreciation_amount -= self.get("depreciation_schedule")[0].depreciation_amount
days = date_diff(self.final_schedule_date, self.schedule_date) + 1
self.schedule_date = add_days(self.schedule_date, days - 1)
def adjust_depr_amount_for_salvage_value(self, row_idx):
"""
Adjust depreciation amount in the last period based on the expected value after useful life
"""
if (
row_idx == cint(self.final_number_of_depreciations) - 1
and flt(self.pending_depreciation_amount) != flt(self.fb_row.expected_value_after_useful_life)
) or flt(self.pending_depreciation_amount) < flt(self.fb_row.expected_value_after_useful_life):
self.depreciation_amount += flt(self.pending_depreciation_amount) - flt(
self.fb_row.expected_value_after_useful_life
)
self.depreciation_amount = flt(
self.depreciation_amount, self.precision("value_after_depreciation")
)
self.skip_row = True
def validate_depreciation_amount_for_low_value_assets(self):
"""
If gross purchase amount is too low, then depreciation amount
can come zero sometimes based on the frequency and number of depreciations.
"""
if flt(self.depreciation_amount, self.asset_doc.precision("gross_purchase_amount")) <= 0:
frappe.throw(
_("Gross Purchase Amount {0} cannot be depreciated over {1} cycles.").format(
frappe.bold(self.asset_doc.gross_purchase_amount),
frappe.bold(self.fb_row.total_number_of_depreciations),
)
)
def add_depr_schedule_row(self, row_idx):
shift = None
if self.shift_based:
shift = (
self.schedules_before_clearing[row_idx].shift
if (self.schedules_before_clearing and len(self.schedules_before_clearing) > row_idx)
else frappe.get_cached_value("Asset Shift Factor", {"default": 1}, "shift_name")
)
self.append(
"depreciation_schedule",
{
"schedule_date": self.schedule_date,
"depreciation_amount": self.depreciation_amount,
"shift": shift,
},
)
def set_accumulated_depreciation(self):
accumulated_depreciation = flt(self.opening_accumulated_depreciation)
for d in self.get("depreciation_schedule"):
if d.journal_entry:
accumulated_depreciation = d.accumulated_depreciation_amount
continue
accumulated_depreciation += d.depreciation_amount
d.accumulated_depreciation_amount = flt(
accumulated_depreciation, d.precision("accumulated_depreciation_amount")
)
def get_depreciation_amount(self, row_idx):
if self.fb_row.depreciation_method in ("Straight Line", "Manual"):
return self.get_straight_line_depr_amount(row_idx)
else:
return self.get_wdv_or_dd_depr_amount(row_idx)
def _get_total_days(self, depreciation_start_date, row_idx):
from_date = add_months(depreciation_start_date, (row_idx - 1) * self.frequency_of_depreciation)
to_date = add_months(from_date, self.frequency_of_depreciation)
if is_last_day_of_the_month(depreciation_start_date):
to_date = get_last_day(to_date)
from_date = add_days(get_last_day(from_date), 1)
return from_date, date_diff(to_date, from_date) + 1
def get_total_days_in_current_depr_year(self):
fy_start_date, fy_end_date = self.get_fiscal_year(self.schedule_date)
return date_diff(fy_end_date, fy_start_date) + 1
def get_fiscal_year(self, date):
fy = get_fiscal_year(date, as_dict=True, raise_on_missing=False)
if fy:
fy_start_date = fy.year_start_date
fy_end_date = fy.year_end_date
else:
current_fy = get_fiscal_year(nowdate(), as_dict=True)
# get fiscal year start date of the year in which the schedule date falls
months = month_diff(date, current_fy.year_start_date)
if months % 12:
years = months // 12
else:
years = months // 12 - 1
fy_start_date = add_years(current_fy.year_start_date, years)
fy_end_date = add_days(add_years(fy_start_date, 1), -1)
return fy_start_date, fy_end_date

View File

@@ -0,0 +1,119 @@
import frappe
from frappe.model.document import Document
from frappe.utils import (
add_days,
add_years,
cint,
date_diff,
flt,
month_diff,
nowdate,
)
import erpnext
from erpnext.accounts.utils import get_fiscal_year
# from erpnext.assets.doctype.asset_depreciation_schedule.deppreciation_schedule_controller import (
# _get_total_days,
# )
class StraightLineMethod(Document):
def get_straight_line_depr_amount(self, row_idx):
self.depreciable_value = flt(self.fb_row.value_after_depreciation) - flt(
self.fb_row.expected_value_after_useful_life
)
if self.fb_row.shift_based:
self.get_shift_depr_amount(row_idx)
if self.fb_row.daily_prorata_based:
return self.get_daily_prorata_based_depr_amount(row_idx)
else:
return self.get_fixed_depr_amount()
def get_fixed_depr_amount(self):
pending_periods = flt(self.pending_months) / flt(self.fb_row.frequency_of_depreciation)
return self.depreciable_value / pending_periods
def get_daily_prorata_based_depr_amount(self, row_idx):
daily_depr_amount = self.get_daily_depr_amount()
from_date, total_depreciable_days = self._get_total_days(self.fb_row.depreciation_start_date, row_idx)
return daily_depr_amount * total_depreciable_days
def get_daily_depr_amount(self):
if cint(frappe.db.get_single_value("Accounts Settings", "calculate_depr_using_total_days")):
return self.depreciable_value / self.total_pending_days
else:
yearly_depr_amount = self.depreciable_value / self.total_pending_years
total_days_in_current_depr_year = self.get_total_days_in_current_depr_year()
return yearly_depr_amount / total_days_in_current_depr_year
def get_shift_depr_amount(self, row_idx):
depreciable_value = (
flt(self.asset_doc.gross_purchase_amount)
- flt(self.asset_doc.opening_accumulated_depreciation)
- flt(self.fb_row.expected_value_after_useful_life)
)
if self.get("__islocal") and not self.asset_doc.flags.shift_allocation:
pending_depreciations = flt(
self.fb_row.total_number_of_depreciations
- self.asset_doc.opening_number_of_booked_depreciations
)
return depreciable_value / pending_depreciations
asset_shift_factors_map = self.get_asset_shift_factors_map()
shift = (
self.schedules_before_clearing[row_idx].shift
if len(self.schedules_before_clearing) > row_idx
else None
)
shift_factor = asset_shift_factors_map.get(shift, 0)
shift_factors_sum = sum(
[flt(asset_shift_factors_map.get(d.shift)) for d in self.schedules_before_clearing]
)
return (depreciable_value / shift_factors_sum) * shift_factor
def get_asset_shift_factors_map(self):
return dict(frappe.db.get_all("Asset Shift Factor", ["shift_name", "shift_factor"], as_list=True))
class WDVMethod(Document):
def get_wdv_or_dd_depr_amount(self, row_idx):
if self.fb_row.daily_prorata_based:
return self.get_daily_prorata_based_wdv_depr_amount(row_idx)
else:
return self.get_wdv_depr_amount()
def get_wdv_depr_amount(self):
if self.is_fiscal_year_changed():
yearly_amount = (
flt(self.pending_depreciation_amount) * flt(self.fb_row.rate_of_depreciation) / 100
)
return (yearly_amount * self.fb_row.frequency_of_depreciation) / 12
else:
return self.prev_depreciation_amount
def is_fiscal_year_changed(self):
fy_start_date, fy_end_date = self.get_fiscal_year(self.schedule_date)
if fy_start_date != self.get("prev_fy_start_date"):
self.prev_fy_start_date = fy_start_date
return True
def get_daily_prorata_based_wdv_depr_amount(self, row_idx):
daily_depr_amount = self.get_daily_wdv_depr_amount()
from_date, total_depreciable_days = self._get_total_days(self.fb_row.depreciation_start_date, row_idx)
return daily_depr_amount * total_depreciable_days
def get_daily_wdv_depr_amount(self):
if self.is_fiscal_year_changed():
self.yearly_wdv_depr_amount = (
self.pending_depreciation_amount * self.fb_row.rate_of_depreciation / 100
)
total_days_in_current_depr_year = self.get_total_days_in_current_depr_year()
return self.yearly_wdv_depr_amount / total_days_in_current_depr_year

View File

@@ -1,333 +0,0 @@
import frappe
from frappe.utils import (
add_days,
add_months,
add_years,
cint,
cstr,
date_diff,
flt,
get_last_day,
is_last_day_of_the_month,
)
import erpnext
def get_depreciation_amount(
asset_depr_schedule,
asset,
value_after_depreciation,
yearly_opening_wdv,
fb_row,
schedule_idx=0,
prev_depreciation_amount=0,
has_wdv_or_dd_non_yearly_pro_rata=False,
number_of_pending_depreciations=0,
prev_per_day_depr=0,
):
if fb_row.depreciation_method in ("Straight Line", "Manual"):
return get_straight_line_or_manual_depr_amount(
asset_depr_schedule,
asset,
fb_row,
schedule_idx,
value_after_depreciation,
number_of_pending_depreciations,
), None
else:
return get_wdv_or_dd_depr_amount(
asset,
fb_row,
value_after_depreciation,
yearly_opening_wdv,
schedule_idx,
prev_depreciation_amount,
has_wdv_or_dd_non_yearly_pro_rata,
asset_depr_schedule,
prev_per_day_depr,
)
def get_straight_line_or_manual_depr_amount(
asset_depr_schedule,
asset,
fb_row,
schedule_idx,
value_after_depreciation,
number_of_pending_depreciations,
):
if fb_row.shift_based:
return get_shift_depr_amount(asset_depr_schedule, asset, fb_row, schedule_idx)
if fb_row.daily_prorata_based:
amount = flt(asset.gross_purchase_amount) - flt(fb_row.expected_value_after_useful_life)
return get_daily_prorata_based_straight_line_depr(
asset, fb_row, schedule_idx, number_of_pending_depreciations, amount
)
else:
return (flt(fb_row.value_after_depreciation) - flt(fb_row.expected_value_after_useful_life)) / (
flt(number_of_pending_depreciations) / flt(fb_row.frequency_of_depreciation)
)
def get_daily_prorata_based_straight_line_depr(
asset, fb_row, schedule_idx, number_of_pending_depreciations, amount
):
daily_depr_amount = get_daily_depr_amount(asset, fb_row, schedule_idx, amount)
from_date, total_depreciable_days = _get_total_days(
fb_row.depreciation_start_date, schedule_idx, fb_row.frequency_of_depreciation
)
return daily_depr_amount * total_depreciable_days
def get_daily_depr_amount(asset, fb_row, schedule_idx, amount):
if cint(frappe.db.get_single_value("Accounts Settings", "calculate_depr_using_total_days")):
total_days = (
date_diff(
get_last_day(
add_months(
fb_row.depreciation_start_date,
flt(
fb_row.total_number_of_depreciations
- asset.opening_number_of_booked_depreciations
- 1
)
* fb_row.frequency_of_depreciation,
)
),
add_days(
get_last_day(
add_months(
fb_row.depreciation_start_date,
(
fb_row.frequency_of_depreciation
* (asset.opening_number_of_booked_depreciations + 1)
)
* -1,
),
),
1,
),
)
+ 1
)
return amount / total_days
else:
total_years = (
flt(
(fb_row.total_number_of_depreciations - fb_row.total_number_of_booked_depreciations)
* fb_row.frequency_of_depreciation
)
/ 12
)
every_year_depr = amount / total_years
depr_period_start_date = add_days(
get_last_day(add_months(fb_row.depreciation_start_date, fb_row.frequency_of_depreciation * -1)), 1
)
year_start_date = add_years(
depr_period_start_date, ((fb_row.frequency_of_depreciation * schedule_idx) // 12)
)
year_end_date = add_days(add_years(year_start_date, 1), -1)
return every_year_depr / (date_diff(year_end_date, year_start_date) + 1)
def get_shift_depr_amount(asset_depr_schedule, asset, fb_row, schedule_idx):
if asset_depr_schedule.get("__islocal") and not asset.flags.shift_allocation:
return (
flt(asset.gross_purchase_amount)
- flt(asset.opening_accumulated_depreciation)
- flt(fb_row.expected_value_after_useful_life)
) / flt(fb_row.total_number_of_depreciations - asset.opening_number_of_booked_depreciations)
asset_shift_factors_map = get_asset_shift_factors_map()
shift = (
asset_depr_schedule.schedules_before_clearing[schedule_idx].shift
if len(asset_depr_schedule.schedules_before_clearing) > schedule_idx
else None
)
shift_factor = asset_shift_factors_map.get(shift) if shift else 0
shift_factors_sum = sum(
flt(asset_shift_factors_map.get(schedule.shift))
for schedule in asset_depr_schedule.schedules_before_clearing
)
return (
(
flt(asset.gross_purchase_amount)
- flt(asset.opening_accumulated_depreciation)
- flt(fb_row.expected_value_after_useful_life)
)
/ flt(shift_factors_sum)
) * shift_factor
def get_asset_shift_factors_map():
return dict(frappe.db.get_all("Asset Shift Factor", ["shift_name", "shift_factor"], as_list=True))
@erpnext.allow_regional
def get_wdv_or_dd_depr_amount(
asset,
fb_row,
value_after_depreciation,
yearly_opening_wdv,
schedule_idx,
prev_depreciation_amount,
has_wdv_or_dd_non_yearly_pro_rata,
asset_depr_schedule,
prev_per_day_depr,
):
return get_default_wdv_or_dd_depr_amount(
asset,
fb_row,
value_after_depreciation,
schedule_idx,
prev_depreciation_amount,
has_wdv_or_dd_non_yearly_pro_rata,
asset_depr_schedule,
prev_per_day_depr,
)
def get_default_wdv_or_dd_depr_amount(
asset,
fb_row,
value_after_depreciation,
schedule_idx,
prev_depreciation_amount,
has_wdv_or_dd_non_yearly_pro_rata,
asset_depr_schedule,
prev_per_day_depr,
):
if not fb_row.daily_prorata_based or cint(fb_row.frequency_of_depreciation) == 12:
return _get_default_wdv_or_dd_depr_amount(
asset,
fb_row,
value_after_depreciation,
schedule_idx,
prev_depreciation_amount,
has_wdv_or_dd_non_yearly_pro_rata,
asset_depr_schedule,
), None
else:
return _get_daily_prorata_based_default_wdv_or_dd_depr_amount(
asset,
fb_row,
value_after_depreciation,
schedule_idx,
prev_depreciation_amount,
has_wdv_or_dd_non_yearly_pro_rata,
asset_depr_schedule,
prev_per_day_depr,
)
def _get_default_wdv_or_dd_depr_amount(
asset,
fb_row,
value_after_depreciation,
schedule_idx,
prev_depreciation_amount,
has_wdv_or_dd_non_yearly_pro_rata,
asset_depr_schedule,
):
if cint(fb_row.frequency_of_depreciation) == 12:
return flt(value_after_depreciation) * (flt(fb_row.rate_of_depreciation) / 100)
else:
if has_wdv_or_dd_non_yearly_pro_rata:
if schedule_idx == 0:
return flt(value_after_depreciation) * (flt(fb_row.rate_of_depreciation) / 100)
elif schedule_idx % (12 / cint(fb_row.frequency_of_depreciation)) == 1:
return (
flt(value_after_depreciation)
* flt(fb_row.frequency_of_depreciation)
* (flt(fb_row.rate_of_depreciation) / 1200)
)
else:
return prev_depreciation_amount
else:
if schedule_idx % (12 / cint(fb_row.frequency_of_depreciation)) == 0:
return (
flt(value_after_depreciation)
* flt(fb_row.frequency_of_depreciation)
* (flt(fb_row.rate_of_depreciation) / 1200)
)
else:
return prev_depreciation_amount
def _get_daily_prorata_based_default_wdv_or_dd_depr_amount(
asset,
fb_row,
value_after_depreciation,
schedule_idx,
prev_depreciation_amount,
has_wdv_or_dd_non_yearly_pro_rata,
asset_depr_schedule,
prev_per_day_depr,
):
if has_wdv_or_dd_non_yearly_pro_rata: # If applicable days for ther first month is less than full month
if schedule_idx == 0:
return flt(value_after_depreciation) * (flt(fb_row.rate_of_depreciation) / 100), None
elif schedule_idx % (12 / cint(fb_row.frequency_of_depreciation)) == 1: # Year changes
return get_monthly_depr_amount(fb_row, schedule_idx, value_after_depreciation)
else:
return get_monthly_depr_amount_based_on_prev_per_day_depr(fb_row, schedule_idx, prev_per_day_depr)
else:
if schedule_idx % (12 / cint(fb_row.frequency_of_depreciation)) == 0: # year changes
return get_monthly_depr_amount(fb_row, schedule_idx, value_after_depreciation)
else:
return get_monthly_depr_amount_based_on_prev_per_day_depr(fb_row, schedule_idx, prev_per_day_depr)
def get_monthly_depr_amount(fb_row, schedule_idx, value_after_depreciation):
"""
Returns monthly depreciation amount when year changes
1. Calculate per day depr based on new year
2. Calculate monthly amount based on new per day amount
"""
from_date, days_in_month = _get_total_days(
fb_row.depreciation_start_date, schedule_idx, cint(fb_row.frequency_of_depreciation)
)
per_day_depr = get_per_day_depr(fb_row, value_after_depreciation, from_date)
return (per_day_depr * days_in_month), per_day_depr
def get_monthly_depr_amount_based_on_prev_per_day_depr(fb_row, schedule_idx, prev_per_day_depr):
""" "
Returns monthly depreciation amount based on prev per day depr
Calculate per day depr only for the first month
"""
from_date, days_in_month = _get_total_days(
fb_row.depreciation_start_date, schedule_idx, cint(fb_row.frequency_of_depreciation)
)
return (prev_per_day_depr * days_in_month), prev_per_day_depr
def get_per_day_depr(
fb_row,
value_after_depreciation,
from_date,
):
to_date = add_days(add_years(from_date, 1), -1)
total_days = date_diff(to_date, from_date) + 1
per_day_depr = (flt(value_after_depreciation) * (flt(fb_row.rate_of_depreciation) / 100)) / total_days
return per_day_depr
def _get_total_days(depreciation_start_date, schedule_idx, frequency_of_depreciation):
from_date = add_months(depreciation_start_date, (schedule_idx - 1) * frequency_of_depreciation)
to_date = add_months(from_date, frequency_of_depreciation)
if is_last_day_of_the_month(depreciation_start_date):
to_date = get_last_day(to_date)
from_date = add_days(get_last_day(from_date), 1)
return from_date, date_diff(to_date, from_date) + 1

View File

@@ -90,7 +90,8 @@
"fieldname": "rate_of_depreciation",
"fieldtype": "Percent",
"label": "Rate of Depreciation (%)",
"mandatory_depends_on": "eval:doc.depreciation_method == 'Written Down Value'"
"mandatory_depends_on": "eval:doc.depreciation_method == 'Written Down Value'",
"no_copy": 1
},
{
"fieldname": "salvage_value_percentage",
@@ -116,6 +117,7 @@
"fieldname": "total_number_of_booked_depreciations",
"fieldtype": "Int",
"label": "Total Number of Booked Depreciations ",
"no_copy": 1,
"read_only": 1
},
{
@@ -138,7 +140,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2024-12-19 17:50:24.012434",
"modified": "2025-01-06 17:14:51.836803",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Finance Book",

View File

@@ -157,6 +157,7 @@
"options": "Asset Repair Consumed Item"
},
{
"fetch_from": "company.cost_center",
"fieldname": "cost_center",
"fieldtype": "Link",
"label": "Cost Center",
@@ -258,7 +259,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2024-12-23 18:08:35.159964",
"modified": "2024-12-27 18:11:40.548727",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Repair",

View File

@@ -12,7 +12,7 @@ from erpnext.assets.doctype.asset.asset import get_asset_account
from erpnext.assets.doctype.asset_activity.asset_activity import add_asset_activity
from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule import (
get_depr_schedule,
make_new_active_asset_depr_schedules_and_cancel_current_ones,
reschedule_depreciation,
)
from erpnext.controllers.accounts_controller import AccountsController
@@ -144,30 +144,27 @@ class AssetRepair(AccountsController):
self.total_repair_cost = flt(self.repair_cost) + flt(self.consumed_items_cost)
def on_submit(self):
self.asset_doc.flags.increase_in_asset_value_due_to_repair = False
self.decrease_stock_quantity()
if self.get("capitalize_repair_cost"):
self.update_asset_value()
self.make_gl_entries()
self.set_increase_in_asset_life()
depreciation_note = self.get_depreciation_note()
make_new_active_asset_depr_schedules_and_cancel_current_ones(self.asset_doc, depreciation_note)
reschedule_depreciation(self.asset_doc, depreciation_note)
self.add_asset_activity()
self.make_gl_entries()
def on_cancel(self):
self.asset_doc = frappe.get_doc("Asset", self.asset)
if self.get("capitalize_repair_cost"):
self.asset_doc.flags.increase_in_asset_value_due_to_repair = True
self.update_asset_value()
self.make_gl_entries(cancel=True)
self.set_increase_in_asset_life()
depreciation_note = self.get_depreciation_note()
make_new_active_asset_depr_schedules_and_cancel_current_ones(self.asset_doc, depreciation_note)
reschedule_depreciation(self.asset_doc, depreciation_note)
self.add_asset_activity()
def after_delete(self):
@@ -358,9 +355,10 @@ class AssetRepair(AccountsController):
def set_increase_in_asset_life(self):
if self.asset_doc.calculate_depreciation and cint(self.increase_in_asset_life) > 0:
for row in self.asset_doc.finance_books:
row.increase_in_asset_life = row.increase_in_asset_life + (
row.increase_in_asset_life = cint(row.increase_in_asset_life) + (
cint(self.increase_in_asset_life) * (1 if self.docstatus == 1 else -1)
)
row.db_update()
def get_depreciation_note(self):
return _("This schedule was created when Asset {0} was repaired through Asset Repair {1}.").format(

View File

@@ -18,7 +18,9 @@ from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_sched
get_asset_depr_schedule_doc,
get_temp_asset_depr_schedule_doc,
)
from erpnext.assets.doctype.asset_depreciation_schedule.utils import get_asset_shift_factors_map
from erpnext.erpnext.assets.doctype.asset_depreciation_schedule.deppreciation_schedule_controller import (
get_asset_shift_factors_map,
)
class AssetShiftAllocation(Document):

View File

@@ -14,7 +14,7 @@ from erpnext.assets.doctype.asset.asset import get_asset_value_after_depreciatio
from erpnext.assets.doctype.asset.depreciation import get_depreciation_accounts
from erpnext.assets.doctype.asset_activity.asset_activity import add_asset_activity
from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule import (
make_new_active_asset_depr_schedules_and_cancel_current_ones,
reschedule_depreciation,
)
@@ -64,7 +64,7 @@ class AssetValueAdjustment(Document):
self.current_asset_value = get_asset_value_after_depreciation(self.asset, self.finance_book)
def on_submit(self):
self.make_depreciation_entry()
self.make_asset_revaluation_entry()
self.update_asset()
add_asset_activity(
self.asset,
@@ -83,7 +83,7 @@ class AssetValueAdjustment(Document):
),
)
def make_depreciation_entry(self):
def make_asset_revaluation_entry(self):
asset = frappe.get_doc("Asset", self.asset)
(
fixed_asset_account,
@@ -170,7 +170,7 @@ class AssetValueAdjustment(Document):
def update_asset(self):
asset = self.update_asset_value_after_depreciation()
note = self.get_adjustment_note()
make_new_active_asset_depr_schedules_and_cancel_current_ones(asset, note)
reschedule_depreciation(asset, note)
def update_asset_value_after_depreciation(self):
difference_amount = self.difference_amount if self.docstatus == 1 else -1 * self.difference_amount

View File

@@ -411,3 +411,4 @@ erpnext.patches.v15_0.update_payment_schedule_fields_in_invoices
erpnext.patches.v15_0.rename_group_by_to_categorize_by
execute:frappe.db.set_single_value("Accounts Settings", "receivable_payable_fetch_method", "Buffered Cursor")
erpnext.patches.v14_0.set_update_price_list_based_on
erpnext.patches.v15_0.update_journal_entry_type

View File

@@ -22,7 +22,7 @@ def execute():
asset_depr_schedule_doc.name,
{"docstatus": 1, "status": "Active"},
)
update_depreciation_schedules(depreciation_schedules, asset_depr_schedule_doc.name)

View File

@@ -0,0 +1,18 @@
import frappe
def execute():
custom_je_type = frappe.db.get_value(
"Property Setter",
{"doc_type": "Journal Entry", "field_name": "voucher_type", "property": "options"},
["name", "value"],
)
if custom_je_type:
custom_je_type.value += "\nAsset Disposal"
frappe.db.set_value("Property Setter", custom_je_type.name, "value", custom_je_type.value)
scrapped_journal_entries = frappe.get_all(
"Asset", filters={"journal_entry_for_scrap": ["is", "not set"]}, fields=["name"]
)
for je in scrapped_journal_entries:
frappe.db.set_value("Journal Entry", je.name, "voucher_type", "Asset Disposal")