feat(india): reduced rate of depreciation as per IT Act (#25648)

* feat(india): reduced rate of depreciation as per IT Act

* refactor: check date difference instead of month difference

* feat: add test for regional feature
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 9aff144..8799275 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -195,8 +195,7 @@
 				# If depreciation is already completed (for double declining balance)
 				if skip_row: continue
 
-				depreciation_amount = self.get_depreciation_amount(value_after_depreciation,
-					d.total_number_of_depreciations, d)
+				depreciation_amount = get_depreciation_amount(self, value_after_depreciation, d)
 
 				if not has_pro_rata or n < cint(number_of_pending_depreciations) - 1:
 					schedule_date = add_months(d.depreciation_start_date,
@@ -208,7 +207,7 @@
 
 				# For first row
 				if has_pro_rata and n==0:
-					depreciation_amount, days, months = get_pro_rata_amt(d, depreciation_amount,
+					depreciation_amount, days, months = self.get_pro_rata_amt(d, depreciation_amount,
 						self.available_for_use_date, d.depreciation_start_date)
 
 					# For first depr schedule date will be the start date
@@ -220,7 +219,7 @@
 					to_date = add_months(self.available_for_use_date,
 						n * cint(d.frequency_of_depreciation))
 
-					depreciation_amount, days, months = get_pro_rata_amt(d,
+					depreciation_amount, days, months = self.get_pro_rata_amt(d,
 						depreciation_amount, schedule_date, to_date)
 
 					monthly_schedule_date = add_months(schedule_date, 1)
@@ -365,24 +364,6 @@
 	def get_value_after_depreciation(self, idx):
 		return flt(self.get('finance_books')[cint(idx)-1].value_after_depreciation)
 
-	def get_depreciation_amount(self, depreciable_value, total_number_of_depreciations, row):
-		precision = self.precision("gross_purchase_amount")
-
-		if row.depreciation_method in ("Straight Line", "Manual"):
-			depreciation_left = (cint(row.total_number_of_depreciations) - cint(self.number_of_depreciations_booked))
-
-			if not depreciation_left:
-				frappe.msgprint(_("All the depreciations has been booked"))
-				depreciation_amount = flt(row.expected_value_after_useful_life)
-				return depreciation_amount
-
-			depreciation_amount = (flt(row.value_after_depreciation) -
-				flt(row.expected_value_after_useful_life)) / depreciation_left
-		else:
-			depreciation_amount = flt(depreciable_value * (flt(row.rate_of_depreciation) / 100), precision)
-
-		return depreciation_amount
-
 	def validate_expected_value_after_useful_life(self):
 		for row in self.get('finance_books'):
 			accumulated_depreciation_after_full_schedule = [d.accumulated_depreciation_amount
@@ -575,6 +556,13 @@
 
 			return 100 * (1 - flt(depreciation_rate, float_precision))
 
+	def get_pro_rata_amt(self, row, depreciation_amount, from_date, to_date):
+		days = date_diff(to_date, from_date)
+		months = month_diff(to_date, from_date)
+		total_days = get_total_days(to_date, row.frequency_of_depreciation)
+
+		return (depreciation_amount * flt(days)) / flt(total_days), days, months
+
 def update_maintenance_status():
 	assets = frappe.get_all(
 		"Asset", filters={"docstatus": 1, "maintenance_required": 1}
@@ -758,15 +746,20 @@
 def is_cwip_accounting_enabled(asset_category):
 	return cint(frappe.db.get_value("Asset Category", asset_category, "enable_cwip_accounting"))
 
-def get_pro_rata_amt(row, depreciation_amount, from_date, to_date):
-	days = date_diff(to_date, from_date)
-	months = month_diff(to_date, from_date)
-	total_days = get_total_days(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)
 
 	return date_diff(date, period_start_date)
+
+@erpnext.allow_regional
+def get_depreciation_amount(asset, depreciable_value, row):
+	depreciation_left = flt(row.total_number_of_depreciations) - flt(asset.number_of_depreciations_booked)
+
+	if row.depreciation_method in ("Straight Line", "Manual"):
+		depreciation_amount = (flt(row.value_after_depreciation) -
+			flt(row.expected_value_after_useful_life)) / depreciation_left
+	else:
+		depreciation_amount = flt(depreciable_value * (flt(row.rate_of_depreciation) / 100))
+
+	return depreciation_amount
\ No newline at end of file
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index 40a8f85..30a270c 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -635,6 +635,45 @@
 		frappe.db.set_value("Asset Category Account", name, "capital_work_in_progress_account", cwip_acc)
 		frappe.db.get_value("Company", "_Test Company", "capital_work_in_progress_account", cwip_acc)
 
+	def test_discounted_wdv_depreciation_rate_for_indian_region(self):
+		# set indian company
+		company_flag = frappe.flags.company
+		frappe.flags.company = "_Test Company"
+
+		pr = make_purchase_receipt(item_code="Macbook Pro",
+			qty=1, rate=8000.0, location="Test Location")
+
+		asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, 'name')
+		asset = frappe.get_doc('Asset', asset_name)
+		asset.calculate_depreciation = 1
+		asset.available_for_use_date = '2030-06-12'
+		asset.purchase_date = '2030-01-01'
+		asset.append("finance_books", {
+			"expected_value_after_useful_life": 1000,
+			"depreciation_method": "Written Down Value",
+			"total_number_of_depreciations": 3,
+			"frequency_of_depreciation": 12,
+			"depreciation_start_date": "2030-12-31"
+		})
+		asset.save(ignore_permissions=True)
+
+		self.assertEqual(asset.finance_books[0].rate_of_depreciation, 50.0)
+
+		expected_schedules = [
+			["2030-12-31", 1106.85, 1106.85],
+			["2031-12-31", 3446.58, 4553.43],
+			["2032-12-31", 1723.29, 6276.72],
+			["2033-06-12", 723.28, 7000.00]
+		]
+
+		schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), flt(d.accumulated_depreciation_amount, 2)]
+			for d in asset.get("schedules")]
+
+		self.assertEqual(schedules, expected_schedules)
+
+		# reset indian company
+		frappe.flags.company = company_flag
+
 def create_asset_data():
 	if not frappe.db.exists("Asset Category", "Computers"):
 		create_asset_category()
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index bb6cd8b..9d1ce9b 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -426,7 +426,8 @@
 		'erpnext.hr.utils.calculate_annual_eligible_hra_exemption': 'erpnext.regional.india.utils.calculate_annual_eligible_hra_exemption',
 		'erpnext.hr.utils.calculate_hra_exemption_for_period': 'erpnext.regional.india.utils.calculate_hra_exemption_for_period',
 		'erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_regional_gl_entries': 'erpnext.regional.india.utils.make_regional_gl_entries',
-		'erpnext.controllers.accounts_controller.validate_einvoice_fields': 'erpnext.regional.india.e_invoice.utils.validate_einvoice_fields'
+		'erpnext.controllers.accounts_controller.validate_einvoice_fields': 'erpnext.regional.india.e_invoice.utils.validate_einvoice_fields',
+		'erpnext.assets.doctype.asset.asset.get_depreciation_amount': 'erpnext.regional.india.utils.get_depreciation_amount'
 	},
 	'United Arab Emirates': {
 		'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.united_arab_emirates.utils.update_itemised_tax_data',
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index 6338056..052d7bd 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -879,3 +879,24 @@
 	if total_charges != additional_taxes:
 		diff = additional_taxes - total_charges
 		doc.get('items')[item_count - 1].taxable_value += diff
+
+def get_depreciation_amount(asset, depreciable_value, row):
+	depreciation_left = flt(row.total_number_of_depreciations) - flt(asset.number_of_depreciations_booked)
+
+	if row.depreciation_method in ("Straight Line", "Manual"):
+		depreciation_amount = (flt(row.value_after_depreciation) -
+			flt(row.expected_value_after_useful_life)) / depreciation_left
+	else:
+		rate_of_depreciation = row.rate_of_depreciation
+		# if its the first depreciation
+		if depreciable_value == asset.gross_purchase_amount:
+			# as per IT act, if the asset is purchased in the 2nd half of fiscal year, then rate is divided by 2
+			diff = date_diff(asset.available_for_use_date, row.depreciation_start_date)
+			if diff <= 180:
+				rate_of_depreciation = rate_of_depreciation / 2
+				frappe.msgprint(
+					_('As per IT Act, the rate of depreciation for the first depreciation entry is reduced by 50%.'))
+
+		depreciation_amount = flt(depreciable_value * (flt(rate_of_depreciation) / 100))
+
+	return depreciation_amount
\ No newline at end of file