fix: Removed conflict

This commit is contained in:
Nabin Hait
2024-12-11 10:44:13 +05:30
committed by Khushi Rawat
parent c1edbc7e4b
commit c9487e0427
7 changed files with 721 additions and 668 deletions

View File

@@ -34,7 +34,6 @@ from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_sched
get_asset_depr_schedule_doc,
get_depr_schedule,
make_draft_asset_depr_schedules,
make_draft_asset_depr_schedules_if_not_present,
update_draft_asset_depr_schedules,
)
from erpnext.controllers.accounts_controller import AccountsController
@@ -131,30 +130,55 @@ class Asset(AccountsController):
self.set_missing_values()
self.validate_gross_and_purchase_amount()
self.validate_finance_books()
if not self.split_from:
self.prepare_depreciation_data()
if self.calculate_depreciation:
update_draft_asset_depr_schedules(self)
if frappe.db.exists("Asset", self.name):
asset_depr_schedules_names = make_draft_asset_depr_schedules_if_not_present(self)
if asset_depr_schedules_names:
asset_depr_schedules_links = get_comma_separated_links(
asset_depr_schedules_names, "Asset Depreciation Schedule"
)
frappe.msgprint(
_(
"Asset Depreciation Schedules created:<br>{0}<br><br>Please check, edit if needed, and submit the Asset."
).format(asset_depr_schedules_links)
)
self.validate_expected_value_after_useful_life()
self.set_total_booked_depreciations()
self.total_asset_cost = self.gross_purchase_amount
self.status = self.get_status()
def create_asset_depreciation_schedule(self):
if self.split_from or not self.calculate_depreciation:
return
self.set_depr_rate_and_value_after_depreciation()
schedules = []
for row in self.get("finance_books"):
self.validate_asset_finance_books(row)
if not row.rate_of_depreciation:
row.rate_of_depreciation = self.get_depreciation_rate(row, on_validate=True)
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.save()
schedules.append(schedule_doc.name)
self.show_schedule_creation_message(schedules)
def set_depr_rate_and_value_after_depreciation(self):
if self.calculate_depreciation:
self.value_after_depreciation = 0
self.set_depreciation_rate()
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:
asset_depr_schedules_links = get_comma_separated_links(schedules, "Asset Depreciation Schedule")
frappe.msgprint(
_(
"Asset Depreciation Schedules created/updated:<br>{0}<br><br>Please check, edit if needed, and submit the Asset."
).format(asset_depr_schedules_links)
)
def on_update(self):
self.create_asset_depreciation_schedule()
def on_submit(self):
self.validate_in_use_date()
self.make_asset_movement()
@@ -178,17 +202,17 @@ class Asset(AccountsController):
self.db_set("booked_fixed_asset", 0)
add_asset_activity(self.name, _("Asset cancelled"))
def after_insert(self):
if self.calculate_depreciation and not self.split_from:
asset_depr_schedules_names = make_draft_asset_depr_schedules(self)
asset_depr_schedules_links = get_comma_separated_links(
asset_depr_schedules_names, "Asset Depreciation Schedule"
)
frappe.msgprint(
_(
"Asset Depreciation Schedules created:<br>{0}<br><br>Please check, edit if needed, and submit the Asset."
).format(asset_depr_schedules_links)
)
# def after_insert(self):
# if self.calculate_depreciation and not self.split_from:
# asset_depr_schedules_names = make_draft_asset_depr_schedules(self)
# asset_depr_schedules_links = get_comma_separated_links(
# asset_depr_schedules_names, "Asset Depreciation Schedule"
# )
# frappe.msgprint(
# _(
# "Asset Depreciation Schedules created:<br>{0}<br><br>Please check, edit if needed, and submit the Asset."
# ).format(asset_depr_schedules_links)
# )
if (
not frappe.db.exists(
{
@@ -250,16 +274,6 @@ class Asset(AccountsController):
if self.is_existing_asset and self.purchase_invoice:
frappe.throw(_("Purchase Invoice cannot be made against an existing asset {0}").format(self.name))
def prepare_depreciation_data(self):
if self.calculate_depreciation:
self.value_after_depreciation = 0
self.set_depreciation_rate()
else:
self.finance_books = []
self.value_after_depreciation = flt(self.gross_purchase_amount) - flt(
self.opening_accumulated_depreciation
)
def validate_item(self):
item = frappe.get_cached_value(
"Item", self.item_code, ["is_fixed_asset", "is_stock_item", "disabled"], as_dict=1
@@ -344,6 +358,22 @@ class Asset(AccountsController):
title=_("Missing Finance Book"),
)
# def set_depreciation_start_date(self):
# if not self.calculate_depreciation:
# return
# for d in self.get("finance_books"):
# if not d.depreciation_start_date:
# if self.is_existing_asset and self.opening_number_of_booked_depreciations:
# months = d.frequency_of_depreciation * self.opening_number_of_booked_depreciations
# else:
# months = d.frequency_of_depreciation
# d.depreciation_start_date = get_last_day(
# add_months(self.available_for_use_date, months)
# )
def validate_precision(self):
if self.gross_purchase_amount:
self.gross_purchase_amount = flt(
@@ -456,61 +486,65 @@ class Asset(AccountsController):
frappe.throw(
_("Row {0}: Expected Value After Useful Life must be less than Gross Purchase Amount").format(
row.idx
),
title=_("Invalid Schedule"),
)
)
if not row.depreciation_start_date:
if not self.available_for_use_date:
frappe.throw(
_("Row {0}: Depreciation Start Date is required").format(row.idx),
title=_("Invalid Schedule"),
)
row.depreciation_start_date = get_last_day(self.available_for_use_date)
self.validate_depreciation_start_date(row)
if not self.is_existing_asset:
self.opening_accumulated_depreciation = 0
self.opening_number_of_booked_depreciations = 0
else:
depreciable_amount = flt(
flt(self.gross_purchase_amount) - flt(row.expected_value_after_useful_life),
self.precision("gross_purchase_amount"),
)
if flt(self.opening_accumulated_depreciation) > depreciable_amount:
frappe.throw(
_("Opening Accumulated Depreciation must be less than or equal to {0}").format(
depreciable_amount
)
)
self.validate_opening_depreciation_values(row)
if self.opening_accumulated_depreciation:
if not self.opening_number_of_booked_depreciations:
frappe.throw(_("Please set Opening Number of Booked Depreciations"))
else:
self.opening_number_of_booked_depreciations = 0
if flt(row.total_number_of_depreciations) <= cint(self.opening_number_of_booked_depreciations):
frappe.throw(
_(
"Row {0}: Total Number of Depreciations cannot be less than or equal to Opening Number of Booked Depreciations"
).format(row.idx),
title=_("Invalid Schedule"),
)
if row.depreciation_start_date and getdate(row.depreciation_start_date) < getdate(self.purchase_date):
def validate_opening_depreciation_values(self, row):
row.expected_value_after_useful_life = flt(
row.expected_value_after_useful_life, self.precision("gross_purchase_amount")
)
depreciable_amount = flt(
flt(self.gross_purchase_amount) - flt(row.expected_value_after_useful_life),
self.precision("gross_purchase_amount"),
)
if flt(self.opening_accumulated_depreciation) > depreciable_amount:
frappe.throw(
_("Depreciation Row {0}: Next Depreciation Date cannot be before Purchase Date").format(
row.idx
_("Row #{0}: Opening Accumulated Depreciation must be less than or equal to {1}").format(
row.idx, depreciable_amount
)
)
if row.depreciation_start_date and getdate(row.depreciation_start_date) < getdate(
self.available_for_use_date
):
if self.opening_accumulated_depreciation:
if not self.opening_number_of_booked_depreciations:
frappe.throw(_("Please set opening number of booked depreciations"))
else:
self.opening_number_of_booked_depreciations = 0
if flt(row.total_number_of_depreciations) <= cint(self.opening_number_of_booked_depreciations):
frappe.throw(
_(
"Depreciation Row {0}: Next Depreciation Date cannot be before Available-for-use Date"
).format(row.idx)
"Row #{0}: Total Number of Depreciations cannot be less than or equal to Opening Number of Booked Depreciations"
).format(row.idx),
title=_("Invalid Schedule"),
)
def validate_depreciation_start_date(self, row):
if row.depreciation_start_date:
if getdate(row.depreciation_start_date) < getdate(self.purchase_date):
frappe.throw(
_("Row #{0}: Next Depreciation Date cannot be before Purchase Date").format(row.idx)
)
if getdate(row.depreciation_start_date) < getdate(self.available_for_use_date):
frappe.throw(
_("Row #{0}: Next Depreciation Date cannot be before Available-for-use Date").format(
row.idx
)
)
else:
frappe.throw(
_("Row #{0}: Depreciation Start Date is required").format(row.idx),
title=_("Invalid Schedule"),
)
def set_total_booked_depreciations(self):
@@ -530,15 +564,11 @@ class Asset(AccountsController):
if not depr_schedule:
continue
accumulated_depreciation_after_full_schedule = [
d.accumulated_depreciation_amount for d in depr_schedule
]
accumulated_depreciation_after_full_schedule = max(
[d.accumulated_depreciation_amount for d in depr_schedule]
)
if accumulated_depreciation_after_full_schedule:
accumulated_depreciation_after_full_schedule = max(
accumulated_depreciation_after_full_schedule
)
asset_value_after_full_schedule = flt(
flt(self.gross_purchase_amount) - flt(accumulated_depreciation_after_full_schedule),
self.precision("gross_purchase_amount"),
@@ -820,53 +850,62 @@ class Asset(AccountsController):
if isinstance(args, str):
args = json.loads(args)
float_precision = cint(frappe.db.get_default("float_precision")) or 2
rate_field_precision = frappe.get_precision(args.doctype, "rate_of_depreciation") or 2
if args.get("depreciation_method") == "Double Declining Balance":
return 200.0 / (
return self.get_double_declining_balance_rate(args, rate_field_precision)
elif args.get("depreciation_method") == "Written Down Value":
return self.get_written_down_value_rate(args, rate_field_precision, on_validate)
def get_double_declining_balance_rate(self, args, rate_field_precision):
return flt(
200.0
/ (
(
flt(args.get("total_number_of_depreciations"), 2)
* flt(args.get("frequency_of_depreciation"))
)
/ 12
),
rate_field_precision,
)
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
):
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")
)
else:
value = flt(args.get("expected_value_after_useful_life")) / (
flt(self.gross_purchase_amount) - flt(self.opening_accumulated_depreciation)
)
if args.get("depreciation_method") == "Written Down Value":
if (
args.get("rate_of_depreciation")
and on_validate
and not self.flags.increase_in_asset_value_due_to_repair
):
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")
)
else:
value = flt(args.get("expected_value_after_useful_life")) / (
flt(self.gross_purchase_amount) - flt(self.opening_accumulated_depreciation)
)
depreciation_rate = math.pow(
value,
1.0
/ (
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"))
flt(args.get("total_number_of_depreciations"), 2)
- flt(self.opening_number_of_booked_depreciations)
)
/ 12
),
)
* flt(args.get("frequency_of_depreciation"))
)
/ 12
),
)
return flt((100 * (1 - depreciation_rate)), float_precision)
return flt((100 * (1 - depreciation_rate)), rate_field_precision)
def has_gl_entries(doctype, docname, target_account):

View File

@@ -34,6 +34,8 @@ from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_sched
_get_pro_rata_amt,
get_asset_depr_schedule_doc,
get_depr_schedule,
)
from erpnext.assets.doctype.asset_depreciation_schedule.utils import (
get_depreciation_amount,
)
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (

View File

@@ -25,6 +25,7 @@
"column_break_8",
"frequency_of_depreciation",
"expected_value_after_useful_life",
"value_after_depreciation",
"depreciation_schedule_section",
"depreciation_schedule",
"details_section",
@@ -38,6 +39,7 @@
"fieldtype": "Link",
"in_list_view": 1,
"label": "Asset",
"link_filters": "[[\"Asset\",\"docstatus\",\"<\",\"2\"],[\"Asset\",\"company\",\"=\",\"eval:doc.company\"]]",
"options": "Asset",
"reqd": 1
},
@@ -202,12 +204,18 @@
"label": "Company",
"options": "Company",
"read_only": 1
},
{
"fieldname": "value_after_depreciation",
"fieldtype": "Currency",
"label": "Value After Depreciation",
"read_only": 1
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2024-03-27 13:06:34.135004",
"modified": "2024-12-02 17:54:20.635668",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Depreciation Schedule",

View File

@@ -21,6 +21,9 @@ 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,
)
class AssetDepreciationSchedule(Document):
@@ -32,9 +35,7 @@ class AssetDepreciationSchedule(Document):
if TYPE_CHECKING:
from frappe.types import DF
from erpnext.assets.doctype.depreciation_schedule.depreciation_schedule import (
DepreciationSchedule,
)
from erpnext.assets.doctype.depreciation_schedule.depreciation_schedule import DepreciationSchedule
amended_from: DF.Link | None
asset: DF.Link
@@ -51,12 +52,13 @@ class AssetDepreciationSchedule(Document):
gross_purchase_amount: DF.Currency
naming_series: DF.Literal["ACC-ADS-.YYYY.-"]
notes: DF.SmallText | None
opening_number_of_booked_depreciations: DF.Int
opening_accumulated_depreciation: DF.Currency
opening_number_of_booked_depreciations: DF.Int
rate_of_depreciation: DF.Percent
shift_based: DF.Check
status: DF.Literal["Draft", "Active", "Cancelled"]
total_number_of_depreciations: DF.Int
value_after_depreciation: DF.Currency
# end: auto-generated types
def before_save(self):
@@ -145,50 +147,9 @@ class AssetDepreciationSchedule(Document):
date_of_return=None,
update_asset_finance_book_row=True,
):
have_asset_details_been_modified = self.have_asset_details_been_modified(asset_doc)
not_manual_depr_or_have_manual_depr_details_been_modified = (
self.not_manual_depr_or_have_manual_depr_details_been_modified(row)
)
self.set_draft_asset_depr_schedule_details(asset_doc, row)
if self.should_prepare_depreciation_schedule(
have_asset_details_been_modified, not_manual_depr_or_have_manual_depr_details_been_modified
):
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 have_asset_details_been_modified(self, asset_doc):
return (
asset_doc.gross_purchase_amount != self.gross_purchase_amount
or asset_doc.opening_accumulated_depreciation != self.opening_accumulated_depreciation
or asset_doc.opening_number_of_booked_depreciations != self.opening_number_of_booked_depreciations
)
def not_manual_depr_or_have_manual_depr_details_been_modified(self, row):
return (
self.depreciation_method != "Manual"
or row.total_number_of_depreciations != self.total_number_of_depreciations
or row.frequency_of_depreciation != self.frequency_of_depreciation
or getdate(row.depreciation_start_date) != self.get("depreciation_schedule")[0].schedule_date
or row.expected_value_after_useful_life != self.expected_value_after_useful_life
)
def should_prepare_depreciation_schedule(
self, have_asset_details_been_modified, not_manual_depr_or_have_manual_depr_details_been_modified
):
if not self.get("depreciation_schedule"):
return True
old_asset_depr_schedule_doc = self.get_doc_before_save()
if self.docstatus != 0 and not old_asset_depr_schedule_doc:
return True
if have_asset_details_been_modified or not_manual_depr_or_have_manual_depr_details_been_modified:
return True
return False
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
@@ -214,11 +175,11 @@ class AssetDepreciationSchedule(Document):
update_asset_finance_book_row=True,
value_after_depreciation=None,
):
if not self.get("depreciation_schedule"):
self.depreciation_schedule = []
# if not self.get("depreciation_schedule"):
# self.depreciation_schedule = []
if not asset_doc.available_for_use_date:
return
# if not asset_doc.available_for_use_date:
# return
start = self.clear_depr_schedule()
@@ -227,6 +188,9 @@ class AssetDepreciationSchedule(Document):
)
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 = []
@@ -254,7 +218,7 @@ class AssetDepreciationSchedule(Document):
update_asset_finance_book_row,
value_after_depreciation,
):
asset_doc.validate_asset_finance_books(row)
# asset_doc.validate_asset_finance_books(row)
if not value_after_depreciation:
value_after_depreciation = _get_value_after_depreciation_for_making_schedule(asset_doc, row)
@@ -263,13 +227,7 @@ class AssetDepreciationSchedule(Document):
if update_asset_finance_book_row:
row.db_update()
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
final_number_of_depreciations, has_pro_rata = self.get_final_number_of_depreciations(asset_doc, row)
has_wdv_or_dd_non_yearly_pro_rata = False
if (
@@ -285,7 +243,7 @@ class AssetDepreciationSchedule(Document):
number_of_pending_depreciations = final_number_of_depreciations - start
yearly_opening_wdv = value_after_depreciation
current_fiscal_year_end_date = None
self.current_fiscal_year_end_date = None
prev_per_day_depr = True
for n in range(start, final_number_of_depreciations):
# If depreciation is already completed (for double declining balance)
@@ -295,16 +253,12 @@ class AssetDepreciationSchedule(Document):
schedule_date = get_last_day(
add_months(row.depreciation_start_date, n * cint(row.frequency_of_depreciation))
)
if not current_fiscal_year_end_date:
current_fiscal_year_end_date = get_fiscal_year(row.depreciation_start_date)[2]
elif getdate(schedule_date) > getdate(current_fiscal_year_end_date):
current_fiscal_year_end_date = add_years(current_fiscal_year_end_date, 1)
if self.has_fiscal_year_changed(row, n):
yearly_opening_wdv = value_after_depreciation
if n > 0 and len(self.get("depreciation_schedule")) > n - 1:
prev_depreciation_amount = self.get("depreciation_schedule")[n - 1].depreciation_amount
else:
prev_depreciation_amount = 0
prev_depreciation_amount = self.get_prev_depreciation_amount(n)
depreciation_amount, prev_per_day_depr = get_depreciation_amount(
self,
asset_doc,
@@ -317,146 +271,120 @@ class AssetDepreciationSchedule(Document):
number_of_pending_depreciations,
prev_per_day_depr,
)
if not has_pro_rata or (
n < (cint(final_number_of_depreciations) - 1) or final_number_of_depreciations == 2
):
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)
schedule_date = self.get_next_schedule_date(
row, n, schedule_date, 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):
from_date = add_months(
getdate(asset_doc.available_for_use_date),
(asset_doc.opening_number_of_booked_depreciations * row.frequency_of_depreciation),
self.get_depreciation_amount_for_disposal(
asset_doc, row, n, schedule_date, date_of_disposal, depreciation_amount
)
if is_last_day_of_the_month(getdate(asset_doc.available_for_use_date)):
from_date = get_last_day(from_date)
if self.depreciation_schedule:
from_date = add_days(self.depreciation_schedule[-1].schedule_date, 1)
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, n)
break
# For first row
if (
n == 0
and (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
):
from_date = asset_doc.available_for_use_date
# needed to calc depr amount for available_for_use_date too
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,
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
)
if flt(depreciation_amount, asset_doc.precision("gross_purchase_amount")) <= 0:
frappe.throw(
_(
"Gross Purchase Amount Too Low: {0} cannot be depreciated over {1} cycles with a frequency of {2} depreciations."
).format(
frappe.bold(asset_doc.gross_purchase_amount),
frappe.bold(row.total_number_of_depreciations),
frappe.bold(row.frequency_of_depreciation),
)
)
elif n == 0 and has_wdv_or_dd_non_yearly_pro_rata and self.opening_accumulated_depreciation:
if not is_first_day_of_the_month(getdate(asset_doc.available_for_use_date)):
from_date = get_last_day(
add_months(
getdate(asset_doc.available_for_use_date),
(
(self.opening_number_of_booked_depreciations - 1)
* row.frequency_of_depreciation
),
)
)
else:
from_date = add_months(
getdate(add_days(asset_doc.available_for_use_date, -1)),
(self.opening_number_of_booked_depreciations * row.frequency_of_depreciation),
)
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,
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
)
# For last row
elif has_pro_rata and n == cint(final_number_of_depreciations) - 1:
if not asset_doc.flags.increase_in_asset_life:
# In case of increase_in_asset_life, the asset.to_date is already set on asset_repair submission
asset_doc.to_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)):
asset_doc.to_date = get_last_day(asset_doc.to_date)
depreciation_amount_without_pro_rata = depreciation_amount
depreciation_amount, days, months = _get_pro_rata_amt(
row,
depreciation_amount,
schedule_date,
asset_doc.to_date,
has_wdv_or_dd_non_yearly_pro_rata,
)
depreciation_amount = self.get_adjusted_depreciation_amount(
depreciation_amount_without_pro_rata, depreciation_amount
)
schedule_date = add_days(schedule_date, days - 1)
if not depreciation_amount:
continue
depreciation_amount = flt(depreciation_amount, asset_doc.precision("gross_purchase_amount"))
break
value_after_depreciation = flt(
value_after_depreciation - flt(depreciation_amount),
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
if (
n == 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, asset_doc.precision("gross_purchase_amount"))
skip_row = True
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)
# to ensure that final accumulated depreciation amount is accurate
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
return final_number_of_depreciations, has_pro_rata
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, schedule_date, has_pro_rata, should_get_last_day, final_number_of_depreciations=None
):
if not has_pro_rata or (
n < (cint(final_number_of_depreciations) - 1) or final_number_of_depreciations == 2
):
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_amount_for_first_row()
depreciation_amount_for_first_row = self.get("depreciation_schedule")[0].depreciation_amount
if (
depreciation_amount_for_first_row + depreciation_amount_for_last_row
@@ -468,8 +396,84 @@ class AssetDepreciationSchedule(Document):
return depreciation_amount_for_last_row
def get_depreciation_amount_for_first_row(self):
return self.get("depreciation_schedule")[0].depreciation_amount
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
"""
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
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)
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 asset_doc.flags.increase_in_asset_life:
# In case of increase_in_asset_life, the asset.to_date is already set on asset_repair submission
asset_doc.to_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)):
asset_doc.to_date = get_last_day(asset_doc.to_date)
if self.opening_accumulated_depreciation:
depreciation_amount, days, months = _get_pro_rata_amt(
row,
depreciation_amount,
schedule_date,
asset_doc.to_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
depreciation_amount -= self.get("depreciation_schedule")[0].depreciation_amount
schedule_date = add_days(schedule_date, days - 1)
return depreciation_amount, schedule_date
def adjust_depr_amount_for_salvage_value(
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:
@@ -576,7 +580,7 @@ def _check_is_pro_rata(asset_doc, row, wdv_or_dd_non_yearly=False):
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(asset_doc, row, wdv_or_dd_non_yearly=False)
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:
@@ -595,11 +599,12 @@ def _check_is_pro_rata(asset_doc, row, wdv_or_dd_non_yearly=False):
return has_pro_rata
def _get_modified_available_for_use_date(asset_doc, row, wdv_or_dd_non_yearly=False):
def _get_modified_available_for_use_date_for_existing_assets(asset_doc, row):
"""
if Asset has opening booked depreciations = 9,
if Asset has opening booked depreciations = 3,
frequency of depreciation = 3,
available for use date = 17-07-2023,
depreciation start date = 30-04-2024
depreciation start date = 30-06-2024
then from date should be 01-04-2024
"""
if asset_doc.opening_number_of_booked_depreciations > 0:
@@ -641,359 +646,6 @@ def get_total_days(date, frequency):
return date_diff(date, period_start_date)
def get_depreciation_amount(
asset_depr_schedule,
asset,
depreciable_value,
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, number_of_pending_depreciations
), None
else:
return get_wdv_or_dd_depr_amount(
asset,
fb_row,
depreciable_value,
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, row, schedule_idx, number_of_pending_depreciations
):
if row.shift_based:
return get_shift_depr_amount(asset_depr_schedule, asset, row, schedule_idx)
# if the Depreciation Schedule is being modified after Asset Repair due to increase in asset life and value
if asset.flags.increase_in_asset_life:
return (flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life)) / (
date_diff(asset.to_date, asset.available_for_use_date) / 365
)
# if the Depreciation Schedule is being modified after Asset Repair due to increase in asset value
elif asset.flags.increase_in_asset_value_due_to_repair:
return (flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life)) / flt(
number_of_pending_depreciations
)
# if the Depreciation Schedule is being modified after Asset Value Adjustment due to decrease in asset value
elif asset.flags.decrease_in_asset_value_due_to_value_adjustment:
if row.daily_prorata_based:
amount = flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life)
return get_daily_prorata_based_straight_line_depr(
asset,
row,
schedule_idx,
number_of_pending_depreciations,
amount,
)
else:
return (
flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life)
) / number_of_pending_depreciations
# if the Depreciation Schedule is being prepared for the first time
else:
if row.daily_prorata_based:
amount = flt(asset.gross_purchase_amount) - flt(row.expected_value_after_useful_life)
return get_daily_prorata_based_straight_line_depr(
asset, row, schedule_idx, number_of_pending_depreciations, amount
)
else:
depreciation_amount = (
flt(asset.gross_purchase_amount) - flt(row.expected_value_after_useful_life)
) / flt(row.total_number_of_depreciations)
return depreciation_amount
def get_daily_prorata_based_straight_line_depr(
asset, row, schedule_idx, number_of_pending_depreciations, amount
):
daily_depr_amount = get_daily_depr_amount(asset, row, schedule_idx, amount)
from_date, total_depreciable_days = _get_total_days(
row.depreciation_start_date, schedule_idx, row.frequency_of_depreciation
)
return daily_depr_amount * total_depreciable_days
def get_daily_depr_amount(asset, 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(
row.depreciation_start_date,
flt(
row.total_number_of_depreciations
- asset.opening_number_of_booked_depreciations
- 1
)
* row.frequency_of_depreciation,
)
),
add_days(
get_last_day(
add_months(
row.depreciation_start_date,
(
row.frequency_of_depreciation
* (asset.opening_number_of_booked_depreciations + 1)
)
* -1,
),
),
1,
),
)
+ 1
)
return amount / total_days
else:
total_years = (
flt(
(row.total_number_of_depreciations - row.total_number_of_booked_depreciations)
* row.frequency_of_depreciation
)
/ 12
)
every_year_depr = amount / total_years
depr_period_start_date = add_days(
get_last_day(add_months(row.depreciation_start_date, row.frequency_of_depreciation * -1)), 1
)
year_start_date = add_years(
depr_period_start_date, ((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, 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(row.expected_value_after_useful_life)
) / flt(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(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,
depreciable_value,
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,
depreciable_value,
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,
depreciable_value,
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,
depreciable_value,
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,
depreciable_value,
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,
depreciable_value,
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(depreciable_value) * (flt(fb_row.rate_of_depreciation) / 100)
else:
if has_wdv_or_dd_non_yearly_pro_rata:
if schedule_idx == 0:
return flt(depreciable_value) * (flt(fb_row.rate_of_depreciation) / 100)
elif schedule_idx % (12 / cint(fb_row.frequency_of_depreciation)) == 1:
return (
flt(depreciable_value)
* 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(depreciable_value)
* 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,
depreciable_value,
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(depreciable_value) * (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, depreciable_value)
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, depreciable_value)
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, depreciable_value):
"""
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, depreciable_value, 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,
depreciable_value,
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(depreciable_value) * (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
def make_draft_asset_depr_schedules_if_not_present(asset_doc):
asset_depr_schedules_names = []
for row in asset_doc.get("finance_books"):
asset_depr_schedule = get_asset_depr_schedule_name(
asset_doc.name, ["Draft", "Active"], row.finance_book
)
if not asset_depr_schedule:
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_schedules(asset_doc):
asset_depr_schedules_names = []

View File

@@ -0,0 +1,351 @@
import frappe
from frappe.utils import (
add_days,
add_months,
add_years,
cint,
date_diff,
flt,
get_last_day,
is_last_day_of_the_month,
)
import erpnext
def get_depreciation_amount(
asset_depr_schedule,
asset,
depreciable_value,
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, number_of_pending_depreciations
), None
else:
return get_wdv_or_dd_depr_amount(
asset,
fb_row,
depreciable_value,
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, row, schedule_idx, number_of_pending_depreciations
):
if row.shift_based:
return get_shift_depr_amount(asset_depr_schedule, asset, row, schedule_idx)
# if the Depreciation Schedule is being modified after Asset Repair due to increase in asset life and value
if asset.flags.increase_in_asset_life:
return (flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life)) / (
date_diff(asset.to_date, asset.available_for_use_date) / 365
)
# if the Depreciation Schedule is being modified after Asset Repair due to increase in asset value
elif asset.flags.increase_in_asset_value_due_to_repair:
return (flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life)) / flt(
number_of_pending_depreciations
)
# if the Depreciation Schedule is being modified after Asset Value Adjustment due to decrease in asset value
elif asset.flags.decrease_in_asset_value_due_to_value_adjustment:
if row.daily_prorata_based:
amount = flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life)
return get_daily_prorata_based_straight_line_depr(
asset,
row,
schedule_idx,
number_of_pending_depreciations,
amount,
)
else:
return (
flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life)
) / number_of_pending_depreciations
# if the Depreciation Schedule is being prepared for the first time
else:
if row.daily_prorata_based:
amount = flt(asset.gross_purchase_amount) - flt(row.expected_value_after_useful_life)
return get_daily_prorata_based_straight_line_depr(
asset, row, schedule_idx, number_of_pending_depreciations, amount
)
else:
depreciation_amount = (
flt(asset.gross_purchase_amount) - flt(row.expected_value_after_useful_life)
) / flt(row.total_number_of_depreciations)
return depreciation_amount
def get_daily_prorata_based_straight_line_depr(
asset, row, schedule_idx, number_of_pending_depreciations, amount
):
daily_depr_amount = get_daily_depr_amount(asset, row, schedule_idx, amount)
from_date, total_depreciable_days = _get_total_days(
row.depreciation_start_date, schedule_idx, row.frequency_of_depreciation
)
return daily_depr_amount * total_depreciable_days
def get_daily_depr_amount(asset, 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(
row.depreciation_start_date,
flt(
row.total_number_of_depreciations
- asset.opening_number_of_booked_depreciations
- 1
)
* row.frequency_of_depreciation,
)
),
add_days(
get_last_day(
add_months(
row.depreciation_start_date,
(
row.frequency_of_depreciation
* (asset.opening_number_of_booked_depreciations + 1)
)
* -1,
),
),
1,
),
)
+ 1
)
return amount / total_days
else:
total_years = (
flt(
(row.total_number_of_depreciations - row.total_number_of_booked_depreciations)
* row.frequency_of_depreciation
)
/ 12
)
every_year_depr = amount / total_years
depr_period_start_date = add_days(
get_last_day(add_months(row.depreciation_start_date, row.frequency_of_depreciation * -1)), 1
)
year_start_date = add_years(
depr_period_start_date, ((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, 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(row.expected_value_after_useful_life)
) / flt(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(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,
depreciable_value,
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,
depreciable_value,
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,
depreciable_value,
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,
depreciable_value,
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,
depreciable_value,
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,
depreciable_value,
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(depreciable_value) * (flt(fb_row.rate_of_depreciation) / 100)
else:
if has_wdv_or_dd_non_yearly_pro_rata:
if schedule_idx == 0:
return flt(depreciable_value) * (flt(fb_row.rate_of_depreciation) / 100)
elif schedule_idx % (12 / cint(fb_row.frequency_of_depreciation)) == 1:
return (
flt(depreciable_value)
* 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(depreciable_value)
* 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,
depreciable_value,
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(depreciable_value) * (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, depreciable_value)
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, depreciable_value)
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, depreciable_value):
"""
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, depreciable_value, 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,
depreciable_value,
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(depreciable_value) * (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

@@ -61,6 +61,7 @@
{
"fieldname": "depreciation_start_date",
"fieldtype": "Date",
"hidden": 1,
"in_list_view": 1,
"label": "Depreciation Posting Date",
"mandatory_depends_on": "eval:parent.doctype == 'Asset'"
@@ -100,7 +101,7 @@
"default": "0",
"fieldname": "daily_prorata_based",
"fieldtype": "Check",
"label": "Depreciate based on daily pro-rata"
"label": "Depreciate based on days per period"
},
{
"default": "0",

View File

@@ -16,9 +16,9 @@ from frappe.utils import (
from erpnext.assets.doctype.asset_activity.asset_activity import add_asset_activity
from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule import (
get_asset_depr_schedule_doc,
get_asset_shift_factors_map,
get_temp_asset_depr_schedule_doc,
)
from erpnext.assets.doctype.asset_depreciation_schedule.utils import get_asset_shift_factors_map
class AssetShiftAllocation(Document):