Merge branch 'develop' into asset_depreciation_schedule
diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json
index f0505ff..9af03ca 100644
--- a/erpnext/assets/doctype/asset/asset.json
+++ b/erpnext/assets/doctype/asset/asset.json
@@ -508,9 +508,14 @@
    "group": "Value",
    "link_doctype": "Asset Value Adjustment",
    "link_fieldname": "asset"
+  },
+  {
+   "group": "Depreciation",
+   "link_doctype": "Asset Depreciation Schedule",
+   "link_fieldname": "asset"
   }
  ],
- "modified": "2022-07-20 10:15:12.887372",
+ "modified": "2022-11-01 15:25:27.669803",
  "modified_by": "Administrator",
  "module": "Assets",
  "name": "Asset",
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index ca6be9b..b8f368a 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -15,6 +15,7 @@
 	flt,
 	get_datetime,
 	get_last_day,
+	is_last_day_of_the_month,
 	getdate,
 	month_diff,
 	nowdate,
@@ -27,6 +28,12 @@
 	get_depreciation_accounts,
 	get_disposal_account_and_cost_center,
 )
+from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule import (
+	make_draft_asset_depreciation_schedules,
+	get_asset_depreciation_schedule_name,
+	convert_draft_asset_depreciation_schedules_into_active,
+	update_draft_asset_depreciation_schedules,
+)
 from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
 from erpnext.controllers.accounts_controller import AccountsController
 
@@ -40,9 +47,9 @@
 		self.set_missing_values()
 		if not self.split_from:
 			self.prepare_depreciation_data()
+			update_draft_asset_depreciation_schedules(self)
 		self.validate_gross_and_purchase_amount()
-		if self.get("schedules"):
-			self.validate_expected_value_after_useful_life()
+		self.validate_expected_value_after_useful_life()
 
 		self.status = self.get_status()
 
@@ -52,6 +59,7 @@
 		self.make_asset_movement()
 		if not self.booked_fixed_asset and self.validate_make_gl_entry():
 			self.make_gl_entries()
+		convert_draft_asset_depreciation_schedules_into_active(self)
 
 	def on_cancel(self):
 		self.validate_cancellation()
@@ -62,6 +70,10 @@
 		make_reverse_gl_entries(voucher_type="Asset", voucher_no=self.name)
 		self.db_set("booked_fixed_asset", 0)
 
+	def after_insert(self):
+		if not self.split_from:
+			make_draft_asset_depreciation_schedules(self)
+
 	def validate_asset_and_reference(self):
 		if self.purchase_invoice or self.purchase_receipt:
 			reference_doc = "Purchase Invoice" if self.purchase_invoice else "Purchase Receipt"
@@ -79,12 +91,10 @@
 				_("Purchase Invoice cannot be made against an existing asset {0}").format(self.name)
 			)
 
-	def prepare_depreciation_data(self, date_of_disposal=None, date_of_return=None):
+	def prepare_depreciation_data(self):
 		if self.calculate_depreciation:
 			self.value_after_depreciation = 0
 			self.set_depreciation_rate()
-			self.make_depreciation_schedule(date_of_disposal)
-			self.set_accumulated_depreciation(date_of_disposal, date_of_return)
 		else:
 			self.finance_books = []
 			self.value_after_depreciation = flt(self.gross_purchase_amount) - flt(
@@ -223,148 +233,6 @@
 				self.get_depreciation_rate(d, on_validate=True), d.precision("rate_of_depreciation")
 			)
 
-	def make_depreciation_schedule(self, date_of_disposal):
-		if "Manual" not in [d.depreciation_method for d in self.finance_books] and not self.get(
-			"schedules"
-		):
-			self.schedules = []
-
-		if not self.available_for_use_date:
-			return
-
-		start = self.clear_depreciation_schedule()
-
-		for finance_book in self.get("finance_books"):
-			self._make_depreciation_schedule(finance_book, start, date_of_disposal)
-
-	def _make_depreciation_schedule(self, finance_book, start, date_of_disposal):
-		self.validate_asset_finance_books(finance_book)
-
-		value_after_depreciation = self._get_value_after_depreciation(finance_book)
-		finance_book.value_after_depreciation = value_after_depreciation
-
-		number_of_pending_depreciations = cint(finance_book.total_number_of_depreciations) - cint(
-			self.number_of_depreciations_booked
-		)
-
-		has_pro_rata = self.check_is_pro_rata(finance_book)
-		if has_pro_rata:
-			number_of_pending_depreciations += 1
-
-		skip_row = False
-		should_get_last_day = is_last_day_of_the_month(finance_book.depreciation_start_date)
-
-		for n in range(start[finance_book.idx - 1], number_of_pending_depreciations):
-			# If depreciation is already completed (for double declining balance)
-			if skip_row:
-				continue
-
-			depreciation_amount = get_depreciation_amount(self, value_after_depreciation, finance_book)
-
-			if not has_pro_rata or n < cint(number_of_pending_depreciations) - 1:
-				schedule_date = add_months(
-					finance_book.depreciation_start_date, n * cint(finance_book.frequency_of_depreciation)
-				)
-
-				if should_get_last_day:
-					schedule_date = get_last_day(schedule_date)
-
-				# schedule date will be a year later from start date
-				# so monthly schedule date is calculated by removing 11 months from it
-				monthly_schedule_date = add_months(schedule_date, -finance_book.frequency_of_depreciation + 1)
-
-			# if asset is being sold
-			if date_of_disposal:
-				from_date = self.get_from_date(finance_book.finance_book)
-				depreciation_amount, days, months = self.get_pro_rata_amt(
-					finance_book, depreciation_amount, from_date, date_of_disposal
-				)
-
-				if depreciation_amount > 0:
-					self._add_depreciation_row(
-						date_of_disposal,
-						depreciation_amount,
-						finance_book.depreciation_method,
-						finance_book.finance_book,
-						finance_book.idx,
-					)
-
-				break
-
-			# For first row
-			if has_pro_rata and not self.opening_accumulated_depreciation and n == 0:
-				from_date = add_days(
-					self.available_for_use_date, -1
-				)  # needed to calc depr amount for available_for_use_date too
-				depreciation_amount, days, months = self.get_pro_rata_amt(
-					finance_book, depreciation_amount, from_date, finance_book.depreciation_start_date
-				)
-
-				# For first depr schedule date will be the start date
-				# so monthly schedule date is calculated by removing month difference between use date and start date
-				monthly_schedule_date = add_months(finance_book.depreciation_start_date, -months + 1)
-
-			# For last row
-			elif has_pro_rata and n == cint(number_of_pending_depreciations) - 1:
-				if not self.flags.increase_in_asset_life:
-					# In case of increase_in_asset_life, the self.to_date is already set on asset_repair submission
-					self.to_date = add_months(
-						self.available_for_use_date,
-						(n + self.number_of_depreciations_booked) * cint(finance_book.frequency_of_depreciation),
-					)
-
-				depreciation_amount_without_pro_rata = depreciation_amount
-
-				depreciation_amount, days, months = self.get_pro_rata_amt(
-					finance_book, depreciation_amount, schedule_date, self.to_date
-				)
-
-				depreciation_amount = self.get_adjusted_depreciation_amount(
-					depreciation_amount_without_pro_rata, depreciation_amount, finance_book.finance_book
-				)
-
-				monthly_schedule_date = add_months(schedule_date, 1)
-				schedule_date = add_days(schedule_date, days)
-				last_schedule_date = schedule_date
-
-			if not depreciation_amount:
-				continue
-			value_after_depreciation -= flt(depreciation_amount, self.precision("gross_purchase_amount"))
-
-			# Adjust depreciation amount in the last period based on the expected value after useful life
-			if finance_book.expected_value_after_useful_life and (
-				(
-					n == cint(number_of_pending_depreciations) - 1
-					and value_after_depreciation != finance_book.expected_value_after_useful_life
-				)
-				or value_after_depreciation < finance_book.expected_value_after_useful_life
-			):
-				depreciation_amount += value_after_depreciation - finance_book.expected_value_after_useful_life
-				skip_row = True
-
-			if depreciation_amount > 0:
-				self._add_depreciation_row(
-					schedule_date,
-					depreciation_amount,
-					finance_book.depreciation_method,
-					finance_book.finance_book,
-					finance_book.idx,
-				)
-
-	def _add_depreciation_row(
-		self, schedule_date, depreciation_amount, depreciation_method, finance_book, finance_book_id
-	):
-		self.append(
-			"schedules",
-			{
-				"schedule_date": schedule_date,
-				"depreciation_amount": depreciation_amount,
-				"depreciation_method": depreciation_method,
-				"finance_book": finance_book,
-				"finance_book_id": finance_book_id,
-			},
-		)
-
 	def _get_value_after_depreciation(self, finance_book):
 		# value_after_depreciation - current Asset value
 		if self.docstatus == 1 and finance_book.value_after_depreciation:
@@ -376,57 +244,17 @@
 
 		return value_after_depreciation
 
-	# depreciation schedules need to be cleared before modification due to increase in asset life/asset sales
-	# JE: Journal Entry, FB: Finance Book
-	def clear_depreciation_schedule(self):
-		start = []
-		num_of_depreciations_completed = 0
-		depr_schedule = []
-
-		for schedule in self.get("schedules"):
-			# to update start when there are JEs linked with all the schedule rows corresponding to an FB
-			if len(start) == (int(schedule.finance_book_id) - 2):
-				start.append(num_of_depreciations_completed)
-				num_of_depreciations_completed = 0
-
-			# to ensure that start will only be updated once for each FB
-			if len(start) == (int(schedule.finance_book_id) - 1):
-				if schedule.journal_entry:
-					num_of_depreciations_completed += 1
-					depr_schedule.append(schedule)
-				else:
-					start.append(num_of_depreciations_completed)
-					num_of_depreciations_completed = 0
-
-		# to update start when all the schedule rows corresponding to the last FB are linked with JEs
-		if len(start) == (len(self.finance_books) - 1):
-			start.append(num_of_depreciations_completed)
-
-		# when the Depreciation Schedule is being created for the first time
-		if start == []:
-			start = [0] * len(self.finance_books)
-		else:
-			self.schedules = depr_schedule
-
-		return start
-
 	def get_from_date(self, finance_book):
-		if not self.get("schedules"):
+		asset_depr_schedule_name = get_asset_depreciation_schedule_name(
+			self.name, finance_book
+		)
+
+		if not asset_depr_schedule_name:
 			return self.available_for_use_date
 
-		if len(self.finance_books) == 1:
-			return self.schedules[-1].schedule_date
+		asset_depr_schedule = frappe.get_doc("Asset Depreciation Schedule", asset_depr_schedule_name)
 
-		from_date = ""
-		for schedule in self.get("schedules"):
-			if schedule.finance_book == finance_book:
-				from_date = schedule.schedule_date
-
-		if from_date:
-			return from_date
-
-		# since depr for available_for_use_date is not yet booked
-		return add_days(self.available_for_use_date, -1)
+		return asset_depr_schedule.get("depreciation_schedule")[-1].schedule_date
 
 	# if it returns True, depreciation_amount will not be equal for the first and last rows
 	def check_is_pro_rata(self, row):
@@ -530,65 +358,29 @@
 		return depreciation_amount_for_last_row
 
 	def get_depreciation_amount_for_first_row(self, finance_book):
-		if self.has_only_one_finance_book():
-			return self.schedules[0].depreciation_amount
-		else:
-			for schedule in self.schedules:
-				if schedule.finance_book == finance_book:
-					return schedule.depreciation_amount
+		asset_depr_schedule_name = get_asset_depreciation_schedule_name(self.name, finance_book)
 
-	def has_only_one_finance_book(self):
-		if len(self.finance_books) == 1:
-			return True
+		asset_depr_schedule = frappe.get_doc("Asset Depreciation Schedule", asset_depr_schedule_name)
 
-	def set_accumulated_depreciation(
-		self, date_of_sale=None, date_of_return=None, ignore_booked_entry=False
-	):
-		straight_line_idx = [
-			d.idx for d in self.get("schedules") if d.depreciation_method == "Straight Line"
-		]
-		finance_books = []
-
-		for i, d in enumerate(self.get("schedules")):
-			if ignore_booked_entry and d.journal_entry:
-				continue
-
-			if int(d.finance_book_id) not in finance_books:
-				accumulated_depreciation = flt(self.opening_accumulated_depreciation)
-				value_after_depreciation = flt(self.get_value_after_depreciation(d.finance_book_id))
-				finance_books.append(int(d.finance_book_id))
-
-			depreciation_amount = flt(d.depreciation_amount, d.precision("depreciation_amount"))
-			value_after_depreciation -= flt(depreciation_amount)
-
-			# for the last row, if depreciation method = Straight Line
-			if (
-				straight_line_idx
-				and i == max(straight_line_idx) - 1
-				and not date_of_sale
-				and not date_of_return
-			):
-				book = self.get("finance_books")[cint(d.finance_book_id) - 1]
-				depreciation_amount += flt(
-					value_after_depreciation - flt(book.expected_value_after_useful_life),
-					d.precision("depreciation_amount"),
-				)
-
-			d.depreciation_amount = depreciation_amount
-			accumulated_depreciation += d.depreciation_amount
-			d.accumulated_depreciation_amount = flt(
-				accumulated_depreciation, d.precision("accumulated_depreciation_amount")
-			)
+		return asset_depr_schedule.get("depreciation_schedule")[0].depreciation_amount
 
 	def get_value_after_depreciation(self, idx):
 		return flt(self.get("finance_books")[cint(idx) - 1].value_after_depreciation)
 
 	def validate_expected_value_after_useful_life(self):
 		for row in self.get("finance_books"):
+			asset_depr_schedule_name = get_asset_depreciation_schedule_name(
+				self.name, row.finance_book
+			)
+
+			if not asset_depr_schedule_name:
+				return
+
+			asset_depr_schedule = frappe.get_doc("Asset Depreciation Schedule", asset_depr_schedule_name)
+
 			accumulated_depreciation_after_full_schedule = [
 				d.accumulated_depreciation_amount
-				for d in self.get("schedules")
-				if cint(d.finance_book_id) == row.idx
+				for d in asset_depr_schedule.get("depreciation_schedule")
 			]
 
 			if accumulated_depreciation_after_full_schedule:
@@ -637,10 +429,20 @@
 			movement.cancel()
 
 	def delete_depreciation_entries(self):
-		for d in self.get("schedules"):
-			if d.journal_entry:
-				frappe.get_doc("Journal Entry", d.journal_entry).cancel()
-				d.db_set("journal_entry", None)
+		for row in self.get("finance_books"):
+			asset_depr_schedule_name = get_asset_depreciation_schedule_name(
+				self.name, row.finance_book
+			)
+
+			if not asset_depr_schedule_name:
+				return
+
+			asset_depr_schedule = frappe.get_doc("Asset Depreciation Schedule", asset_depr_schedule_name)
+
+			for d in asset_depr_schedule.get("depreciation_schedule"):
+				if d.journal_entry:
+					frappe.get_doc("Journal Entry", d.journal_entry).cancel()
+					d.db_set("journal_entry", None)
 
 		self.db_set(
 			"value_after_depreciation",
@@ -1072,32 +874,6 @@
 	return date_diff(date, period_start_date)
 
 
-def is_last_day_of_the_month(date):
-	last_day_of_the_month = get_last_day(date)
-
-	return getdate(last_day_of_the_month) == getdate(date)
-
-
-@erpnext.allow_regional
-def get_depreciation_amount(asset, depreciable_value, row):
-	if row.depreciation_method in ("Straight Line", "Manual"):
-		# if the Depreciation Schedule is being prepared for the first time
-		if not asset.flags.increase_in_asset_life:
-			depreciation_amount = (
-				flt(asset.gross_purchase_amount) - flt(row.expected_value_after_useful_life)
-			) / flt(row.total_number_of_depreciations)
-
-		# if the Depreciation Schedule is being modified after Asset Repair
-		else:
-			depreciation_amount = (
-				flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life)
-			) / (date_diff(asset.to_date, asset.available_for_use_date) / 365)
-	else:
-		depreciation_amount = flt(depreciable_value * (flt(row.rate_of_depreciation) / 100))
-
-	return depreciation_amount
-
-
 @frappe.whitelist()
 def split_asset(asset_name, split_qty):
 	asset = frappe.get_doc("Asset", asset_name)
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index 19c913a..e25d890 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -881,7 +881,9 @@
 	def test_get_depreciation_amount(self):
 		"""Tests if get_depreciation_amount() returns the right value."""
 
-		from erpnext.assets.doctype.asset.asset import get_depreciation_amount
+		from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule import (
+			get_depreciation_amount
+		)
 
 		asset = create_asset(item_code="Macbook Pro", available_for_use_date="2019-12-31")
 
diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/__init__.py b/erpnext/assets/doctype/asset_depreciation_schedule/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/assets/doctype/asset_depreciation_schedule/__init__.py
diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.js b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.js
new file mode 100644
index 0000000..9d41de3
--- /dev/null
+++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Asset Depreciation Schedule', {
+	// refresh: function(frm) {
+
+	// }
+});
diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json
new file mode 100644
index 0000000..83ed089
--- /dev/null
+++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json
@@ -0,0 +1,174 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "autoname": "naming_series:",
+ "creation": "2022-10-31 15:03:35.424877",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "asset",
+  "naming_series",
+  "column_break_2",
+  "finance_book",
+  "depreciation_details_section",
+  "depreciation_method",
+  "total_number_of_depreciations",
+  "rate_of_depreciation",
+  "column_break_8",
+  "frequency_of_depreciation",
+  "expected_value_after_useful_life",
+  "depreciation_schedule_section",
+  "depreciation_schedule",
+  "details_section",
+  "notes",
+  "column_break_15",
+  "status",
+  "amended_from"
+ ],
+ "fields": [
+  {
+   "fieldname": "asset",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Asset",
+   "options": "Asset",
+   "reqd": 1
+  },
+  {
+   "fieldname": "naming_series",
+   "fieldtype": "Select",
+   "label": "Naming Series",
+   "options": "ACC-ADS-.YYYY.-"
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Asset Depreciation Schedule",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_2",
+   "fieldtype": "Column Break"
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "depreciation_details_section",
+   "fieldtype": "Section Break",
+   "label": "Depreciation Details"
+  },
+  {
+   "fieldname": "finance_book",
+   "fieldtype": "Link",
+   "label": "Finance Book",
+   "options": "Finance Book",
+   "read_only": 1
+  },
+  {
+   "fieldname": "depreciation_method",
+   "fieldtype": "Select",
+   "label": "Depreciation Method",
+   "options": "\nStraight Line\nDouble Declining Balance\nWritten Down Value\nManual",
+   "read_only": 1
+  },
+  {
+   "depends_on": "eval:doc.depreciation_method == 'Written Down Value'",
+   "description": "In Percentage",
+   "fieldname": "rate_of_depreciation",
+   "fieldtype": "Percent",
+   "label": "Rate of Depreciation",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_8",
+   "fieldtype": "Column Break"
+  },
+  {
+   "depends_on": "total_number_of_depreciations",
+   "fieldname": "total_number_of_depreciations",
+   "fieldtype": "Int",
+   "label": "Total Number of Depreciations",
+   "read_only": 1
+  },
+  {
+   "fieldname": "depreciation_schedule_section",
+   "fieldtype": "Section Break",
+   "label": "Depreciation Schedule"
+  },
+  {
+   "fieldname": "depreciation_schedule",
+   "fieldtype": "Table",
+   "label": "Depreciation Schedule",
+   "options": "Depreciation Schedule"
+  },
+  {
+   "collapsible": 1,
+   "collapsible_depends_on": "notes",
+   "fieldname": "details_section",
+   "fieldtype": "Section Break",
+   "label": "Details"
+  },
+  {
+   "fieldname": "notes",
+   "fieldtype": "Small Text",
+   "label": "Notes"
+  },
+  {
+   "fieldname": "status",
+   "fieldtype": "Select",
+   "hidden": 1,
+   "label": "Status",
+   "options": "Draft\nActive\nCancelled",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_15",
+   "fieldtype": "Column Break"
+  },
+  {
+   "depends_on": "frequency_of_depreciation",
+   "fieldname": "frequency_of_depreciation",
+   "fieldtype": "Int",
+   "label": "Frequency of Depreciation (Months)",
+   "read_only": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "expected_value_after_useful_life",
+   "fieldtype": "Currency",
+   "label": "Expected Value After Useful Life",
+   "options": "Company:company:default_currency",
+   "read_only": 1
+  }
+ ],
+ "in_create": 1,
+ "index_web_pages_for_search": 1,
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2022-11-14 14:33:53.360643",
+ "modified_by": "Administrator",
+ "module": "Assets",
+ "name": "Asset Depreciation Schedule",
+ "naming_rule": "By \"Naming Series\" field",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": []
+}
\ No newline at end of file
diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py
new file mode 100644
index 0000000..ae3707b
--- /dev/null
+++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py
@@ -0,0 +1,355 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import frappe
+from frappe.utils import (
+	add_days,
+	add_months,
+	cint,
+	date_diff,
+	flt,
+	get_last_day,
+	is_last_day_of_the_month
+)
+from frappe.model.document import Document
+
+import erpnext
+
+
+class AssetDepreciationSchedule(Document):
+	pass
+
+
+def make_draft_asset_depreciation_schedules(asset):
+	for row in asset.get("finance_books"):
+		asset_depr_schedule = frappe.new_doc("Asset Depreciation Schedule")
+
+		prepare_draft_asset_depreciation_schedule_data(asset_depr_schedule, asset, row)
+
+
+def update_draft_asset_depreciation_schedules(asset):
+	for row in asset.get("finance_books"):
+		asset_depr_schedule_name = get_asset_depreciation_schedule_name(
+			asset.name, row.finance_book
+		)
+
+		if not asset_depr_schedule_name:
+			return
+
+		asset_depr_schedule = frappe.get_doc("Asset Depreciation Schedule", asset_depr_schedule_name)
+
+		prepare_draft_asset_depreciation_schedule_data(asset_depr_schedule, asset, row)
+
+
+def prepare_draft_asset_depreciation_schedule_data(asset_depr_schedule, asset, row):
+	set_draft_asset_depreciation_schedule_details(asset_depr_schedule, asset.name, row)
+	make_depreciation_schedule(asset_depr_schedule, asset, row)
+	set_accumulated_depreciation(asset_depr_schedule, asset, row)
+
+	asset_depr_schedule.save()
+
+
+def set_draft_asset_depreciation_schedule_details(asset_depr_schedule, asset_name, row):
+	asset_depr_schedule.asset = asset_name
+	asset_depr_schedule.finance_book = row.finance_book
+	asset_depr_schedule.depreciation_method = row.depreciation_method
+	asset_depr_schedule.total_number_of_depreciations = row.total_number_of_depreciations
+	asset_depr_schedule.frequency_of_depreciation = row.frequency_of_depreciation
+	asset_depr_schedule.rate_of_depreciation = row.rate_of_depreciation
+	asset_depr_schedule.expected_value_after_useful_life = row.expected_value_after_useful_life
+	asset_depr_schedule.status = "Draft"
+
+
+def convert_draft_asset_depreciation_schedules_into_active(asset):
+	for row in asset.get("finance_books"):
+		asset_depr_schedule_name = get_asset_depreciation_schedule_name(
+			asset.name, row.finance_book
+		)
+
+		if not asset_depr_schedule_name:
+			return
+
+		asset_depr_schedule = frappe.get_doc("Asset Depreciation Schedule", asset_depr_schedule_name)
+
+		asset_depr_schedule.status = "Active"
+
+		asset_depr_schedule.submit()
+
+
+def make_new_active_asset_depreciation_schedules_from_existing(
+	asset, date_of_disposal=None, date_of_return=None
+):
+	for row in asset.get("finance_books"):
+		old_asset_depr_schedule_name = get_asset_depreciation_schedule_name(
+			asset.name, row.finance_book
+		)
+
+		if not old_asset_depr_schedule_name:
+			return
+
+		old_asset_depr_schedule = frappe.get_doc(
+			"Asset Depreciation Schedule",
+			old_asset_depr_schedule_name
+		)
+
+		asset_depr_schedule = frappe.copy_doc(old_asset_depr_schedule, ignore_no_copy=False)
+
+		make_depreciation_schedule(asset_depr_schedule, asset, row, date_of_disposal)
+		set_accumulated_depreciation(asset_depr_schedule, asset, row, date_of_disposal, date_of_return)
+
+		asset_depr_schedule.save()
+
+
+def make_depreciation_schedule(asset_depr_schedule, asset, row, date_of_disposal=None):
+	if row.depreciation_method != "Manual" and not asset_depr_schedule.get("depreciation_schedule"):
+		asset_depr_schedule.depreciation_schedule = []
+
+	if not asset.available_for_use_date:
+		return
+
+	start = clear_depreciation_schedule(asset_depr_schedule)
+
+	_make_depreciation_schedule(asset_depr_schedule, asset, row, start, date_of_disposal)
+
+
+def get_asset_depreciation_schedule_name(asset_name, finance_book):
+	return frappe.db.get_value(
+		doctype="Asset Depreciation Schedule",
+		filters=[
+			["asset", "=", asset_name],
+			["finance_book", "=", finance_book],
+			["docstatus", "<", 2],
+		]
+	)
+
+
+def clear_depreciation_schedule(asset_depr_schedule):
+	start = []
+	num_of_depreciations_completed = 0
+	depr_schedule = []
+
+	for schedule in asset_depr_schedule.get("depreciation_schedule"):
+		if len(start) != 0:
+			break
+
+		if schedule.journal_entry:
+			num_of_depreciations_completed += 1
+			depr_schedule.append(schedule)
+		else:
+			start.append(num_of_depreciations_completed)
+			num_of_depreciations_completed = 0
+
+	# to update start when all the schedule rows corresponding to the FB are linked with JEs
+	if len(start) == 0:
+		start.append(num_of_depreciations_completed)
+
+	# when the Depreciation Schedule is being created for the first time
+	if start == []:
+		start = [0]
+	else:
+		asset_depr_schedule.depreciation_schedule = depr_schedule
+
+	return start
+
+
+def _make_depreciation_schedule(asset_depr_schedule, asset, row, start, date_of_disposal):
+	asset.validate_asset_finance_books(row)
+
+	value_after_depreciation = asset._get_value_after_depreciation(row)
+	row.value_after_depreciation = value_after_depreciation
+
+	number_of_pending_depreciations = cint(row.total_number_of_depreciations) - cint(
+		asset.number_of_depreciations_booked
+	)
+
+	has_pro_rata = asset.check_is_pro_rata(row)
+	if has_pro_rata:
+		number_of_pending_depreciations += 1
+
+	skip_row = False
+	should_get_last_day = is_last_day_of_the_month(row.depreciation_start_date)
+
+	for n in range(start[row.idx - 1], number_of_pending_depreciations):
+		# If depreciation is already completed (for double declining balance)
+		if skip_row:
+			continue
+
+		depreciation_amount = get_depreciation_amount(asset, value_after_depreciation, row)
+
+		if not has_pro_rata or n < cint(number_of_pending_depreciations) - 1:
+			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 will be a year later from start date
+			# so monthly schedule date is calculated by removing 11 months from it
+			monthly_schedule_date = add_months(schedule_date, -row.frequency_of_depreciation + 1)
+
+		# if asset is being sold or scrapped
+		if date_of_disposal:
+			from_date = asset.get_from_date(row.finance_book)
+			depreciation_amount, days, months = asset.get_pro_rata_amt(
+				row, depreciation_amount, from_date, date_of_disposal
+			)
+
+			if depreciation_amount > 0:
+				add_depr_schedule_row(
+					asset_depr_schedule,
+					date_of_disposal,
+					depreciation_amount,
+					row.depreciation_method,
+					row.finance_book,
+					row.idx,
+				)
+
+			break
+
+		# For first row
+		if has_pro_rata and not asset.opening_accumulated_depreciation and n == 0:
+			from_date = add_days(
+				asset.available_for_use_date, -1
+			)  # needed to calc depr amount for available_for_use_date too
+			depreciation_amount, days, months = asset.get_pro_rata_amt(
+				row, depreciation_amount, from_date, row.depreciation_start_date
+			)
+
+			# For first depr schedule date will be the start date
+			# so monthly schedule date is calculated by removing
+			# month difference between use date and start date
+			monthly_schedule_date = add_months(row.depreciation_start_date, -months + 1)
+
+		# For last row
+		elif has_pro_rata and n == cint(number_of_pending_depreciations) - 1:
+			if not asset.flags.increase_in_asset_life:
+				# In case of increase_in_asset_life, the asset.to_date is already set on asset_repair submission
+				asset.to_date = add_months(
+					asset.available_for_use_date,
+					(n + asset.number_of_depreciations_booked) * cint(row.frequency_of_depreciation),
+				)
+
+			depreciation_amount_without_pro_rata = depreciation_amount
+
+			depreciation_amount, days, months = asset.get_pro_rata_amt(
+				row, depreciation_amount, schedule_date, asset.to_date
+			)
+
+			depreciation_amount = asset.get_adjusted_depreciation_amount(
+				depreciation_amount_without_pro_rata, depreciation_amount, row.finance_book
+			)
+
+			monthly_schedule_date = add_months(schedule_date, 1)
+			schedule_date = add_days(schedule_date, days)
+			last_schedule_date = schedule_date
+
+		if not depreciation_amount:
+			continue
+		value_after_depreciation -= flt(depreciation_amount, asset.precision("gross_purchase_amount"))
+
+		# Adjust depreciation amount in the last period based on the expected value after useful life
+		if row.expected_value_after_useful_life and (
+			(
+				n == cint(number_of_pending_depreciations) - 1
+				and value_after_depreciation != row.expected_value_after_useful_life
+			)
+			or value_after_depreciation < row.expected_value_after_useful_life
+		):
+			depreciation_amount += value_after_depreciation - row.expected_value_after_useful_life
+			skip_row = True
+
+		if depreciation_amount > 0:
+			add_depr_schedule_row(
+				asset_depr_schedule,
+				schedule_date,
+				depreciation_amount,
+				row.depreciation_method,
+				row.finance_book,
+				row.idx,
+			)
+
+
+@erpnext.allow_regional
+def get_depreciation_amount(asset, depreciable_value, row):
+	if row.depreciation_method in ("Straight Line", "Manual"):
+		# if the Depreciation Schedule is being prepared for the first time
+		if not asset.flags.increase_in_asset_life:
+			depreciation_amount = (
+				flt(asset.gross_purchase_amount) - flt(row.expected_value_after_useful_life)
+			) / flt(row.total_number_of_depreciations)
+
+		# if the Depreciation Schedule is being modified after Asset Repair
+		else:
+			depreciation_amount = (
+				flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life)
+			) / (date_diff(asset.to_date, asset.available_for_use_date) / 365)
+	else:
+		depreciation_amount = flt(depreciable_value * (flt(row.rate_of_depreciation) / 100))
+
+	return depreciation_amount
+
+
+def add_depr_schedule_row(
+	asset_depr_schedule,
+	schedule_date,
+	depreciation_amount,
+	depreciation_method,
+	finance_book,
+	finance_book_id
+):
+	asset_depr_schedule.append(
+		"depreciation_schedule",
+		{
+			"schedule_date": schedule_date,
+			"depreciation_amount": depreciation_amount,
+			"depreciation_method": depreciation_method,
+			"finance_book": finance_book,
+			"finance_book_id": finance_book_id,
+		},
+	)
+
+
+def set_accumulated_depreciation(
+	asset_depr_schedule,
+	asset,
+	row,
+	date_of_disposal=None,
+	date_of_return=None,
+	ignore_booked_entry=False
+):
+	straight_line_idx = [
+		d.idx for d in asset_depr_schedule.get("depreciation_schedule")
+		if d.depreciation_method == "Straight Line"
+	]
+	finance_books = []
+
+	for i, d in enumerate(asset_depr_schedule.get("depreciation_schedule")):
+		if ignore_booked_entry and d.journal_entry:
+			continue
+
+		if int(d.finance_book_id) not in finance_books:
+			accumulated_depreciation = flt(asset.opening_accumulated_depreciation)
+			value_after_depreciation = flt(asset.get_value_after_depreciation(d.finance_book_id))
+			finance_books.append(int(d.finance_book_id))
+
+		depreciation_amount = flt(d.depreciation_amount, d.precision("depreciation_amount"))
+		value_after_depreciation -= flt(depreciation_amount)
+
+		# for the last row, if depreciation method = Straight Line
+		if (
+			straight_line_idx
+			and i == max(straight_line_idx) - 1
+			and not date_of_disposal
+			and not date_of_return
+		):
+			depreciation_amount += flt(
+				value_after_depreciation - flt(row.expected_value_after_useful_life),
+				d.precision("depreciation_amount"),
+			)
+
+		d.depreciation_amount = depreciation_amount
+		accumulated_depreciation += d.depreciation_amount
+		d.accumulated_depreciation_amount = flt(
+			accumulated_depreciation, d.precision("accumulated_depreciation_amount")
+		)
diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py
new file mode 100644
index 0000000..21de8cd
--- /dev/null
+++ b/erpnext/assets/doctype/asset_depreciation_schedule/test_asset_depreciation_schedule.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# import frappe
+from frappe.tests.utils import FrappeTestCase
+
+
+class TestAssetDepreciationSchedule(FrappeTestCase):
+	pass
diff --git a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py
index 84aa8fa..ae6e2b4 100644
--- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py
+++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.py
@@ -10,7 +10,9 @@
 from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
 	get_checks_for_pl_and_bs_accounts,
 )
-from erpnext.assets.doctype.asset.asset import get_depreciation_amount
+from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule import (
+	get_depreciation_amount
+)
 from erpnext.assets.doctype.asset.depreciation import get_depreciation_accounts