Merge remote-tracking branch 'upstream/develop' into asset-capitalization

# Conflicts:
#	erpnext/accounts/doctype/sales_invoice/sales_invoice.py
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index cd270f5..86b7aaa 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -35,7 +35,6 @@
 	get_disposal_account_and_cost_center,
 	get_gl_entries_on_asset_disposal,
 	get_gl_entries_on_asset_regain,
-	make_depreciation_entry,
 )
 from erpnext.controllers.selling_controller import SellingController
 from erpnext.projects.doctype.timesheet.timesheet import get_projectwise_timesheet_data
@@ -163,7 +162,7 @@
 					if self.update_stock:
 						frappe.throw(_("'Update Stock' cannot be checked for fixed asset sale"))
 
-					elif asset.status in ("Scrapped", "Cancelled") or (asset.status == "Sold" and not self.is_return):
+					elif asset.status in ("Scrapped", "Cancelled", "Capitalized", "Decapitalized") or (asset.status == "Sold" and not self.is_return):
 						frappe.throw(_("Row #{0}: Asset {1} cannot be submitted, it is already {2}").format(d.idx, d.asset, asset.status))
 
 	def validate_item_cost_centers(self):
@@ -932,7 +931,7 @@
 						asset.db_set("disposal_date", None)
 
 						if asset.calculate_depreciation:
-							self.reverse_depreciation_entry_made_after_sale(asset)
+							self.reverse_depreciation_entry_made_after_disposal(asset)
 							self.reset_depreciation_schedule(asset)
 
 					else:
@@ -988,89 +987,6 @@
 		self.check_finance_books(item, asset)
 		return asset
 
-	def check_finance_books(self, item, asset):
-		if (len(asset.finance_books) > 1 and not item.finance_book
-			and asset.finance_books[0].finance_book):
-			frappe.throw(_("Select finance book for the item {0} at row {1}")
-				.format(item.item_code, item.idx))
-
-	def depreciate_asset(self, asset):
-		asset.flags.ignore_validate_update_after_submit = True
-		asset.prepare_depreciation_data(date_of_sale=self.posting_date)
-		asset.save()
-
-		make_depreciation_entry(asset.name, self.posting_date)
-
-	def reset_depreciation_schedule(self, asset):
-		asset.flags.ignore_validate_update_after_submit = True
-
-		# recreate original depreciation schedule of the asset
-		asset.prepare_depreciation_data(date_of_return=self.posting_date)
-
-		self.modify_depreciation_schedule_for_asset_repairs(asset)
-		asset.save()
-
-	def modify_depreciation_schedule_for_asset_repairs(self, asset):
-		asset_repairs = frappe.get_all(
-			'Asset Repair',
-			filters = {'asset': asset.name},
-			fields = ['name', 'increase_in_asset_life']
-		)
-
-		for repair in asset_repairs:
-			if repair.increase_in_asset_life:
-				asset_repair = frappe.get_doc('Asset Repair', repair.name)
-				asset_repair.modify_depreciation_schedule()
-				asset.prepare_depreciation_data()
-
-	def reverse_depreciation_entry_made_after_sale(self, asset):
-		from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry
-
-		posting_date_of_original_invoice = self.get_posting_date_of_sales_invoice()
-
-		row = -1
-		finance_book = asset.get('schedules')[0].get('finance_book')
-		for schedule in asset.get('schedules'):
-			if schedule.finance_book != finance_book:
-				row = 0
-				finance_book = schedule.finance_book
-			else:
-				row += 1
-
-			if schedule.schedule_date == posting_date_of_original_invoice:
-				if not self.sale_was_made_on_original_schedule_date(asset, schedule, row, posting_date_of_original_invoice) \
-					or self.sale_happens_in_the_future(posting_date_of_original_invoice):
-
-					reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry)
-					reverse_journal_entry.posting_date = nowdate()
-					frappe.flags.is_reverse_depr_entry = True
-					reverse_journal_entry.submit()
-
-					frappe.flags.is_reverse_depr_entry = False
-					asset.flags.ignore_validate_update_after_submit = True
-					schedule.journal_entry = None
-					asset.save()
-
-	def get_posting_date_of_sales_invoice(self):
-		return frappe.db.get_value('Sales Invoice', self.return_against, 'posting_date')
-
-	# if the invoice had been posted on the date the depreciation was initially supposed to happen, the depreciation shouldn't be undone
-	def sale_was_made_on_original_schedule_date(self, asset, schedule, row, posting_date_of_original_invoice):
-		for finance_book in asset.get('finance_books'):
-			if schedule.finance_book == finance_book.finance_book:
-				orginal_schedule_date = add_months(finance_book.depreciation_start_date,
-					row * cint(finance_book.frequency_of_depreciation))
-
-				if orginal_schedule_date == posting_date_of_original_invoice:
-					return True
-		return False
-
-	def sale_happens_in_the_future(self, posting_date_of_original_invoice):
-		if posting_date_of_original_invoice > getdate():
-			return True
-
-		return False
-
 	@property
 	def enable_discount_accounting(self):
 		if not hasattr(self, "_enable_discount_accounting"):
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index c0c437f..163e9eb 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -72,12 +72,12 @@
 		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, date_of_sale=None, date_of_return=None):
+	def prepare_depreciation_data(self, date_of_disposal=None, date_of_return=None):
 		if self.calculate_depreciation:
 			self.value_after_depreciation = 0
 			self.set_depreciation_rate()
-			self.make_depreciation_schedule(date_of_sale)
-			self.set_accumulated_depreciation(date_of_sale, date_of_return)
+			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) -
@@ -178,7 +178,7 @@
 			d.rate_of_depreciation = flt(self.get_depreciation_rate(d, on_validate=True),
 				d.precision("rate_of_depreciation"))
 
-	def make_depreciation_schedule(self, date_of_sale):
+	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 = []
 
@@ -224,14 +224,14 @@
 					monthly_schedule_date = add_months(schedule_date, - d.frequency_of_depreciation + 1)
 
 				# if asset is being sold
-				if date_of_sale:
+				if date_of_disposal:
 					from_date = self.get_from_date(d.finance_book)
 					depreciation_amount, days, months = self.get_pro_rata_amt(d, depreciation_amount,
-						from_date, date_of_sale)
+						from_date, date_of_disposal)
 
 					if depreciation_amount > 0:
 						self.append("schedules", {
-							"schedule_date": date_of_sale,
+							"schedule_date": date_of_disposal,
 							"depreciation_amount": depreciation_amount,
 							"depreciation_method": d.depreciation_method,
 							"finance_book": d.finance_book,
diff --git a/erpnext/assets/doctype/asset/asset_list.js b/erpnext/assets/doctype/asset/asset_list.js
index 4302cb2..3d00eb7 100644
--- a/erpnext/assets/doctype/asset/asset_list.js
+++ b/erpnext/assets/doctype/asset/asset_list.js
@@ -10,6 +10,9 @@
 		} else if (doc.status === "Sold") {
 			return [__("Sold"), "green", "status,=,Sold"];
 
+		} else if (["Capitalized", "Decapitalized"].includes(doc.status)) {
+			return [__(doc.status), "grey", "status,=," + doc.status];
+
 		} else if (doc.status === "Scrapped") {
 			return [__("Scrapped"), "grey", "status,=,Scrapped"];
 
diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py
index ca10b1d..9189e24 100644
--- a/erpnext/assets/doctype/asset/depreciation.py
+++ b/erpnext/assets/doctype/asset/depreciation.py
@@ -11,7 +11,7 @@
 )
 
 
-def post_depreciation_entries(date=None):
+def post_depreciation_entries(date=None, commit=True):
 	# Return if automatic booking of asset depreciation is disabled
 	if not cint(frappe.db.get_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically")):
 		return
@@ -20,7 +20,8 @@
 		date = today()
 	for asset in get_depreciable_assets(date):
 		make_depreciation_entry(asset, date)
-		frappe.db.commit()
+		if commit:
+			frappe.db.commit()
 
 def get_depreciable_assets(date):
 	return frappe.db.sql_list("""select a.name
@@ -138,7 +139,7 @@
 
 	if asset.docstatus != 1:
 		frappe.throw(_("Asset {0} must be submitted").format(asset.name))
-	elif asset.status in ("Cancelled", "Sold", "Scrapped"):
+	elif asset.status in ("Cancelled", "Sold", "Scrapped", "Capitalized", "Decapitalized"):
 		frappe.throw(_("Asset {0} cannot be scrapped, as it is already {1}").format(asset.name, asset.status))
 
 	depreciation_series = frappe.get_cached_value('Company',  asset.company,  "series_for_depreciation_entry")
@@ -267,3 +268,25 @@
 		frappe.throw(_("Please set 'Asset Depreciation Cost Center' in Company {0}").format(company))
 
 	return disposal_account, depreciation_cost_center
+
+
+@frappe.whitelist()
+def get_value_after_depreciation_on_disposal_date(asset, disposal_date, finance_book=None):
+	asset_doc = frappe.get_doc("Asset", asset)
+
+	if asset_doc.calculate_depreciation:
+		asset_doc.prepare_depreciation_data(getdate(disposal_date))
+
+		finance_book_id = 1
+		if finance_book:
+			for fb in asset_doc.finance_books:
+				if fb.finance_book == finance_book:
+					finance_book_id = fb.idx
+					break
+
+		asset_schedules = [sch for sch in asset_doc.schedules if cint(sch.finance_book_id) == finance_book_id]
+		accumulated_depr_amount = asset_schedules[-1].accumulated_depreciation_amount
+
+		return flt(flt(asset_doc.gross_purchase_amount) - accumulated_depr_amount, asset_doc.precision('gross_purchase_amount'))
+	else:
+		return flt(asset_doc.value_after_depreciation)
diff --git a/erpnext/assets/doctype/asset_capitalization/__init__.py b/erpnext/assets/doctype/asset_capitalization/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/assets/doctype/asset_capitalization/__init__.py
diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js
new file mode 100644
index 0000000..d135e60
--- /dev/null
+++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js
@@ -0,0 +1,414 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.provide("erpnext.assets");
+
+
+erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.stock.StockController {
+	setup() {
+		this.setup_posting_date_time_check();
+	}
+
+	onload() {
+		this.setup_queries();
+	}
+
+	refresh() {
+		erpnext.hide_company();
+		this.show_general_ledger();
+		if ((this.frm.doc.stock_items && this.frm.doc.stock_items.length) || !this.frm.doc.target_is_fixed_asset) {
+			this.show_stock_ledger();
+		}
+	}
+
+	setup_queries() {
+		var me = this;
+
+		me.setup_warehouse_query();
+
+		me.frm.set_query("target_item_code", function() {
+			return erpnext.queries.item();
+		});
+
+		me.frm.set_query("target_asset", function() {
+			var filters = {};
+
+			if (me.frm.doc.target_item_code) {
+				filters['item_code'] = me.frm.doc.target_item_code;
+			}
+
+			filters['status'] = ["not in", ["Draft", "Scrapped", "Sold", "Capitalized", "Decapitalized"]];
+			filters['docstatus'] = 1;
+
+			return {
+				filters: filters
+			};
+		});
+
+		me.frm.set_query("asset", "asset_items", function() {
+			var filters = {
+				'status': ["not in", ["Draft", "Scrapped", "Sold", "Capitalized", "Decapitalized"]],
+				'docstatus': 1
+			};
+
+			if (me.frm.doc.target_asset) {
+				filters['name'] = ['!=', me.frm.doc.target_asset];
+			}
+
+			return {
+				filters: filters
+			};
+		});
+
+		me.frm.set_query("item_code", "stock_items", function() {
+			return erpnext.queries.item({"is_stock_item": 1});
+		});
+
+		me.frm.set_query("item_code", "service_items", function() {
+			return erpnext.queries.item({"is_stock_item": 0, "is_fixed_asset": 0});
+		});
+
+		me.frm.set_query('batch_no', 'stock_items', function(doc, cdt, cdn) {
+			var item = locals[cdt][cdn];
+			if (!item.item_code) {
+				frappe.throw(__("Please enter Item Code to get Batch Number"));
+			} else {
+				var filters = {
+					'item_code': item.item_code,
+					'posting_date': me.frm.doc.posting_date || frappe.datetime.nowdate(),
+					'warehouse': item.warehouse
+				};
+
+				return {
+					query: "erpnext.controllers.queries.get_batch_no",
+					filters: filters
+				};
+			}
+		});
+
+		me.frm.set_query('expense_account', 'service_items', function() {
+			return {
+				filters: {
+					"account_type": ['in', ["Tax", "Expense Account", "Income Account", "Expenses Included In Valuation", "Expenses Included In Asset Valuation"]],
+					"is_group": 0,
+					"company": me.frm.doc.company
+				}
+			};
+		});
+	}
+
+	target_item_code() {
+		return this.get_target_item_details();
+	}
+
+	target_asset() {
+		return this.get_target_asset_details();
+	}
+
+	item_code(doc, cdt, cdn) {
+		var row = frappe.get_doc(cdt, cdn);
+		if (cdt === "Asset Capitalization Stock Item") {
+			this.get_consumed_stock_item_details(row);
+		} else if (cdt == "Asset Capitalization Service Item") {
+			this.get_service_item_details(row);
+		}
+	}
+
+	warehouse(doc, cdt, cdn) {
+		var row = frappe.get_doc(cdt, cdn);
+		if (cdt === "Asset Capitalization Stock Item") {
+			this.get_warehouse_details(row);
+		}
+	}
+
+	asset(doc, cdt, cdn) {
+		var row = frappe.get_doc(cdt, cdn);
+		if (cdt === "Asset Capitalization Asset Item") {
+			this.get_consumed_asset_details(row);
+		}
+	}
+
+	posting_date() {
+		if (this.frm.doc.posting_date) {
+			frappe.run_serially([
+				() => this.get_all_item_warehouse_details(),
+				() => this.get_all_asset_values()
+			]);
+		}
+	}
+
+	posting_time() {
+		if (this.frm.doc.posting_time) {
+			this.get_all_item_warehouse_details();
+		}
+	}
+
+	finance_book(doc, cdt, cdn) {
+		if (cdt === "Asset Capitalization Asset Item") {
+			var row = frappe.get_doc(cdt, cdn);
+			this.get_consumed_asset_details(row);
+		} else {
+			this.get_all_asset_values();
+		}
+	}
+
+	stock_qty() {
+		this.calculate_totals();
+	}
+
+	qty() {
+		this.calculate_totals();
+	}
+
+	target_qty() {
+		this.calculate_totals();
+	}
+
+	rate() {
+		this.calculate_totals();
+	}
+
+	company() {
+		var me = this;
+
+		if (me.frm.doc.company) {
+			frappe.model.set_value(me.frm.doc.doctype, me.frm.doc.name, "cost_center", null);
+			$.each(me.frm.doc.stock_items || [], function (i, d) {
+				frappe.model.set_value(d.doctype, d.name, "cost_center", null);
+			});
+			$.each(me.frm.doc.asset_items || [], function (i, d) {
+				frappe.model.set_value(d.doctype, d.name, "cost_center", null);
+			});
+			$.each(me.frm.doc.service_items || [], function (i, d) {
+				frappe.model.set_value(d.doctype, d.name, "cost_center", null);
+			});
+		}
+
+		erpnext.accounts.dimensions.update_dimension(me.frm, me.frm.doctype);
+	}
+
+	stock_items_add(doc, cdt, cdn) {
+		erpnext.accounts.dimensions.copy_dimension_from_first_row(this.frm, cdt, cdn, 'stock_items');
+	}
+
+	asset_items_add(doc, cdt, cdn) {
+		erpnext.accounts.dimensions.copy_dimension_from_first_row(this.frm, cdt, cdn, 'asset_items');
+	}
+
+	serivce_items_add(doc, cdt, cdn) {
+		erpnext.accounts.dimensions.copy_dimension_from_first_row(this.frm, cdt, cdn, 'service_items');
+	}
+
+	get_target_item_details() {
+		var me = this;
+
+		if (me.frm.doc.target_item_code) {
+			return me.frm.call({
+				method: "erpnext.assets.doctype.asset_capitalization.asset_capitalization.get_target_item_details",
+				child: me.frm.doc,
+				args: {
+					item_code: me.frm.doc.target_item_code,
+					company: me.frm.doc.company,
+				},
+				callback: function (r) {
+					if (!r.exc) {
+						me.frm.refresh_fields();
+					}
+				}
+			});
+		}
+	}
+
+	get_target_asset_details() {
+		var me = this;
+
+		if (me.frm.doc.target_asset) {
+			return me.frm.call({
+				method: "erpnext.assets.doctype.asset_capitalization.asset_capitalization.get_target_asset_details",
+				child: me.frm.doc,
+				args: {
+					asset: me.frm.doc.target_asset,
+					company: me.frm.doc.company,
+				},
+				callback: function (r) {
+					if (!r.exc) {
+						me.frm.refresh_fields();
+					}
+				}
+			});
+		}
+	}
+
+	get_consumed_stock_item_details(row) {
+		var me = this;
+
+		if (row && row.item_code) {
+			return me.frm.call({
+				method: "erpnext.assets.doctype.asset_capitalization.asset_capitalization.get_consumed_stock_item_details",
+				child: row,
+				args: {
+					args: {
+						item_code: row.item_code,
+						warehouse: row.warehouse,
+						stock_qty: flt(row.stock_qty),
+						doctype: me.frm.doc.doctype,
+						name: me.frm.doc.name,
+						company: me.frm.doc.company,
+						posting_date: me.frm.doc.posting_date,
+						posting_time: me.frm.doc.posting_time,
+					}
+				},
+				callback: function (r) {
+					if (!r.exc) {
+						me.calculate_totals();
+					}
+				}
+			});
+		}
+	}
+
+	get_consumed_asset_details(row) {
+		var me = this;
+
+		if (row && row.asset) {
+			return me.frm.call({
+				method: "erpnext.assets.doctype.asset_capitalization.asset_capitalization.get_consumed_asset_details",
+				child: row,
+				args: {
+					args: {
+						asset: row.asset,
+						doctype: me.frm.doc.doctype,
+						name: me.frm.doc.name,
+						company: me.frm.doc.company,
+						finance_book: row.finance_book || me.frm.doc.finance_book,
+						posting_date: me.frm.doc.posting_date,
+						posting_time: me.frm.doc.posting_time,
+					}
+				},
+				callback: function (r) {
+					if (!r.exc) {
+						me.calculate_totals();
+					}
+				}
+			});
+		}
+	}
+
+	get_service_item_details(row) {
+		var me = this;
+
+		if (row && row.item_code) {
+			return me.frm.call({
+				method: "erpnext.assets.doctype.asset_capitalization.asset_capitalization.get_service_item_details",
+				child: row,
+				args: {
+					args: {
+						item_code: row.item_code,
+						qty: flt(row.qty),
+						expense_account: row.expense_account,
+						company: me.frm.doc.company,
+					}
+				},
+				callback: function (r) {
+					if (!r.exc) {
+						me.calculate_totals();
+					}
+				}
+			});
+		}
+	}
+
+	get_warehouse_details(item) {
+		var me = this;
+		if (item.item_code && item.warehouse) {
+			me.frm.call({
+				method: "erpnext.assets.doctype.asset_capitalization.asset_capitalization.get_warehouse_details",
+				child: item,
+				args: {
+					args: {
+						'item_code': item.item_code,
+						'warehouse': cstr(item.warehouse),
+						'qty': flt(item.stock_qty),
+						'serial_no': item.serial_no,
+						'posting_date': me.frm.doc.posting_date,
+						'posting_time': me.frm.doc.posting_time,
+						'company': me.frm.doc.company,
+						'voucher_type': me.frm.doc.doctype,
+						'voucher_no': me.frm.doc.name,
+						'allow_zero_valuation': 1
+					}
+				},
+				callback: function(r) {
+					if (!r.exc) {
+						me.calculate_totals();
+					}
+				}
+			});
+		}
+	}
+
+	get_all_item_warehouse_details() {
+		var me = this;
+		return me.frm.call({
+			method: "set_warehouse_details",
+			doc: me.frm.doc,
+			callback: function(r) {
+				if (!r.exc) {
+					me.calculate_totals();
+				}
+			}
+		});
+	}
+
+	get_all_asset_values() {
+		var me = this;
+		return me.frm.call({
+			method: "set_asset_values",
+			doc: me.frm.doc,
+			callback: function(r) {
+				if (!r.exc) {
+					me.calculate_totals();
+				}
+			}
+		});
+	}
+
+	calculate_totals() {
+		var me = this;
+
+		me.frm.doc.stock_items_total = 0;
+		me.frm.doc.asset_items_total = 0;
+		me.frm.doc.service_items_total = 0;
+
+		$.each(me.frm.doc.stock_items || [], function (i, d) {
+			d.amount = flt(flt(d.stock_qty) * flt(d.valuation_rate), precision('amount', d));
+			me.frm.doc.stock_items_total += d.amount;
+		});
+
+		$.each(me.frm.doc.asset_items || [], function (i, d) {
+			d.asset_value = flt(flt(d.asset_value), precision('asset_value', d));
+			me.frm.doc.asset_items_total += d.asset_value;
+		});
+
+		$.each(me.frm.doc.service_items || [], function (i, d) {
+			d.amount = flt(flt(d.qty) * flt(d.rate), precision('amount', d));
+			me.frm.doc.service_items_total += d.amount;
+		});
+
+		me.frm.doc.stock_items_total = flt(me.frm.doc.stock_items_total, precision('stock_items_total'));
+		me.frm.doc.asset_items_total = flt(me.frm.doc.asset_items_total, precision('asset_items_total'));
+		me.frm.doc.service_items_total = flt(me.frm.doc.service_items_total, precision('service_items_total'));
+
+		me.frm.doc.total_value = me.frm.doc.stock_items_total + me.frm.doc.asset_items_total + me.frm.doc.service_items_total;
+		me.frm.doc.total_value = flt(me.frm.doc.total_value, precision('total_value'));
+
+		me.frm.doc.target_qty = flt(me.frm.doc.target_qty, precision('target_qty'));
+		me.frm.doc.target_incoming_rate = me.frm.doc.target_qty ? me.frm.doc.total_value / flt(me.frm.doc.target_qty)
+			: me.frm.doc.total_value;
+
+		me.frm.refresh_fields();
+	}
+};
+
+//$.extend(cur_frm.cscript, new erpnext.assets.AssetCapitalization({frm: cur_frm}));
+cur_frm.cscript = new erpnext.assets.AssetCapitalization({frm: cur_frm});
diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.json b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.json
new file mode 100644
index 0000000..d7e6b54
--- /dev/null
+++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.json
@@ -0,0 +1,383 @@
+{
+ "actions": [],
+ "autoname": "naming_series:",
+ "creation": "2021-09-04 13:38:04.217187",
+ "doctype": "DocType",
+ "document_type": "Document",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "title",
+  "target_item_code",
+  "target_item_name",
+  "target_is_fixed_asset",
+  "target_has_batch_no",
+  "target_has_serial_no",
+  "entry_type",
+  "finance_book",
+  "naming_series",
+  "column_break_9",
+  "company",
+  "posting_date",
+  "posting_time",
+  "set_posting_time",
+  "amended_from",
+  "target_item_details_section",
+  "target_asset",
+  "target_asset_name",
+  "target_warehouse",
+  "target_batch_no",
+  "target_serial_no",
+  "column_break_5",
+  "target_qty",
+  "target_stock_uom",
+  "section_break_16",
+  "stock_items",
+  "stock_items_total",
+  "section_break_26",
+  "asset_items",
+  "asset_items_total",
+  "service_expenses_section",
+  "service_items",
+  "service_items_total",
+  "totals_section",
+  "total_value",
+  "column_break_36",
+  "target_incoming_rate",
+  "accounting_dimensions_section",
+  "cost_center",
+  "dimension_col_break",
+  "target_fixed_asset_account"
+ ],
+ "fields": [
+  {
+   "fieldname": "title",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Title"
+  },
+  {
+   "fieldname": "target_item_code",
+   "fieldtype": "Link",
+   "in_standard_filter": 1,
+   "label": "Target Item Code",
+   "options": "Item",
+   "reqd": 1
+  },
+  {
+   "depends_on": "eval:doc.target_item_code && doc.target_item_name != doc.target_item_code",
+   "fetch_from": "target_item_code.item_name",
+   "fieldname": "target_item_name",
+   "fieldtype": "Data",
+   "label": "Target Item Name",
+   "read_only": 1
+  },
+  {
+   "default": "0",
+   "fetch_from": "target_item_code.is_fixed_asset",
+   "fieldname": "target_is_fixed_asset",
+   "fieldtype": "Check",
+   "hidden": 1,
+   "label": "Target Is Fixed Asset",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_5",
+   "fieldtype": "Column Break"
+  },
+  {
+   "depends_on": "eval:!doc.target_item_code || doc.target_is_fixed_asset",
+   "fieldname": "target_asset",
+   "fieldtype": "Link",
+   "in_standard_filter": 1,
+   "label": "Target Asset",
+   "no_copy": 1,
+   "options": "Asset"
+  },
+  {
+   "depends_on": "target_asset",
+   "fetch_from": "target_asset.asset_name",
+   "fieldname": "target_asset_name",
+   "fieldtype": "Data",
+   "label": "Asset Name",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_9",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fetch_from": "asset.company",
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company",
+   "reqd": 1
+  },
+  {
+   "default": "Today",
+   "fieldname": "posting_date",
+   "fieldtype": "Date",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Posting Date",
+   "no_copy": 1,
+   "reqd": 1,
+   "search_index": 1
+  },
+  {
+   "default": "Now",
+   "fieldname": "posting_time",
+   "fieldtype": "Time",
+   "label": "Posting Time",
+   "no_copy": 1,
+   "reqd": 1
+  },
+  {
+   "default": "0",
+   "depends_on": "eval:doc.docstatus==0",
+   "fieldname": "set_posting_time",
+   "fieldtype": "Check",
+   "label": "Edit Posting Date and Time"
+  },
+  {
+   "fieldname": "naming_series",
+   "fieldtype": "Select",
+   "label": "Series",
+   "options": "ACC-ASC-.YYYY.-",
+   "reqd": 1
+  },
+  {
+   "fieldname": "amended_from",
+   "fieldtype": "Link",
+   "label": "Amended From",
+   "no_copy": 1,
+   "options": "Asset Capitalization",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "depends_on": "eval:doc.docstatus == 0 || (doc.stock_items && doc.stock_items.length)",
+   "fieldname": "section_break_16",
+   "fieldtype": "Section Break",
+   "label": "Consumed Stock Items"
+  },
+  {
+   "fieldname": "stock_items",
+   "fieldtype": "Table",
+   "label": "Stock Items",
+   "options": "Asset Capitalization Stock Item"
+  },
+  {
+   "fieldname": "target_item_details_section",
+   "fieldtype": "Section Break",
+   "label": "Target Item Details"
+  },
+  {
+   "depends_on": "eval:!doc.target_is_fixed_asset",
+   "fieldname": "target_warehouse",
+   "fieldtype": "Link",
+   "label": "Target Warehouse",
+   "options": "Warehouse"
+  },
+  {
+   "depends_on": "target_has_batch_no",
+   "fieldname": "target_batch_no",
+   "fieldtype": "Link",
+   "label": "Target Batch No",
+   "options": "Batch"
+  },
+  {
+   "default": "1",
+   "fieldname": "target_qty",
+   "fieldtype": "Float",
+   "label": "Target Qty",
+   "read_only_depends_on": "target_is_fixed_asset"
+  },
+  {
+   "fetch_from": "target_item_code.stock_uom",
+   "fieldname": "target_stock_uom",
+   "fieldtype": "Link",
+   "label": "Stock UOM",
+   "options": "UOM",
+   "read_only": 1
+  },
+  {
+   "default": "0",
+   "fetch_from": "target_item_code.has_batch_no",
+   "fieldname": "target_has_batch_no",
+   "fieldtype": "Check",
+   "hidden": 1,
+   "label": "Target Has Batch No",
+   "read_only": 1
+  },
+  {
+   "default": "0",
+   "fetch_from": "target_item_code.has_serial_no",
+   "fieldname": "target_has_serial_no",
+   "fieldtype": "Check",
+   "hidden": 1,
+   "label": "Target Has Serial No",
+   "read_only": 1
+  },
+  {
+   "depends_on": "target_has_serial_no",
+   "fieldname": "target_serial_no",
+   "fieldtype": "Small Text",
+   "label": "Target Serial No"
+  },
+  {
+   "depends_on": "eval:doc.docstatus == 0 || (doc.asset_items && doc.asset_items.length)",
+   "fieldname": "section_break_26",
+   "fieldtype": "Section Break",
+   "label": "Consumed Asset Items"
+  },
+  {
+   "fieldname": "asset_items",
+   "fieldtype": "Table",
+   "label": "Assets",
+   "options": "Asset Capitalization Asset Item"
+  },
+  {
+   "fieldname": "entry_type",
+   "fieldtype": "Select",
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Entry Type",
+   "options": "\nCapitalization\nDecapitalization",
+   "read_only": 1
+  },
+  {
+   "fieldname": "stock_items_total",
+   "fieldtype": "Currency",
+   "label": "Consumed Stock Total Value",
+   "options": "Company:company:default_currency",
+   "read_only": 1
+  },
+  {
+   "fieldname": "asset_items_total",
+   "fieldtype": "Currency",
+   "label": "Consumed Asset Total Value",
+   "options": "Company:company:default_currency",
+   "read_only": 1
+  },
+  {
+   "fieldname": "finance_book",
+   "fieldtype": "Link",
+   "label": "Finance Book",
+   "options": "Finance Book"
+  },
+  {
+   "depends_on": "eval:doc.docstatus == 0 || (doc.service_items && doc.service_items.length)",
+   "fieldname": "service_expenses_section",
+   "fieldtype": "Section Break",
+   "label": "Service Expenses"
+  },
+  {
+   "fieldname": "service_items",
+   "fieldtype": "Table",
+   "label": "Services",
+   "options": "Asset Capitalization Service Item"
+  },
+  {
+   "fieldname": "service_items_total",
+   "fieldtype": "Currency",
+   "label": "Service Expense Total Amount",
+   "options": "Company:company:default_currency",
+   "read_only": 1
+  },
+  {
+   "fieldname": "totals_section",
+   "fieldtype": "Section Break",
+   "label": "Totals"
+  },
+  {
+   "fieldname": "total_value",
+   "fieldtype": "Currency",
+   "label": "Total Value",
+   "options": "Company:company:default_currency",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_36",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "target_incoming_rate",
+   "fieldtype": "Currency",
+   "label": "Target Incoming Rate",
+   "options": "Company:company:default_currency",
+   "read_only": 1
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "accounting_dimensions_section",
+   "fieldtype": "Section Break",
+   "label": "Accounting Dimensions"
+  },
+  {
+   "fieldname": "cost_center",
+   "fieldtype": "Link",
+   "label": "Cost Center",
+   "options": "Cost Center"
+  },
+  {
+   "fieldname": "dimension_col_break",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "target_fixed_asset_account",
+   "fieldtype": "Link",
+   "label": "Target Fixed Asset Account",
+   "options": "Account",
+   "read_only": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "is_submittable": 1,
+ "links": [],
+ "modified": "2021-09-15 15:41:27.917458",
+ "modified_by": "Administrator",
+ "module": "Assets",
+ "name": "Asset Capitalization",
+ "naming_rule": "By \"Naming Series\" field",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Manufacturing Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  },
+  {
+   "amend": 1,
+   "cancel": 1,
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Quality Manager",
+   "share": 1,
+   "submit": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "title",
+ "track_changes": 1,
+ "track_seen": 1
+}
\ No newline at end of file
diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
new file mode 100644
index 0000000..a8f2d79
--- /dev/null
+++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
@@ -0,0 +1,663 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+import json
+
+import frappe
+
+# import erpnext
+from frappe import _
+from frappe.utils import cint, flt
+from six import string_types
+
+from erpnext.assets.doctype.asset.depreciation import (
+	get_gl_entries_on_asset_disposal,
+	get_gl_entries_on_asset_regain,
+	get_value_after_depreciation_on_disposal_date,
+)
+from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
+from erpnext.assets.doctype.asset_value_adjustment.asset_value_adjustment import (
+	get_current_asset_value,
+)
+from erpnext.controllers.stock_controller import StockController
+from erpnext.setup.doctype.brand.brand import get_brand_defaults
+from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
+from erpnext.stock import get_warehouse_account_map
+from erpnext.stock.doctype.item.item import get_item_defaults
+from erpnext.stock.get_item_details import (
+	get_default_cost_center,
+	get_default_expense_account,
+	get_item_warehouse,
+)
+from erpnext.stock.stock_ledger import get_previous_sle
+from erpnext.stock.utils import get_incoming_rate
+
+force_fields = ['target_item_name', 'target_asset_name', 'item_name', 'asset_name',
+	'target_is_fixed_asset', 'target_has_serial_no', 'target_has_batch_no',
+	'target_stock_uom', 'stock_uom', 'target_fixed_asset_account', 'fixed_asset_account']
+
+
+class AssetCapitalization(StockController):
+	def validate(self):
+		self.validate_posting_time()
+		self.set_missing_values(for_validate=True)
+		self.set_entry_type()
+		self.validate_target_item()
+		self.validate_target_asset()
+		self.validate_consumed_stock_item()
+		self.validate_consumed_asset_item()
+		self.validate_service_item()
+		self.set_warehouse_details()
+		self.set_asset_values()
+		self.calculate_totals()
+		self.set_title()
+
+	def before_submit(self):
+		self.validate_source_mandatory()
+
+	def on_submit(self):
+		self.update_stock_ledger()
+		self.make_gl_entries()
+
+	def on_cancel(self):
+		self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry', 'Repost Item Valuation')
+		self.update_stock_ledger()
+		self.make_gl_entries()
+
+	def set_entry_type(self):
+		self.entry_type = "Capitalization" if self.target_is_fixed_asset else "Decapitalization"
+
+	def set_title(self):
+		self.title = self.target_asset_name or self.target_item_name or self.target_item_code
+
+	def set_missing_values(self, for_validate=False):
+		target_item_details = get_target_item_details(self.target_item_code, self.company)
+		for k, v in target_item_details.items():
+			if self.meta.has_field(k) and (not self.get(k) or k in force_fields):
+				self.set(k, v)
+
+		# Remove asset if item not a fixed asset
+		if not self.target_is_fixed_asset:
+			self.target_asset = None
+
+		target_asset_details = get_target_asset_details(self.target_asset, self.company)
+		for k, v in target_asset_details.items():
+			if self.meta.has_field(k) and (not self.get(k) or k in force_fields):
+				self.set(k, v)
+
+		for d in self.stock_items:
+			args = self.as_dict()
+			args.update(d.as_dict())
+			args.doctype = self.doctype
+			args.name = self.name
+			consumed_stock_item_details = get_consumed_stock_item_details(args, get_valuation_rate=False)
+			for k, v in consumed_stock_item_details.items():
+				if d.meta.has_field(k) and (not d.get(k) or k in force_fields):
+					d.set(k, v)
+
+		for d in self.asset_items:
+			args = self.as_dict()
+			args.update(d.as_dict())
+			args.doctype = self.doctype
+			args.name = self.name
+			args.finance_book = d.get('finance_book') or self.get('finance_book')
+			consumed_asset_details = get_consumed_asset_details(args, get_asset_value=False)
+			for k, v in consumed_asset_details.items():
+				if d.meta.has_field(k) and (not d.get(k) or k in force_fields):
+					d.set(k, v)
+
+		for d in self.service_items:
+			args = self.as_dict()
+			args.update(d.as_dict())
+			args.doctype = self.doctype
+			args.name = self.name
+			service_item_details = get_service_item_details(args)
+			for k, v in service_item_details.items():
+				if d.meta.has_field(k) and (not d.get(k) or k in force_fields):
+					d.set(k, v)
+
+	def validate_target_item(self):
+		target_item = frappe.get_cached_doc("Item", self.target_item_code)
+
+		if not target_item.is_fixed_asset and not target_item.is_stock_item:
+			frappe.throw(_("Target Item {0} is neither a Fixed Asset nor a Stock Item")
+				.format(target_item.name))
+
+		if target_item.is_fixed_asset:
+			self.target_qty = 1
+		if flt(self.target_qty) <= 0:
+			frappe.throw(_("Target Qty must be a positive number"))
+
+		if not target_item.is_stock_item:
+			self.target_warehouse = None
+		if not target_item.is_fixed_asset:
+			self.target_asset = None
+			self.target_fixed_asset_account = None
+		if not target_item.has_batch_no:
+			self.target_batch_no = None
+		if not target_item.has_serial_no:
+			self.target_serial_no = ""
+
+		if target_item.is_stock_item and not self.target_warehouse:
+			frappe.throw(_("Target Warehouse is mandatory for Decapitalization"))
+
+		self.validate_item(target_item)
+
+	def validate_target_asset(self):
+		if self.target_is_fixed_asset and not self.target_asset:
+			frappe.throw(_("Target Asset is mandatory for Capitalization"))
+
+		if self.target_asset:
+			target_asset = self.get_asset_for_validation(self.target_asset)
+
+			if target_asset.item_code != self.target_item_code:
+				frappe.throw(_("Asset {0} does not belong to Item {1}").format(self.target_asset, self.target_item_code))
+
+			self.validate_asset(target_asset)
+
+	def validate_consumed_stock_item(self):
+		for d in self.stock_items:
+			if d.item_code:
+				item = frappe.get_cached_doc("Item", d.item_code)
+
+				if not item.is_stock_item:
+					frappe.throw(_("Row #{0}: Item {1} is not a stock item").format(d.idx, d.item_code))
+
+				if flt(d.stock_qty) <= 0:
+					frappe.throw(_("Row #{0}: Qty must be a positive number").format(d.idx))
+
+				self.validate_item(item)
+
+	def validate_consumed_asset_item(self):
+		for d in self.asset_items:
+			if d.asset:
+				if d.asset == self.target_asset:
+					frappe.throw(_("Row #{0}: Consumed Asset {1} cannot be the same as the Target Asset")
+						.format(d.idx, d.asset))
+
+				asset = self.get_asset_for_validation(d.asset)
+				self.validate_asset(asset)
+
+	def validate_service_item(self):
+		for d in self.service_items:
+			if d.item_code:
+				item = frappe.get_cached_doc("Item", d.item_code)
+
+				if item.is_stock_item or item.is_fixed_asset:
+					frappe.throw(_("Row #{0}: Item {1} is not a service item").format(d.idx, d.item_code))
+
+				if flt(d.qty) <= 0:
+					frappe.throw(_("Row #{0}: Qty must be a positive number").format(d.idx))
+
+				if flt(d.rate) <= 0:
+					frappe.throw(_("Row #{0}: Amount must be a positive number").format(d.idx))
+
+				self.validate_item(item)
+
+			if not d.cost_center:
+				d.cost_center = frappe.get_cached_value("Company", self.company, "cost_center")
+
+	def validate_source_mandatory(self):
+		if not self.target_is_fixed_asset and not self.get('asset_items'):
+			frappe.throw(_("Consumed Asset Items is mandatory for Decapitalization"))
+
+		if not self.get('stock_items') and not self.get('asset_items'):
+			frappe.throw(_("Consumed Stock Items or Consumed Asset Items is mandatory for Capitalization"))
+
+	def validate_item(self, item):
+		from erpnext.stock.doctype.item.item import validate_end_of_life
+		validate_end_of_life(item.name, item.end_of_life, item.disabled)
+
+	def get_asset_for_validation(self, asset):
+		return frappe.db.get_value("Asset", asset, ["name", "item_code", "company", "status", "docstatus"], as_dict=1)
+
+	def validate_asset(self, asset):
+		if asset.status in ("Draft", "Scrapped", "Sold", "Capitalized", "Decapitalized"):
+			frappe.throw(_("Asset {0} is {1}").format(asset.name, asset.status))
+
+		if asset.docstatus == 0:
+			frappe.throw(_("Asset {0} is Draft").format(asset.name))
+		if asset.docstatus == 2:
+			frappe.throw(_("Asset {0} is cancelled").format(asset.name))
+
+		if asset.company != self.company:
+			frappe.throw(_("Asset {0} does not belong to company {1}").format(asset.name, self.company))
+
+	@frappe.whitelist()
+	def set_warehouse_details(self):
+		for d in self.get('stock_items'):
+			if d.item_code and d.warehouse:
+				args = self.get_args_for_incoming_rate(d)
+				warehouse_details = get_warehouse_details(args)
+				d.update(warehouse_details)
+
+	@frappe.whitelist()
+	def set_asset_values(self):
+		for d in self.get('asset_items'):
+			if d.asset:
+				finance_book = d.get('finance_book') or self.get('finance_book')
+				d.current_asset_value = flt(get_current_asset_value(d.asset, finance_book=finance_book))
+				d.asset_value = get_value_after_depreciation_on_disposal_date(d.asset, self.posting_date,
+					finance_book=finance_book)
+
+	def get_args_for_incoming_rate(self, item):
+		return frappe._dict({
+			"item_code": item.item_code,
+			"warehouse": item.warehouse,
+			"posting_date": self.posting_date,
+			"posting_time": self.posting_time,
+			"qty": -1 * flt(item.stock_qty),
+			"serial_no": item.serial_no,
+			"batch_no": item.batch_no,
+			"voucher_type": self.doctype,
+			"voucher_no": self.name,
+			"company": self.company,
+			"allow_zero_valuation": cint(item.get('allow_zero_valuation_rate')),
+		})
+
+	def calculate_totals(self):
+		self.stock_items_total = 0
+		self.asset_items_total = 0
+		self.service_items_total = 0
+
+		for d in self.stock_items:
+			d.amount = flt(flt(d.stock_qty) * flt(d.valuation_rate), d.precision('amount'))
+			self.stock_items_total += d.amount
+
+		for d in self.asset_items:
+			d.asset_value = flt(flt(d.asset_value), d.precision('asset_value'))
+			self.asset_items_total += d.asset_value
+
+		for d in self.service_items:
+			d.amount = flt(flt(d.qty) * flt(d.rate), d.precision('amount'))
+			self.service_items_total += d.amount
+
+		self.stock_items_total = flt(self.stock_items_total, self.precision('stock_items_total'))
+		self.asset_items_total = flt(self.asset_items_total, self.precision('asset_items_total'))
+		self.service_items_total = flt(self.service_items_total, self.precision('service_items_total'))
+
+		self.total_value = self.stock_items_total + self.asset_items_total + self.service_items_total
+		self.total_value = flt(self.total_value, self.precision('total_value'))
+
+		self.target_qty = flt(self.target_qty, self.precision('target_qty'))
+		self.target_incoming_rate = self.total_value / self.target_qty
+
+	def update_stock_ledger(self):
+		sl_entries = []
+
+		for d in self.stock_items:
+			sle = self.get_sl_entries(d, {
+				"actual_qty": -flt(d.stock_qty),
+			})
+			sl_entries.append(sle)
+
+		if not frappe.db.get_value("Item", self.target_item_code, "is_fixed_asset", cache=1):
+			sle = self.get_sl_entries(self, {
+				"item_code": self.target_item_code,
+				"warehouse": self.target_warehouse,
+				"batch_no": self.target_batch_no,
+				"serial_no": self.target_serial_no,
+				"actual_qty": flt(self.target_qty),
+				"incoming_rate": flt(self.target_incoming_rate)
+			})
+			sl_entries.append(sle)
+
+		# reverse sl entries if cancel
+		if self.docstatus == 2:
+			sl_entries.reverse()
+
+		if sl_entries:
+			self.make_sl_entries(sl_entries)
+
+	def make_gl_entries(self, gl_entries=None, from_repost=False):
+		from erpnext.accounts.general_ledger import make_gl_entries, make_reverse_gl_entries
+
+		if not gl_entries:
+			gl_entries = self.get_gl_entries()
+
+		if self.docstatus == 1:
+			if gl_entries:
+				make_gl_entries(gl_entries, from_repost=from_repost)
+		elif self.docstatus == 2:
+			make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
+
+	def get_gl_entries(self, warehouse_account=None, default_expense_account=None, default_cost_center=None):
+		# Stock GL Entries
+		gl_entries = []
+
+		if not warehouse_account:
+			warehouse_account = get_warehouse_account_map(self.company)
+
+		precision = self.get_debit_field_precision()
+		sle_map = self.get_stock_ledger_details()
+
+		if self.target_is_fixed_asset:
+			target_account = self.target_fixed_asset_account
+		else:
+			target_account = warehouse_account[self.target_warehouse]["account"]
+
+		target_against = set()
+
+		# Consumed Stock Items
+		total_consumed_stock_value = 0
+		for item_row in self.stock_items:
+			sle_list = sle_map.get(item_row.name)
+			if sle_list:
+				for sle in sle_list:
+					stock_value_difference = flt(sle.stock_value_difference, precision)
+					total_consumed_stock_value += -1 * sle.stock_value_difference
+
+					account = warehouse_account[sle.warehouse]["account"]
+					target_against.add(account)
+
+					gl_entries.append(self.get_gl_dict({
+						"account": account,
+						"against": target_account,
+						"cost_center": item_row.cost_center,
+						"project": item_row.get('project') or self.get('project'),
+						"remarks": self.get("remarks") or "Accounting Entry for Stock",
+						"credit": -1 * stock_value_difference,
+					}, warehouse_account[sle.warehouse]["account_currency"], item=item_row))
+
+		# Consumed Assets
+		for item in self.asset_items:
+			asset = self.get_asset(item)
+
+			if self.docstatus == 2:
+				fixed_asset_gl_entries = get_gl_entries_on_asset_regain(asset,
+					item.asset_value, item.get('finance_book') or self.get('finance_book'))
+				asset.db_set("disposal_date", None)
+
+				self.set_consumed_asset_status(asset)
+
+				if asset.calculate_depreciation:
+					self.reverse_depreciation_entry_made_after_disposal(asset)
+					self.reset_depreciation_schedule(asset)
+			else:
+				if asset.calculate_depreciation:
+					self.depreciate_asset(asset)
+					asset.reload()
+
+				fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(asset,
+					item.asset_value, item.get('finance_book') or self.get('finance_book'))
+				asset.db_set("disposal_date", self.posting_date)
+
+				self.set_consumed_asset_status(asset)
+
+			for gle in fixed_asset_gl_entries:
+				gle["against"] = target_account
+				gl_entries.append(self.get_gl_dict(gle, item=item))
+
+		# Service Expenses
+		total_service_expenses = 0
+		for item_row in self.service_items:
+			expense_amount = flt(item_row.amount, precision)
+			total_service_expenses += expense_amount
+			target_against.add(item_row.expense_account)
+
+			gl_entries.append(self.get_gl_dict({
+				"account": item_row.expense_account,
+				"against": target_account,
+				"cost_center": item_row.cost_center,
+				"project": item_row.get('project') or self.get('project'),
+				"remarks": self.get("remarks") or "Accounting Entry for Stock",
+				"credit": expense_amount,
+			}, item=item_row))
+
+		target_against = ", ".join(target_against)
+		total_target_stock_value = 0
+		total_target_asset_value = 0
+
+		if self.target_is_fixed_asset:
+			# Target Asset Item
+			total_target_asset_value = flt(self.total_value, precision)
+			gl_entries.append(self.get_gl_dict({
+				"account": self.target_fixed_asset_account,
+				"against": target_against,
+				"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
+				"debit": total_target_asset_value,
+				"cost_center": self.get('cost_center')
+			}, item=self))
+
+			if self.docstatus == 1:
+				asset_doc = frappe.get_doc("Asset", self.target_asset)
+				asset_doc.purchase_date = self.posting_date
+				asset_doc.gross_purchase_amount = total_target_asset_value
+				asset_doc.purchase_receipt_amount = total_target_asset_value
+				asset_doc.prepare_depreciation_data()
+				asset_doc.flags.ignore_validate_update_after_submit = True
+				asset_doc.save()
+		else:
+			# Target Stock Item
+			sle_list = sle_map.get(self.name)
+			for sle in sle_list:
+				stock_value_difference = flt(sle.stock_value_difference, precision)
+				total_target_stock_value += sle.stock_value_difference
+				account = warehouse_account[sle.warehouse]["account"]
+
+				gl_entries.append(self.get_gl_dict({
+					"account": account,
+					"against": target_against,
+					"cost_center": self.cost_center,
+					"project": self.get('project'),
+					"remarks": self.get("remarks") or "Accounting Entry for Stock",
+					"debit": stock_value_difference,
+				}, warehouse_account[sle.warehouse]["account_currency"], item=self))
+
+		return gl_entries
+
+	def get_asset(self, item):
+		asset = frappe.get_doc("Asset", item.asset)
+		self.check_finance_books(item, asset)
+		return asset
+
+	def set_consumed_asset_status(self, asset):
+		if self.docstatus == 1:
+			asset.set_status("Capitalized" if self.target_is_fixed_asset else "Decapitalized")
+		else:
+			asset.set_status()
+
+
+@frappe.whitelist()
+def get_target_item_details(item_code=None, company=None):
+	out = frappe._dict()
+
+	# Get Item Details
+	item = frappe._dict()
+	if item_code:
+		item = frappe.get_cached_doc("Item", item_code)
+
+	# Set Item Details
+	out.target_item_name = item.item_name
+	out.target_stock_uom = item.stock_uom
+	out.target_is_fixed_asset = cint(item.is_fixed_asset)
+	out.target_has_batch_no = cint(item.has_batch_no)
+	out.target_has_serial_no = cint(item.has_serial_no)
+
+	if out.target_is_fixed_asset:
+		out.target_qty = 1
+		out.target_warehouse = None
+	else:
+		out.target_asset = None
+
+	if not out.target_has_batch_no:
+		out.target_batch_no = None
+	if not out.target_has_serial_no:
+		out.target_serial_no = ""
+
+	# Cost Center
+	item_defaults = get_item_defaults(item.name, company)
+	item_group_defaults = get_item_group_defaults(item.name, company)
+	brand_defaults = get_brand_defaults(item.name, company)
+	out.cost_center = get_default_cost_center(frappe._dict({'item_code': item.name, 'company': company}),
+		item_defaults, item_group_defaults, brand_defaults)
+
+	# Set Entry Type
+	if not item_code:
+		out.entry_type = ""
+	elif out.target_is_fixed_asset:
+		out.entry_type = "Capitalization"
+	else:
+		out.entry_type = "Decapitalization"
+
+	return out
+
+
+@frappe.whitelist()
+def get_target_asset_details(asset=None, company=None):
+	out = frappe._dict()
+
+	# Get Asset Details
+	asset_details = frappe._dict()
+	if asset:
+		asset_details = frappe.db.get_value("Asset", asset, ['asset_name', 'item_code'], as_dict=1)
+		if not asset_details:
+			frappe.throw(_("Asset {0} does not exist").format(asset))
+
+		# Re-set item code from Asset
+		out.target_item_code = asset_details.item_code
+
+	# Set Asset Details
+	out.asset_name = asset_details.asset_name
+
+	if asset_details.item_code:
+		out.target_fixed_asset_account = get_asset_category_account('fixed_asset_account', item=asset_details.item_code,
+			company=company)
+	else:
+		out.target_fixed_asset_account = None
+
+	return out
+
+
+@frappe.whitelist()
+def get_consumed_stock_item_details(args, get_valuation_rate=True):
+	if isinstance(args, string_types):
+		args = json.loads(args)
+
+	args = frappe._dict(args)
+	out = frappe._dict()
+
+	item = frappe._dict()
+	if args.item_code:
+		item = frappe.get_cached_doc("Item", args.item_code)
+
+	out.item_name = item.item_name
+	out.batch_no = None
+	out.serial_no = ""
+
+	out.stock_qty = flt(args.stock_qty) or 1
+	out.stock_uom = item.stock_uom
+
+	out.warehouse = get_item_warehouse(item, args, overwrite_warehouse=True) if item else None
+
+	# Cost Center
+	item_defaults = get_item_defaults(item.name, args.company)
+	item_group_defaults = get_item_group_defaults(item.name, args.company)
+	brand_defaults = get_brand_defaults(item.name, args.company)
+	out.cost_center = get_default_cost_center(args, item_defaults, item_group_defaults, brand_defaults)
+
+	if get_valuation_rate:
+		if args.item_code and out.warehouse:
+			incoming_rate_args = frappe._dict({
+				'item_code': args.item_code,
+				'warehouse': out.warehouse,
+				'posting_date': args.posting_date,
+				'posting_time': args.posting_time,
+				'qty': -1 * flt(out.stock_qty),
+				"voucher_type": args.doctype,
+				"voucher_no": args.name,
+				"company": args.company,
+			})
+			out.update(get_warehouse_details(incoming_rate_args))
+		else:
+			out.valuation_rate = 0
+			out.actual_qty = 0
+
+	return out
+
+
+@frappe.whitelist()
+def get_warehouse_details(args):
+	if isinstance(args, string_types):
+		args = json.loads(args)
+
+	args = frappe._dict(args)
+
+	out = {}
+	if args.warehouse and args.item_code:
+		out = {
+			"actual_qty": get_previous_sle(args).get("qty_after_transaction") or 0,
+			"valuation_rate": get_incoming_rate(args, raise_error_if_no_rate=False)
+		}
+	return out
+
+
+@frappe.whitelist()
+def get_consumed_asset_details(args, get_asset_value=True):
+	if isinstance(args, string_types):
+		args = json.loads(args)
+
+	args = frappe._dict(args)
+	out = frappe._dict()
+
+	asset_details = frappe._dict()
+	if args.asset:
+		asset_details = frappe.db.get_value("Asset", args.asset, ['asset_name', 'item_code', 'item_name'], as_dict=1)
+		if not asset_details:
+			frappe.throw(_("Asset {0} does not exist").format(args.asset))
+
+	out.item_code = asset_details.item_code
+	out.asset_name = asset_details.asset_name
+	out.item_name = asset_details.item_name
+
+	if get_asset_value:
+		if args.asset:
+			out.current_asset_value = flt(get_current_asset_value(args.asset, finance_book=args.finance_book))
+			out.asset_value = get_value_after_depreciation_on_disposal_date(args.asset, args.posting_date,
+				finance_book=args.finance_book)
+		else:
+			out.current_asset_value = 0
+			out.asset_value = 0
+
+	# Account
+	if asset_details.item_code:
+		out.fixed_asset_account = get_asset_category_account('fixed_asset_account', item=asset_details.item_code,
+			company=args.company)
+	else:
+		out.fixed_asset_account = None
+
+	# Cost Center
+	if asset_details.item_code:
+		item = frappe.get_cached_doc("Item", asset_details.item_code)
+		item_defaults = get_item_defaults(item.name, args.company)
+		item_group_defaults = get_item_group_defaults(item.name, args.company)
+		brand_defaults = get_brand_defaults(item.name, args.company)
+		out.cost_center = get_default_cost_center(args, item_defaults, item_group_defaults, brand_defaults)
+
+	return out
+
+
+@frappe.whitelist()
+def get_service_item_details(args):
+	if isinstance(args, string_types):
+		args = json.loads(args)
+
+	args = frappe._dict(args)
+	out = frappe._dict()
+
+	item = frappe._dict()
+	if args.item_code:
+		item = frappe.get_cached_doc("Item", args.item_code)
+
+	out.item_name = item.item_name
+	out.qty = flt(args.qty) or 1
+	out.uom = item.purchase_uom or item.stock_uom
+
+	item_defaults = get_item_defaults(item.name, args.company)
+	item_group_defaults = get_item_group_defaults(item.name, args.company)
+	brand_defaults = get_brand_defaults(item.name, args.company)
+
+	out.expense_account = get_default_expense_account(args, item_defaults, item_group_defaults, brand_defaults)
+	out.cost_center = get_default_cost_center(args, item_defaults, item_group_defaults, brand_defaults)
+
+	return out
diff --git a/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py
new file mode 100644
index 0000000..7046de6
--- /dev/null
+++ b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py
@@ -0,0 +1,335 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+import unittest
+
+import frappe
+from frappe.utils import cint, flt, getdate, now_datetime
+
+from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries
+from erpnext.assets.doctype.asset.test_asset import (
+	create_asset,
+	create_asset_data,
+	set_depreciation_settings_in_company,
+)
+from erpnext.stock.doctype.item.test_item import create_item
+
+
+class TestAssetCapitalization(unittest.TestCase):
+	def setUp(self):
+		set_depreciation_settings_in_company()
+		create_asset_data()
+		create_asset_capitalization_data()
+		frappe.db.sql("delete from `tabTax Rule`")
+
+	def test_capitalization(self):
+		# Variables
+		consumed_asset_value = 100_000
+
+		stock_rate = 1000
+		stock_qty = 2
+		stock_amount = 2000
+
+		service_rate = 500
+		service_qty = 2
+		service_amount = 1000
+
+		total_amount = 103_000
+
+		# Create assets
+		target_asset = create_asset(asset_name='Asset Capitalization Target Asset', submit=1)
+		consumed_asset = create_asset(asset_name='Asset Capitalization Consumable Asset', asset_value=consumed_asset_value,
+			submit=1)
+
+		# Create and submit Asset Captitalization
+		asset_capitalization = create_asset_capitalization(target_asset=target_asset.name,
+			stock_qty=stock_qty, stock_rate=stock_rate,
+			consumed_asset=consumed_asset.name,
+			service_qty=service_qty, service_rate=service_rate,
+			service_expense_account='Expenses Included In Asset Valuation - _TC',
+			submit=1)
+
+		# Test Asset Capitalization values
+		self.assertEqual(asset_capitalization.entry_type, 'Capitalization')
+		self.assertEqual(asset_capitalization.target_qty, 1)
+
+		self.assertEqual(asset_capitalization.stock_items[0].valuation_rate, stock_rate)
+		self.assertEqual(asset_capitalization.stock_items[0].amount, stock_amount)
+		self.assertEqual(asset_capitalization.stock_items_total, stock_amount)
+
+		self.assertEqual(asset_capitalization.asset_items[0].asset_value, consumed_asset_value)
+		self.assertEqual(asset_capitalization.asset_items_total, consumed_asset_value)
+
+		self.assertEqual(asset_capitalization.service_items[0].amount, service_amount)
+		self.assertEqual(asset_capitalization.service_items_total, service_amount)
+
+		self.assertEqual(asset_capitalization.total_value, total_amount)
+		self.assertEqual(asset_capitalization.target_incoming_rate, total_amount)
+
+		# Test Target Asset values
+		target_asset.reload()
+		self.assertEqual(target_asset.gross_purchase_amount, total_amount)
+		self.assertEqual(target_asset.purchase_receipt_amount, total_amount)
+
+		# Test Consumed Asset values
+		self.assertEqual(consumed_asset.db_get('status'), 'Capitalized')
+
+		# Test General Ledger Entries
+		expected_gle = {
+			'_Test Fixed Asset - _TC': 3000,
+			'Expenses Included In Asset Valuation - _TC': -1000,
+			'Stock In Hand - _TC' : -2000
+		}
+		actual_gle = get_actual_gle_dict(asset_capitalization.name)
+
+		self.assertEqual(actual_gle, expected_gle)
+
+		# Test Stock Ledger Entries
+		expected_sle = {
+			('Capitalization Source Stock Item', '_Test Warehouse - _TC'): {
+				'actual_qty': -stock_qty, 'stock_value_difference': -stock_amount
+			}
+		}
+		actual_sle = get_actual_sle_dict(asset_capitalization.name)
+
+		self.assertEqual(actual_sle, expected_sle)
+
+		# Cancel Asset Capitalization and make test entries and status are reversed
+		asset_capitalization.cancel()
+		self.assertEqual(consumed_asset.db_get('status'), 'Submitted')
+		self.assertFalse(get_actual_gle_dict(asset_capitalization.name))
+		self.assertFalse(get_actual_sle_dict(asset_capitalization.name))
+
+	def test_decapitalization_with_depreciation(self):
+		# Variables
+		purchase_date = '2020-01-01'
+		depreciation_start_date = '2020-12-31'
+		capitalization_date = '2021-06-30'
+
+		total_number_of_depreciations = 3
+		expected_value_after_useful_life = 10_000
+		consumed_asset_purchase_value = 100_000
+		consumed_asset_current_value = 70_000
+		consumed_asset_value_before_disposal = 55_000
+
+		target_qty = 10
+		target_incoming_rate = 5500
+
+		depreciation_before_disposal_amount = 15_000
+		accumulated_depreciation = 45_000
+
+		# to accomodate for depreciation on disposal calculation minor difference
+		consumed_asset_value_before_disposal = 55_123.29
+		target_incoming_rate = 5512.329
+		depreciation_before_disposal_amount = 14_876.71
+		accumulated_depreciation = 44_876.71
+
+		# Create assets
+		consumed_asset = create_depreciation_asset(
+			asset_name='Asset Capitalization Consumable Asset',
+			asset_value=consumed_asset_purchase_value,
+			purchase_date=purchase_date,
+			depreciation_start_date=depreciation_start_date,
+			depreciation_method='Straight Line',
+			total_number_of_depreciations=total_number_of_depreciations,
+			frequency_of_depreciation=12,
+			expected_value_after_useful_life=expected_value_after_useful_life,
+			submit=1)
+
+		# Create and submit Asset Captitalization
+		asset_capitalization = create_asset_capitalization(
+			posting_date=capitalization_date,  # half a year
+			target_item_code="Capitalization Target Stock Item",
+			target_qty=target_qty,
+			consumed_asset=consumed_asset.name,
+			submit=1)
+
+		# Test Asset Capitalization values
+		self.assertEqual(asset_capitalization.entry_type, 'Decapitalization')
+
+		self.assertEqual(asset_capitalization.asset_items[0].current_asset_value, consumed_asset_current_value)
+		self.assertEqual(asset_capitalization.asset_items[0].asset_value, consumed_asset_value_before_disposal)
+		self.assertEqual(asset_capitalization.asset_items_total, consumed_asset_value_before_disposal)
+
+		self.assertEqual(asset_capitalization.total_value, consumed_asset_value_before_disposal)
+		self.assertEqual(asset_capitalization.target_incoming_rate, target_incoming_rate)
+
+		# Test Consumed Asset values
+		consumed_asset.reload()
+		self.assertEqual(consumed_asset.status, 'Decapitalized')
+
+		consumed_depreciation_schedule = [d for d in consumed_asset.schedules
+			if getdate(d.schedule_date) == getdate(capitalization_date)]
+		self.assertTrue(consumed_depreciation_schedule and consumed_depreciation_schedule[0].journal_entry)
+		self.assertEqual(consumed_depreciation_schedule[0].depreciation_amount, depreciation_before_disposal_amount)
+
+		# Test General Ledger Entries
+		expected_gle = {
+			'Stock In Hand - _TC': consumed_asset_value_before_disposal,
+			'_Test Accumulated Depreciations - _TC': accumulated_depreciation,
+			'_Test Fixed Asset - _TC': -consumed_asset_purchase_value,
+		}
+		actual_gle = get_actual_gle_dict(asset_capitalization.name)
+
+		self.assertEqual(actual_gle, expected_gle)
+
+		# Cancel Asset Capitalization and make test entries and status are reversed
+		asset_capitalization.reload()
+		asset_capitalization.cancel()
+		self.assertEqual(consumed_asset.db_get('status'), 'Partially Depreciated')
+		self.assertFalse(get_actual_gle_dict(asset_capitalization.name))
+		self.assertFalse(get_actual_sle_dict(asset_capitalization.name))
+
+
+def create_asset_capitalization_data():
+	create_item("Capitalization Target Stock Item",
+		is_stock_item=1, is_fixed_asset=0, is_purchase_item=0)
+	create_item("Capitalization Source Stock Item",
+		is_stock_item=1, is_fixed_asset=0, is_purchase_item=0)
+	create_item("Capitalization Source Service Item",
+		is_stock_item=0, is_fixed_asset=0, is_purchase_item=0)
+
+
+def create_asset_capitalization(**args):
+	from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
+
+	args = frappe._dict(args)
+
+	now = now_datetime()
+	target_asset = frappe.get_doc("Asset", args.target_asset) if args.target_asset else frappe._dict()
+	target_item_code = target_asset.item_code or args.target_item_code
+	company = target_asset.company or args.company or "_Test Company"
+	warehouse = args.warehouse or create_warehouse("_Test Warehouse", company=company)
+	target_warehouse = args.target_warehouse or warehouse
+	source_warehouse = args.source_warehouse or warehouse
+
+	asset_capitalization = frappe.new_doc("Asset Capitalization")
+	asset_capitalization.update({
+		"company": company,
+		"posting_date": args.posting_date or now.strftime('%Y-%m-%d'),
+		"posting_time": args.posting_time or now.strftime('%H:%M:%S.%f'),
+		"target_item_code": target_item_code,
+		"target_asset": target_asset.name,
+		"target_warehouse": target_warehouse,
+		"target_qty": flt(args.target_qty) or 1,
+		"target_batch_no": args.target_batch_no,
+		"target_serial_no": args.target_serial_no,
+		"finance_book": args.finance_book
+	})
+
+	if args.posting_date or args.posting_time:
+		asset_capitalization.set_posting_time = 1
+
+	if flt(args.stock_rate):
+		asset_capitalization.append("stock_items", {
+			"item_code": args.stock_item or "Capitalization Source Stock Item",
+			"warehouse": source_warehouse,
+			"stock_qty": flt(args.stock_qty) or 1,
+			"batch_no": args.stock_batch_no,
+			"serial_no": args.stock_serial_no,
+		})
+
+	if args.consumed_asset:
+		asset_capitalization.append("asset_items", {
+			"asset": args.consumed_asset,
+		})
+
+	if flt(args.service_rate):
+		asset_capitalization.append("service_items", {
+			"item_code": args.service_item or "Capitalization Source Service Item",
+			"expense_account": args.service_expense_account,
+			"qty": flt(args.service_qty) or 1,
+			"rate": flt(args.service_rate)
+		})
+
+	if args.submit:
+		create_stock_reconciliation(asset_capitalization, stock_rate=args.stock_rate)
+
+	asset_capitalization.insert()
+
+	if args.submit:
+		asset_capitalization.submit()
+
+	return asset_capitalization
+
+
+def create_stock_reconciliation(asset_capitalization, stock_rate=0):
+	from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
+		create_stock_reconciliation,
+	)
+	if not asset_capitalization.get('stock_items'):
+		return
+
+	return create_stock_reconciliation(
+		item_code=asset_capitalization.stock_items[0].item_code,
+		warehouse=asset_capitalization.stock_items[0].warehouse,
+		qty=flt(asset_capitalization.stock_items[0].stock_qty),
+		rate=flt(stock_rate),
+		company=asset_capitalization.company)
+
+
+def create_depreciation_asset(**args):
+	args = frappe._dict(args)
+
+	asset = frappe.new_doc("Asset")
+	asset.is_existing_asset = 1
+	asset.calculate_depreciation = 1
+	asset.asset_owner = "Company"
+
+	asset.company = args.company or "_Test Company"
+	asset.item_code = args.item_code or "Macbook Pro"
+	asset.asset_name = args.asset_name or asset.item_code
+	asset.location = args.location or "Test Location"
+
+	asset.purchase_date = args.purchase_date or '2020-01-01'
+	asset.available_for_use_date = args.available_for_use_date or asset.purchase_date
+
+	asset.gross_purchase_amount = args.asset_value or 100000
+	asset.purchase_receipt_amount = asset.gross_purchase_amount
+
+	finance_book = asset.append('finance_books')
+	finance_book.depreciation_start_date = args.depreciation_start_date or '2020-12-31'
+	finance_book.depreciation_method = args.depreciation_method or 'Straight Line'
+	finance_book.total_number_of_depreciations = cint(args.total_number_of_depreciations) or 3
+	finance_book.frequency_of_depreciation = cint(args.frequency_of_depreciation) or 12
+	finance_book.expected_value_after_useful_life = flt(args.expected_value_after_useful_life)
+
+	if args.submit:
+		asset.submit()
+
+		frappe.db.set_value("Company", "_Test Company", "series_for_depreciation_entry", "DEPR-")
+		post_depreciation_entries(date=finance_book.depreciation_start_date)
+		asset.load_from_db()
+
+	return asset
+
+
+def get_actual_gle_dict(name):
+	return dict(frappe.db.sql("""
+		select account, sum(debit-credit) as diff
+		from `tabGL Entry`
+		where voucher_type = 'Asset Capitalization' and voucher_no = %s
+		group by account
+		having diff != 0
+	""", name))
+
+
+def get_actual_sle_dict(name):
+	sles = frappe.db.sql("""
+		select
+			item_code, warehouse,
+			sum(actual_qty) as actual_qty,
+			sum(stock_value_difference) as stock_value_difference
+		from `tabStock Ledger Entry`
+		where voucher_type = 'Asset Capitalization' and voucher_no = %s
+		group by item_code, warehouse
+		having actual_qty != 0
+	""", name, as_dict=1)
+
+	sle_dict = {}
+	for d in sles:
+		sle_dict[(d.item_code, d.warehouse)] = {
+			'actual_qty': d.actual_qty, 'stock_value_difference': d.stock_value_difference
+		}
+
+	return sle_dict
diff --git a/erpnext/assets/doctype/asset_capitalization_asset_item/__init__.py b/erpnext/assets/doctype/asset_capitalization_asset_item/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/assets/doctype/asset_capitalization_asset_item/__init__.py
diff --git a/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.json b/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.json
new file mode 100644
index 0000000..ebaaffb
--- /dev/null
+++ b/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.json
@@ -0,0 +1,128 @@
+{
+ "actions": [],
+ "creation": "2021-09-05 15:52:10.124538",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "asset",
+  "asset_name",
+  "finance_book",
+  "column_break_3",
+  "item_code",
+  "item_name",
+  "section_break_6",
+  "current_asset_value",
+  "asset_value",
+  "column_break_9",
+  "accounting_dimensions_section",
+  "cost_center",
+  "dimension_col_break",
+  "fixed_asset_account"
+ ],
+ "fields": [
+  {
+   "fieldname": "asset",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Asset",
+   "options": "Asset",
+   "reqd": 1
+  },
+  {
+   "fetch_from": "asset.asset_name",
+   "fieldname": "asset_name",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Asset Name",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_3",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fetch_from": "asset.item_code",
+   "fieldname": "item_code",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Item Code",
+   "options": "Item",
+   "read_only": 1,
+   "reqd": 1
+  },
+  {
+   "fieldname": "item_name",
+   "fieldtype": "Data",
+   "label": "Item Name",
+   "read_only": 1
+  },
+  {
+   "fieldname": "section_break_6",
+   "fieldtype": "Section Break",
+   "label": "Value"
+  },
+  {
+   "default": "0",
+   "fieldname": "asset_value",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Asset Value",
+   "options": "Company:company:default_currency",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_9",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "fixed_asset_account",
+   "fieldtype": "Link",
+   "label": "Fixed Asset Account",
+   "options": "Account",
+   "read_only": 1
+  },
+  {
+   "fieldname": "cost_center",
+   "fieldtype": "Link",
+   "label": "Cost Center",
+   "options": "Cost Center"
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "accounting_dimensions_section",
+   "fieldtype": "Section Break",
+   "label": "Accounting Dimensions"
+  },
+  {
+   "fieldname": "dimension_col_break",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "finance_book",
+   "fieldtype": "Link",
+   "label": "Finance Book",
+   "options": "Finance Book"
+  },
+  {
+   "fieldname": "current_asset_value",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Current Asset Value",
+   "options": "Company:company:default_currency",
+   "read_only": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-09-12 14:30:02.915132",
+ "modified_by": "Administrator",
+ "module": "Assets",
+ "name": "Asset Capitalization Asset Item",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.py b/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.py
new file mode 100644
index 0000000..ba356d6
--- /dev/null
+++ b/erpnext/assets/doctype/asset_capitalization_asset_item/asset_capitalization_asset_item.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class AssetCapitalizationAssetItem(Document):
+	pass
diff --git a/erpnext/assets/doctype/asset_capitalization_service_item/__init__.py b/erpnext/assets/doctype/asset_capitalization_service_item/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/assets/doctype/asset_capitalization_service_item/__init__.py
diff --git a/erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.json b/erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.json
new file mode 100644
index 0000000..0ae1c14
--- /dev/null
+++ b/erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.json
@@ -0,0 +1,122 @@
+{
+ "actions": [],
+ "creation": "2021-09-06 13:32:08.642060",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "item_code",
+  "item_name",
+  "column_break_3",
+  "expense_account",
+  "section_break_6",
+  "qty",
+  "uom",
+  "column_break_9",
+  "rate",
+  "amount",
+  "accounting_dimensions_section",
+  "cost_center",
+  "dimension_col_break"
+ ],
+ "fields": [
+  {
+   "bold": 1,
+   "fieldname": "item_code",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Item Code",
+   "options": "Item"
+  },
+  {
+   "fetch_from": "item_code.item_name",
+   "fieldname": "item_name",
+   "fieldtype": "Data",
+   "label": "Item Name",
+   "read_only": 1
+  },
+  {
+   "fieldname": "column_break_3",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "expense_account",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Expense Account",
+   "options": "Account",
+   "reqd": 1
+  },
+  {
+   "fieldname": "section_break_6",
+   "fieldtype": "Section Break",
+   "label": "Qty and Rate"
+  },
+  {
+   "columns": 1,
+   "default": "1",
+   "fieldname": "qty",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Qty",
+   "non_negative": 1
+  },
+  {
+   "columns": 1,
+   "fetch_from": "stock_item_code.stock_uom",
+   "fieldname": "uom",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "UOM",
+   "options": "UOM"
+  },
+  {
+   "fieldname": "column_break_9",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "rate",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Rate",
+   "options": "Company:company:default_currency"
+  },
+  {
+   "default": "0",
+   "fieldname": "amount",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Amount",
+   "options": "Company:company:default_currency",
+   "read_only": 1
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "accounting_dimensions_section",
+   "fieldtype": "Section Break",
+   "label": "Accounting Dimensions"
+  },
+  {
+   "fieldname": "cost_center",
+   "fieldtype": "Link",
+   "label": "Cost Center",
+   "options": "Cost Center"
+  },
+  {
+   "fieldname": "dimension_col_break",
+   "fieldtype": "Column Break"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-09-08 15:52:08.598100",
+ "modified_by": "Administrator",
+ "module": "Assets",
+ "name": "Asset Capitalization Service Item",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.py b/erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.py
new file mode 100644
index 0000000..28d018e
--- /dev/null
+++ b/erpnext/assets/doctype/asset_capitalization_service_item/asset_capitalization_service_item.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class AssetCapitalizationServiceItem(Document):
+	pass
diff --git a/erpnext/assets/doctype/asset_capitalization_stock_item/__init__.py b/erpnext/assets/doctype/asset_capitalization_stock_item/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/assets/doctype/asset_capitalization_stock_item/__init__.py
diff --git a/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json
new file mode 100644
index 0000000..14eb0f6
--- /dev/null
+++ b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.json
@@ -0,0 +1,156 @@
+{
+ "actions": [],
+ "creation": "2021-09-05 15:23:23.492310",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "item_code",
+  "item_name",
+  "column_break_3",
+  "warehouse",
+  "section_break_6",
+  "stock_qty",
+  "stock_uom",
+  "actual_qty",
+  "column_break_9",
+  "valuation_rate",
+  "amount",
+  "batch_and_serial_no_section",
+  "batch_no",
+  "column_break_13",
+  "serial_no",
+  "accounting_dimensions_section",
+  "cost_center",
+  "dimension_col_break"
+ ],
+ "fields": [
+  {
+   "fieldname": "column_break_3",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "warehouse",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Warehouse",
+   "options": "Warehouse",
+   "reqd": 1
+  },
+  {
+   "fieldname": "batch_no",
+   "fieldtype": "Link",
+   "label": "Batch No",
+   "options": "Batch"
+  },
+  {
+   "fieldname": "section_break_6",
+   "fieldtype": "Section Break",
+   "label": "Qty and Rate"
+  },
+  {
+   "columns": 1,
+   "fieldname": "stock_qty",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Qty",
+   "non_negative": 1
+  },
+  {
+   "columns": 1,
+   "fetch_from": "stock_item_code.stock_uom",
+   "fieldname": "stock_uom",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Stock UOM",
+   "options": "UOM",
+   "read_only": 1,
+   "reqd": 1
+  },
+  {
+   "fieldname": "column_break_9",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "valuation_rate",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Valuation Rate",
+   "options": "Company:company:default_currency",
+   "read_only": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "amount",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Amount",
+   "options": "Company:company:default_currency",
+   "read_only": 1
+  },
+  {
+   "fieldname": "batch_and_serial_no_section",
+   "fieldtype": "Section Break",
+   "label": "Batch and Serial No"
+  },
+  {
+   "fieldname": "column_break_13",
+   "fieldtype": "Column Break"
+  },
+  {
+   "fieldname": "serial_no",
+   "fieldtype": "Small Text",
+   "label": "Serial No"
+  },
+  {
+   "fieldname": "item_code",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Item Code",
+   "options": "Item",
+   "reqd": 1
+  },
+  {
+   "fetch_from": "item_code.item_name",
+   "fieldname": "item_name",
+   "fieldtype": "Data",
+   "label": "Item Name",
+   "read_only": 1
+  },
+  {
+   "fieldname": "actual_qty",
+   "fieldtype": "Float",
+   "label": "Actual Qty in Warehouse",
+   "no_copy": 1,
+   "read_only": 1
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "accounting_dimensions_section",
+   "fieldtype": "Section Break",
+   "label": "Accounting Dimensions"
+  },
+  {
+   "fieldname": "cost_center",
+   "fieldtype": "Link",
+   "label": "Cost Center",
+   "options": "Cost Center"
+  },
+  {
+   "fieldname": "dimension_col_break",
+   "fieldtype": "Column Break"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-09-08 15:56:20.230548",
+ "modified_by": "Administrator",
+ "module": "Assets",
+ "name": "Asset Capitalization Stock Item",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.py b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.py
new file mode 100644
index 0000000..5d6f98d
--- /dev/null
+++ b/erpnext/assets/doctype/asset_capitalization_stock_item/asset_capitalization_stock_item.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class AssetCapitalizationStockItem(Document):
+	pass
diff --git a/erpnext/assets/workspace/assets/assets.json b/erpnext/assets/workspace/assets/assets.json
index 495de46..292d081 100644
--- a/erpnext/assets/workspace/assets/assets.json
+++ b/erpnext/assets/workspace/assets/assets.json
@@ -131,6 +131,17 @@
    "type": "Link"
   },
   {
+   "dependencies": "Asset",
+   "hidden": 0,
+   "is_query_report": 0,
+   "label": "Asset Capitalization",
+   "link_count": 0,
+   "link_to": "Asset Capitalization",
+   "link_type": "DocType",
+   "onboard": 0,
+   "type": "Link"
+  },
+  {
    "hidden": 0,
    "is_query_report": 0,
    "label": "Reports",
@@ -172,7 +183,7 @@
    "type": "Link"
   }
  ],
- "modified": "2021-08-05 12:15:54.839453",
+ "modified": "2021-10-04 12:15:54.839454",
  "modified_by": "Administrator",
  "module": "Assets",
  "name": "Assets",
@@ -205,4 +216,4 @@
   }
  ],
  "title": "Assets"
-}
\ No newline at end of file
+}
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index e0551a4..9fa22ed 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -36,6 +36,7 @@
 	validate_party_frozen_disabled,
 )
 from erpnext.accounts.utils import get_account_currency, get_fiscal_years, validate_fiscal_year
+from erpnext.assets.doctype.asset.depreciation import make_depreciation_entry
 from erpnext.buying.utils import update_last_purchase_rate
 from erpnext.controllers.print_settings import (
 	set_print_templates_for_item_table,
@@ -1505,6 +1506,92 @@
 		jv.save()
 		jv.submit()
 
+	def check_finance_books(self, item, asset):
+		if (len(asset.finance_books) > 1 and not item.get('finance_book') and not self.get('finance_book')
+			and asset.finance_books[0].finance_book):
+			frappe.throw(_("Select finance book for the item {0} at row {1}")
+				.format(item.item_code, item.idx))
+
+	def depreciate_asset(self, asset):
+		asset.flags.ignore_validate_update_after_submit = True
+		asset.prepare_depreciation_data(date_of_disposal=self.posting_date)
+		asset.save()
+
+		make_depreciation_entry(asset.name, self.posting_date)
+
+	def reset_depreciation_schedule(self, asset):
+		asset.flags.ignore_validate_update_after_submit = True
+
+		# recreate original depreciation schedule of the asset
+		asset.prepare_depreciation_data(date_of_return=self.posting_date)
+
+		self.modify_depreciation_schedule_for_asset_repairs(asset)
+		asset.save()
+
+	def modify_depreciation_schedule_for_asset_repairs(self, asset):
+		asset_repairs = frappe.get_all(
+			'Asset Repair',
+			filters={'asset': asset.name},
+			fields=['name', 'increase_in_asset_life']
+		)
+
+		for repair in asset_repairs:
+			if repair.increase_in_asset_life:
+				asset_repair = frappe.get_doc('Asset Repair', repair.name)
+				asset_repair.modify_depreciation_schedule()
+				asset.prepare_depreciation_data()
+
+	def reverse_depreciation_entry_made_after_disposal(self, asset):
+		from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry
+
+		posting_date_of_original_disposal = self.get_posting_date_of_disposal_entry()
+
+		row = -1
+		finance_book = asset.get('schedules')[0].get('finance_book')
+		for schedule in asset.get('schedules'):
+			if schedule.finance_book != finance_book:
+				row = 0
+				finance_book = schedule.finance_book
+			else:
+				row += 1
+
+			if schedule.schedule_date == posting_date_of_original_disposal:
+				if not self.disposal_was_made_on_original_schedule_date(asset, schedule, row, posting_date_of_original_disposal) \
+					or self.disposal_happens_in_the_future(posting_date_of_original_disposal):
+
+					reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry)
+					reverse_journal_entry.posting_date = nowdate()
+					frappe.flags.is_reverse_depr_entry = True
+					reverse_journal_entry.submit()
+
+					frappe.flags.is_reverse_depr_entry = False
+					asset.flags.ignore_validate_update_after_submit = True
+					schedule.journal_entry = None
+					asset.save()
+
+	def get_posting_date_of_disposal_entry(self):
+		if self.doctype == "Sales Invoice" and self.return_against:
+			return frappe.db.get_value('Sales Invoice', self.return_against, 'posting_date')
+		else:
+			return self.posting_date
+
+	# if the invoice had been posted on the date the depreciation was initially supposed to happen, the depreciation shouldn't be undone
+	def disposal_was_made_on_original_schedule_date(self, asset, schedule, row, posting_date_of_disposal):
+		for finance_book in asset.get('finance_books'):
+			if schedule.finance_book == finance_book.finance_book:
+				orginal_schedule_date = add_months(finance_book.depreciation_start_date,
+					row * cint(finance_book.frequency_of_depreciation))
+
+				if orginal_schedule_date == posting_date_of_disposal:
+					return True
+		return False
+
+	def disposal_happens_in_the_future(self, posting_date_of_disposal):
+		if posting_date_of_disposal > getdate():
+			return True
+
+		return False
+
 @frappe.whitelist()
 def get_tax_rate(account_head):
 	return frappe.db.get_value("Account", account_head, ["tax_rate", "account_name"], as_dict=True)