Merge branch 'develop' into enable-discount-accounting
diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py
index 2f86c6c..335e8a1 100644
--- a/erpnext/accounts/deferred_revenue.py
+++ b/erpnext/accounts/deferred_revenue.py
@@ -301,17 +301,21 @@
 	start_date = add_months(today(), -1)
 	end_date = add_days(today(), -1)
 
-	for record_type in ('Income', 'Expense'):
-		doc = frappe.get_doc(dict(
-			doctype='Process Deferred Accounting',
-			posting_date=posting_date,
-			start_date=start_date,
-			end_date=end_date,
-			type=record_type
-		))
+	companies = frappe.get_all('Company')
 
-		doc.insert()
-		doc.submit()
+	for company in companies:
+		for record_type in ('Income', 'Expense'):
+			doc = frappe.get_doc(dict(
+				doctype='Process Deferred Accounting',
+				company=company.name,
+				posting_date=posting_date,
+				start_date=start_date,
+				end_date=end_date,
+				type=record_type
+			))
+
+			doc.insert()
+			doc.submit()
 
 def make_gl_entries(doc, credit_account, debit_account, against,
 	amount, base_amount, posting_date, project, account_currency, cost_center, item, deferred_process=None):
diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
index 5f110e2..ffc9d1c 100644
--- a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
+++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.py
@@ -51,7 +51,7 @@
 			self.import_file, self.google_sheets_url
 		)
 
-		if 'Bank Account' not in json.dumps(preview):
+		if 'Bank Account' not in json.dumps(preview['columns']):
 			frappe.throw(_("Please add the Bank Account column"))
 
 		from frappe.core.page.background_jobs.background_jobs import get_info
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index ec93314..189260a 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -231,25 +231,25 @@
 			self.assertEqual(expected_values[gle.account][2], gle.credit)
 
 	def test_purchase_invoice_with_exchange_rate_difference(self):
-		pr = make_purchase_receipt(currency = "USD", conversion_rate = 70)
-		pi = make_purchase_invoice(currency = "USD", conversion_rate = 80, do_not_save = "True")
+		from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice as create_purchase_invoice
 
-		pi.items[0].purchase_receipt = pr.name
-		pi.items[0].pr_detail = pr.items[0].name
+		pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse='Stores - TCP1',
+			currency = "USD", conversion_rate = 70)
+
+		pi = create_purchase_invoice(pr.name)
+		pi.conversion_rate = 80
 
 		pi.insert()
 		pi.submit()		
 
-		# fetching the latest GL Entry with 'Exchange Gain/Loss - _TC' account
-		gl_entries = frappe.get_all('GL Entry', filters = {'account': 'Exchange Gain/Loss - _TC'})
-		voucher_no = frappe.get_value('GL Entry', gl_entries[0]['name'], 'voucher_no')	
+		# Get exchnage gain and loss account
+		exchange_gain_loss_account = frappe.db.get_value('Company', pi.company, 'exchange_gain_loss_account')
 
-		self.assertEqual(pi.name, voucher_no)
-
-		exchange_gain_loss_amount = frappe.get_value('GL Entry', gl_entries[0]['name'], 'debit')
+		# fetching the latest GL Entry with exchange gain and loss account account
+		amount = frappe.db.get_value('GL Entry', {'account': exchange_gain_loss_account, 'voucher_no': pi.name}, 'debit')
 		discrepancy_caused_by_exchange_rate_diff = abs(pi.items[0].base_net_amount - pr.items[0].base_net_amount)
 
-		self.assertEqual(exchange_gain_loss_amount, discrepancy_caused_by_exchange_rate_diff)
+		self.assertEqual(discrepancy_caused_by_exchange_rate_diff, amount)
 
 	def test_purchase_invoice_change_naming_series(self):
 		pi = frappe.copy_doc(test_records[1])
@@ -1031,21 +1031,21 @@
 		# Check GLE for Purchase Invoice
 		# Zero net effect on final TDS Payable on invoice
 		expected_gle = [
-			['_Test Account Cost for Goods Sold - _TC', 30000, 0],
-			['_Test Account Excise Duty - _TC', 0, 3000],
-			['Creditors - _TC', 0, 27000],
-			['TDS Payable - _TC', 3000, 3000]
+			['_Test Account Cost for Goods Sold - _TC', 30000],
+			['_Test Account Excise Duty - _TC', -3000],
+			['Creditors - _TC', -27000],
+			['TDS Payable - _TC', 0]
 		]
 
-		gl_entries = frappe.db.sql("""select account, debit, credit
+		gl_entries = frappe.db.sql("""select account, sum(debit - credit) as amount
 			from `tabGL Entry`
 			where voucher_type='Purchase Invoice' and voucher_no=%s
+			group by account
 			order by account asc""", (purchase_invoice.name), as_dict=1)
 
 		for i, gle in enumerate(gl_entries):
 			self.assertEqual(expected_gle[i][0], gle.account)
-			self.assertEqual(expected_gle[i][1], gle.debit)
-			self.assertEqual(expected_gle[i][2], gle.credit)
+			self.assertEqual(expected_gle[i][1], gle.amount)
 
 def update_tax_witholding_category(company, account, date):
 	from erpnext.accounts.utils import get_fiscal_year
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index e025fc6..b97dc40 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -542,6 +542,7 @@
 		select company, sum(debit_in_account_currency) - sum(credit_in_account_currency)
 		from `tabGL Entry`
 		where party_type = %s and party=%s
+		and is_cancelled = 0
 		group by company""", (party_type, party)))
 
 	for d in companies:
diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
index 9c9ada8..f1b231b 100644
--- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
+++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py
@@ -397,6 +397,7 @@
 				{'name': 'Budget', 'chartType': 'bar', 'values': budget_values},
 				{'name': 'Actual Expense', 'chartType': 'bar', 'values': actual_values}
 			]
-		}
+		},
+		'type' : 'bar'
 	}
 
diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
index 7793af7..56a67bb 100644
--- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
+++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py
@@ -380,7 +380,7 @@
 		gl_entries = frappe.db.sql("""select gl.posting_date, gl.account, gl.debit, gl.credit, gl.is_opening, gl.company,
 			gl.fiscal_year, gl.debit_in_account_currency, gl.credit_in_account_currency, gl.account_currency,
 			acc.account_name, acc.account_number
-			from `tabGL Entry` gl, `tabAccount` acc where acc.name = gl.account and gl.company = %(company)s
+			from `tabGL Entry` gl, `tabAccount` acc where acc.name = gl.account and gl.company = %(company)s and gl.is_cancelled = 0
 			{additional_conditions} and gl.posting_date <= %(to_date)s and acc.lft >= %(lft)s and acc.rgt <= %(rgt)s
 			order by gl.account, gl.posting_date""".format(additional_conditions=additional_conditions),
 			{
diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js
index 6f1bb28..922cc4a 100644
--- a/erpnext/assets/doctype/asset/asset.js
+++ b/erpnext/assets/doctype/asset/asset.js
@@ -82,24 +82,46 @@
 			if (in_list(["Submitted", "Partially Depreciated", "Fully Depreciated"], frm.doc.status)) {
 				frm.add_custom_button("Transfer Asset", function() {
 					erpnext.asset.transfer_asset(frm);
-				});
+				}, __("Manage"));
 
 				frm.add_custom_button("Scrap Asset", function() {
 					erpnext.asset.scrap_asset(frm);
-				});
+				}, __("Manage"));
 
 				frm.add_custom_button("Sell Asset", function() {
 					frm.trigger("make_sales_invoice");
-				});
+				}, __("Manage"));
 
 			} else if (frm.doc.status=='Scrapped') {
 				frm.add_custom_button("Restore Asset", function() {
 					erpnext.asset.restore_asset(frm);
-				});
+				}, __("Manage"));
+			}
+
+			if (frm.doc.maintenance_required && !frm.doc.maintenance_schedule) {
+				frm.add_custom_button(__("Maintain Asset"), function() {
+					frm.trigger("create_asset_maintenance");
+				}, __("Manage"));
+			}
+		
+			frm.add_custom_button(__("Repair Asset"), function() {
+				frm.trigger("create_asset_repair");
+			}, __("Manage"));
+			
+			if (frm.doc.status != 'Fully Depreciated') {
+				frm.add_custom_button(__("Adjust Asset Value"), function() {
+					frm.trigger("create_asset_adjustment");
+				}, __("Manage"));
+			}
+
+			if (!frm.doc.calculate_depreciation) {
+				frm.add_custom_button(__("Create Depreciation Entry"), function() {
+					frm.trigger("make_journal_entry");
+				}, __("Manage"));
 			}
 
 			if (frm.doc.purchase_receipt || !frm.doc.is_existing_asset) {
-				frm.add_custom_button("General Ledger", function() {
+				frm.add_custom_button("View General Ledger", function() {
 					frappe.route_options = {
 						"voucher_no": frm.doc.name,
 						"from_date": frm.doc.available_for_use_date,
@@ -107,27 +129,9 @@
 						"company": frm.doc.company
 					};
 					frappe.set_route("query-report", "General Ledger");
-				});
+				}, __("Manage"));
 			}
 
-			if (frm.doc.maintenance_required && !frm.doc.maintenance_schedule) {
-				frm.add_custom_button(__("Asset Maintenance"), function() {
-					frm.trigger("create_asset_maintenance");
-				}, __('Create'));
-			}
-			if (frm.doc.status != 'Fully Depreciated') {
-				frm.add_custom_button(__("Asset Value Adjustment"), function() {
-					frm.trigger("create_asset_adjustment");
-				}, __('Create'));
-			}
-
-			if (!frm.doc.calculate_depreciation) {
-				frm.add_custom_button(__("Depreciation Entry"), function() {
-					frm.trigger("make_journal_entry");
-				}, __('Create'));
-			}
-
-			frm.page.set_inner_btn_group_as_primary(__('Create'));
 			frm.trigger("setup_chart");
 		}
 
@@ -304,6 +308,20 @@
 		})
 	},
 
+	create_asset_repair: function(frm) {
+		frappe.call({
+			args: {
+				"asset": frm.doc.name,
+				"asset_name": frm.doc.asset_name
+			},
+			method: "erpnext.assets.doctype.asset.asset.create_asset_repair",
+			callback: function(r) {
+				var doclist = frappe.model.sync(r.message);
+				frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
+			}
+		});
+	},
+
 	create_asset_adjustment: function(frm) {
 		frappe.call({
 			args: {
diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json
index 421b9a6..de06075 100644
--- a/erpnext/assets/doctype/asset/asset.json
+++ b/erpnext/assets/doctype/asset/asset.json
@@ -502,7 +502,7 @@
    "link_fieldname": "asset"
   }
  ],
- "modified": "2021-01-22 12:38:59.091510",
+ "modified": "2021-06-24 14:58:51.097908",
  "modified_by": "Administrator",
  "module": "Assets",
  "name": "Asset",
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 8799275..66f0bdc 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -168,17 +168,24 @@
 				d.precision("rate_of_depreciation"))
 
 	def make_depreciation_schedule(self):
-		if 'Manual' not in [d.depreciation_method for d in self.finance_books]:
+		if 'Manual' not in [d.depreciation_method for d in self.finance_books] and not self.schedules:
 			self.schedules = []
 
-		if self.get("schedules") or not self.available_for_use_date:
+		if not self.available_for_use_date:
 			return
 
 		for d in self.get('finance_books'):
 			self.validate_asset_finance_books(d)
+			
+			start = self.clear_depreciation_schedule()
 
-			value_after_depreciation = (flt(self.gross_purchase_amount) -
-				flt(self.opening_accumulated_depreciation))
+			# value_after_depreciation - current Asset value
+			if d.value_after_depreciation:
+				value_after_depreciation = (flt(d.value_after_depreciation) -
+					flt(self.opening_accumulated_depreciation)) 
+			else:
+				value_after_depreciation = (flt(self.gross_purchase_amount) -
+					flt(self.opening_accumulated_depreciation)) 
 
 			d.value_after_depreciation = value_after_depreciation
 
@@ -191,7 +198,7 @@
 				number_of_pending_depreciations += 1
 
 			skip_row = False
-			for n in range(number_of_pending_depreciations):
+			for n in range(start, number_of_pending_depreciations):
 				# If depreciation is already completed (for double declining balance)
 				if skip_row: continue
 
@@ -216,11 +223,13 @@
 
 				# For last row
 				elif has_pro_rata and n == cint(number_of_pending_depreciations) - 1:
-					to_date = add_months(self.available_for_use_date,
-						n * cint(d.frequency_of_depreciation))
+					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 * cint(d.frequency_of_depreciation))
 
 					depreciation_amount, days, months = self.get_pro_rata_amt(d,
-						depreciation_amount, schedule_date, to_date)
+						depreciation_amount, schedule_date, self.to_date)
 
 					monthly_schedule_date = add_months(schedule_date, 1)
 
@@ -284,10 +293,23 @@
 							"finance_book_id": d.idx
 						})
 
+	# used when depreciation schedule needs to be modified due to increase in asset life
+	def clear_depreciation_schedule(self):
+		start = 0
+		for n in range(len(self.schedules)):
+			if not self.schedules[n].journal_entry:
+				del self.schedules[n:]
+				start = n
+				break
+		return start
+
+
+	# if it returns True, depreciation_amount will not be equal for the first and last rows
 	def check_is_pro_rata(self, row):
 		has_pro_rata = False
-
 		days = date_diff(row.depreciation_start_date, self.available_for_use_date) + 1
+
+		# if frequency_of_depreciation is 12 months, total_days = 365
 		total_days = get_total_days(row.depreciation_start_date, row.frequency_of_depreciation)
 
 		if days < total_days:
@@ -346,11 +368,12 @@
 			if 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(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:
 				book = self.get('finance_books')[cint(d.finance_book_id) - 1]
 				depreciation_amount += flt(value_after_depreciation -
@@ -626,8 +649,17 @@
 	return asset_maintenance
 
 @frappe.whitelist()
+def create_asset_repair(asset, asset_name):
+	asset_repair = frappe.new_doc("Asset Repair")
+	asset_repair.update({
+		"asset": asset,
+		"asset_name": asset_name
+	})
+	return asset_repair
+
+@frappe.whitelist()
 def create_asset_adjustment(asset, asset_category, company):
-	asset_maintenance = frappe.new_doc("Asset Value Adjustment")
+	asset_maintenance = frappe.get_doc("Asset Value Adjustment")
 	asset_maintenance.update({
 		"asset": asset,
 		"company": company,
@@ -757,8 +789,15 @@
 	depreciation_left = flt(row.total_number_of_depreciations) - flt(asset.number_of_depreciations_booked)
 
 	if row.depreciation_method in ("Straight Line", "Manual"):
-		depreciation_amount = (flt(row.value_after_depreciation) -
-			flt(row.expected_value_after_useful_life)) / depreciation_left
+		# if the Depreciation Schedule is being prepared for the first time
+		if not asset.flags.increase_in_asset_life:
+			depreciation_amount = (flt(row.value_after_depreciation) -
+				flt(row.expected_value_after_useful_life)) / depreciation_left
+
+		# 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))
 
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index 8845f24..59fbe3b 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -125,7 +125,6 @@
 			"frequency_of_depreciation": 12,
 			"depreciation_start_date": "2030-12-31"
 		})
-		asset.insert()
 		self.assertEqual(asset.status, "Draft")
 		asset.save()
 		expected_schedules = [
@@ -154,9 +153,8 @@
 			"frequency_of_depreciation": 12,
 			"depreciation_start_date": '2030-12-31'
 		})
-		asset.insert()
-		self.assertEqual(asset.status, "Draft")
 		asset.save()
+		self.assertEqual(asset.status, "Draft")
 
 		expected_schedules = [
 			['2030-12-31', 66667.00, 66667.00],
@@ -185,7 +183,7 @@
 			"frequency_of_depreciation": 12,
 			"depreciation_start_date": "2030-12-31"
 		})
-		asset.insert()
+		asset.save()
 		self.assertEqual(asset.status, "Draft")
 
 		expected_schedules = [
@@ -216,7 +214,6 @@
 			"depreciation_start_date": "2030-12-31"
 		})
 
-		asset.insert()
 		asset.save()
 
 		expected_schedules = [
@@ -247,7 +244,6 @@
 			"frequency_of_depreciation": 10,
 			"depreciation_start_date": "2020-12-31"
 		})
-		asset.insert()
 		asset.submit()
 		asset.load_from_db()
 		self.assertEqual(asset.status, "Submitted")
@@ -350,7 +346,6 @@
 			"frequency_of_depreciation": 10,
 			"depreciation_start_date": "2020-12-31"
 		})
-		asset.insert()
 		asset.submit()
 		post_depreciation_entries(date="2021-01-01")
 
@@ -380,7 +375,6 @@
 			"total_number_of_depreciations": 10,
 			"frequency_of_depreciation": 1
 		})
-		asset.insert()
 		asset.submit()
 
 		post_depreciation_entries(date=add_months('2020-01-01', 4))
@@ -424,7 +418,6 @@
 			"frequency_of_depreciation": 10,
 			"depreciation_start_date": "2020-12-31"
 		})
-		asset.insert()
 		asset.submit()
 		post_depreciation_entries(date="2021-01-01")
 
@@ -468,7 +461,7 @@
 			"total_number_of_depreciations": 3,
 			"frequency_of_depreciation": 10
 		})
-		asset.insert()
+		asset.save()
 		accumulated_depreciation_after_full_schedule = \
 			max(d.accumulated_depreciation_amount for d in asset.get("schedules"))
 
@@ -699,7 +692,7 @@
 		"item_code": args.item_code or "Macbook Pro",
 		"company": args.company or"_Test Company",
 		"purchase_date": "2015-01-01",
-		"calculate_depreciation": 0,
+		"calculate_depreciation": args.calculate_depreciation or 0,
 		"gross_purchase_amount": 100000,
 		"purchase_receipt_amount": 100000,
 		"expected_value_after_useful_life": 10000,
@@ -707,9 +700,16 @@
 		"available_for_use_date": "2020-06-06",
 		"location": "Test Location",
 		"asset_owner": "Company",
-		"is_existing_asset": args.is_existing_asset or 0
+		"is_existing_asset": 1
 	})
 
+	if asset.calculate_depreciation:
+		asset.append("finance_books", {
+			"depreciation_method": "Straight Line",
+			"frequency_of_depreciation": 12,
+			"total_number_of_depreciations": 5
+		})
+
 	try:
 		asset.save()
 	except frappe.DuplicateEntryError:
diff --git a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json
index d9b7b69..e5a5f19 100644
--- a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json
+++ b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json
@@ -67,7 +67,6 @@
   {
    "fieldname": "value_after_depreciation",
    "fieldtype": "Currency",
-   "hidden": 1,
    "label": "Value After Depreciation",
    "no_copy": 1,
    "options": "Company:company:default_currency",
@@ -85,7 +84,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-11-05 16:30:09.213479",
+ "modified": "2021-06-17 12:59:05.743683",
  "modified_by": "Administrator",
  "module": "Assets",
  "name": "Asset Finance Book",
diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.js b/erpnext/assets/doctype/asset_repair/asset_repair.js
index 4ba2b44..1cebfff 100644
--- a/erpnext/assets/doctype/asset_repair/asset_repair.js
+++ b/erpnext/assets/doctype/asset_repair/asset_repair.js
@@ -2,6 +2,45 @@
 // For license information, please see license.txt
 
 frappe.ui.form.on('Asset Repair', {
+	setup: function(frm) {
+		frm.fields_dict.cost_center.get_query = function(doc) {
+			return {
+				filters: {
+					'is_group': 0,
+					'company': doc.company
+				}
+			};
+		};
+
+		frm.fields_dict.project.get_query = function(doc) {
+			return {
+				filters: {
+					'company': doc.company
+				}
+			};
+		};
+
+		frm.fields_dict.warehouse.get_query = function(doc) {
+			return {
+				filters: {
+					'is_group': 0,
+					'company': doc.company
+				}
+			};
+		};
+	},
+
+	refresh: function(frm) {
+		if (frm.doc.docstatus) {
+			frm.add_custom_button("View General Ledger", function() {
+				frappe.route_options = {
+					"voucher_no": frm.doc.name
+				};
+				frappe.set_route("query-report", "General Ledger");
+			});
+		}
+	},
+
 	repair_status: (frm) => {
 		if (frm.doc.completion_date && frm.doc.repair_status == "Completed") {
 			frappe.call ({
@@ -17,5 +56,16 @@
 				}
 			});
 		}
+
+		if (frm.doc.repair_status == "Completed") {
+			frm.set_value('completion_date', frappe.datetime.now_datetime());
+		}				
 	}
 });
+
+frappe.ui.form.on('Asset Repair Consumed Item', {
+	consumed_quantity: function(frm, cdt, cdn) {
+		var row = locals[cdt][cdn];
+		frappe.model.set_value(cdt, cdn, 'total_value', row.consumed_quantity * row.valuation_rate);
+	},
+});
\ No newline at end of file
diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.json b/erpnext/assets/doctype/asset_repair/asset_repair.json
index d338fc0..ba31898 100644
--- a/erpnext/assets/doctype/asset_repair/asset_repair.json
+++ b/erpnext/assets/doctype/asset_repair/asset_repair.json
@@ -7,39 +7,44 @@
  "editable_grid": 1,
  "engine": "InnoDB",
  "field_order": [
-  "naming_series",
-  "asset_name",
+  "asset",
+  "company",
   "column_break_2",
-  "item_code",
-  "item_name",
+  "asset_name",
+  "naming_series",
   "section_break_5",
   "failure_date",
-  "assign_to",
-  "assign_to_name",
+  "repair_status",
   "column_break_6",
   "completion_date",
-  "repair_status",
+  "accounting_dimensions_section",
+  "cost_center",
+  "column_break_14",
+  "project",
+  "accounting_details",
   "repair_cost",
+  "capitalize_repair_cost",
+  "stock_consumption",
+  "column_break_8",
+  "purchase_invoice",
+  "stock_consumption_details_section",
+  "warehouse",
+  "stock_items",
+  "total_repair_cost",
+  "stock_entry",
+  "asset_depreciation_details_section",
+  "increase_in_asset_life",
   "section_break_9",
   "description",
   "column_break_9",
   "actions_performed",
-  "section_break_17",
+  "section_break_23",
   "downtime",
   "column_break_19",
   "amended_from"
  ],
  "fields": [
   {
-   "columns": 1,
-   "fieldname": "asset_name",
-   "fieldtype": "Link",
-   "in_list_view": 1,
-   "label": "Asset",
-   "options": "Asset",
-   "reqd": 1
-  },
-  {
    "fieldname": "naming_series",
    "fieldtype": "Select",
    "label": "Series",
@@ -51,18 +56,6 @@
    "fieldtype": "Column Break"
   },
   {
-   "fetch_from": "asset_name.item_code",
-   "fieldname": "item_code",
-   "fieldtype": "Read Only",
-   "label": "Item Code"
-  },
-  {
-   "fetch_from": "asset_name.item_name",
-   "fieldname": "item_name",
-   "fieldtype": "Read Only",
-   "label": "Item Name"
-  },
-  {
    "fieldname": "section_break_5",
    "fieldtype": "Section Break",
    "label": "Repair Details"
@@ -75,32 +68,19 @@
    "reqd": 1
   },
   {
-   "allow_on_submit": 1,
-   "fieldname": "assign_to",
-   "fieldtype": "Link",
-   "label": "Assign To",
-   "options": "User"
-  },
-  {
-   "allow_on_submit": 1,
-   "fetch_from": "assign_to.full_name",
-   "fieldname": "assign_to_name",
-   "fieldtype": "Read Only",
-   "label": "Assign To Name"
-  },
-  {
    "fieldname": "column_break_6",
    "fieldtype": "Column Break"
   },
   {
-   "allow_on_submit": 1,
+   "depends_on": "eval:!doc.__islocal",
    "fieldname": "completion_date",
    "fieldtype": "Datetime",
-   "label": "Completion Date"
+   "label": "Completion Date",
+   "no_copy": 1
   },
   {
-   "allow_on_submit": 1,
    "default": "Pending",
+   "depends_on": "eval:!doc.__islocal",
    "fieldname": "repair_status",
    "fieldtype": "Select",
    "label": "Repair Status",
@@ -116,25 +96,18 @@
   {
    "fieldname": "description",
    "fieldtype": "Long Text",
-   "label": "Error Description",
-   "reqd": 1
+   "label": "Error Description"
   },
   {
    "fieldname": "column_break_9",
    "fieldtype": "Column Break"
   },
   {
-   "allow_on_submit": 1,
    "fieldname": "actions_performed",
    "fieldtype": "Long Text",
    "label": "Actions performed"
   },
   {
-   "fieldname": "section_break_17",
-   "fieldtype": "Section Break"
-  },
-  {
-   "allow_on_submit": 1,
    "fieldname": "downtime",
    "fieldtype": "Data",
    "in_list_view": 1,
@@ -146,7 +119,7 @@
    "fieldtype": "Column Break"
   },
   {
-   "allow_on_submit": 1,
+   "default": "0",
    "fieldname": "repair_cost",
    "fieldtype": "Currency",
    "label": "Repair Cost"
@@ -159,12 +132,139 @@
    "options": "Asset Repair",
    "print_hide": 1,
    "read_only": 1
+  },
+  {
+   "columns": 1,
+   "fieldname": "asset",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Asset",
+   "options": "Asset",
+   "reqd": 1
+  },
+  {
+   "fetch_from": "asset.asset_name",
+   "fieldname": "asset_name",
+   "fieldtype": "Read Only",
+   "label": "Asset Name"
+  },
+  {
+   "fieldname": "column_break_8",
+   "fieldtype": "Column Break"
+  },
+  {
+   "default": "0",
+   "depends_on": "eval:!doc.__islocal",
+   "fieldname": "capitalize_repair_cost",
+   "fieldtype": "Check",
+   "label": "Capitalize Repair Cost"
+  },
+  {
+   "fieldname": "accounting_details",
+   "fieldtype": "Section Break",
+   "label": "Accounting Details"
+  },
+  {
+   "fieldname": "stock_items",
+   "fieldtype": "Table",
+   "label": "Stock Items",
+   "mandatory_depends_on": "stock_consumption",
+   "options": "Asset Repair Consumed Item"
+  },
+  {
+   "fieldname": "section_break_23",
+   "fieldtype": "Section Break"
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "accounting_dimensions_section",
+   "fieldtype": "Section Break",
+   "label": "Accounting Dimensions"
+  },
+  {
+   "fieldname": "cost_center",
+   "fieldtype": "Link",
+   "label": "Cost Center",
+   "options": "Cost Center"
+  },
+  {
+   "fieldname": "project",
+   "fieldtype": "Link",
+   "label": "Project",
+   "options": "Project"
+  },
+  {
+   "fieldname": "column_break_14",
+   "fieldtype": "Column Break"
+  },
+  {
+   "default": "0",
+   "depends_on": "eval:!doc.__islocal",
+   "fieldname": "stock_consumption",
+   "fieldtype": "Check",
+   "label": "Stock Consumed During Repair"
+  },
+  {
+   "depends_on": "stock_consumption",
+   "fieldname": "stock_consumption_details_section",
+   "fieldtype": "Section Break",
+   "label": "Stock Consumption Details"
+  },
+  {
+   "depends_on": "eval: doc.stock_consumption && doc.total_repair_cost > 0",
+   "description": "Sum of Repair Cost and Value of Consumed Stock Items.",
+   "fieldname": "total_repair_cost",
+   "fieldtype": "Currency",
+   "label": "Total Repair Cost",
+   "read_only": 1
+  },
+  {
+   "depends_on": "stock_consumption",
+   "fieldname": "warehouse",
+   "fieldtype": "Link",
+   "label": "Warehouse",
+   "options": "Warehouse"
+  },
+  {
+   "depends_on": "capitalize_repair_cost",
+   "fieldname": "asset_depreciation_details_section",
+   "fieldtype": "Section Break",
+   "label": "Asset Depreciation Details"
+  },
+  {
+   "fieldname": "increase_in_asset_life",
+   "fieldtype": "Int",
+   "label": "Increase In Asset Life(Months)",
+   "no_copy": 1
+  },
+  {
+   "depends_on": "eval:!doc.__islocal",
+   "fieldname": "purchase_invoice",
+   "fieldtype": "Link",
+   "label": "Purchase Invoice",
+   "mandatory_depends_on": "eval: doc.repair_status == 'Completed' && doc.repair_cost > 0",
+   "no_copy": 1,
+   "options": "Purchase Invoice"
+  },
+  {
+   "fetch_from": "asset.company",
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "label": "Company",
+   "options": "Company"
+  },
+  {
+   "fieldname": "stock_entry",
+   "fieldtype": "Link",
+   "label": "Stock Entry",
+   "options": "Stock Entry",
+   "read_only": 1
   }
  ],
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-01-22 15:08:12.495850",
+ "modified": "2021-06-25 13:14:38.307723",
  "modified_by": "Administrator",
  "module": "Assets",
  "name": "Asset Repair",
@@ -203,6 +303,7 @@
  ],
  "sort_field": "modified",
  "sort_order": "DESC",
+ "title_field": "asset_name",
  "track_changes": 1,
  "track_seen": 1
 }
\ No newline at end of file
diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py
index 049b931..d32fdf7 100644
--- a/erpnext/assets/doctype/asset_repair/asset_repair.py
+++ b/erpnext/assets/doctype/asset_repair/asset_repair.py
@@ -5,16 +5,252 @@
 from __future__ import unicode_literals
 import frappe
 from frappe import _
-from frappe.utils import time_diff_in_hours
-from frappe.model.document import Document
+from frappe.utils import time_diff_in_hours, getdate, add_months, flt, cint
+from erpnext.accounts.general_ledger import make_gl_entries
+from erpnext.assets.doctype.asset.asset import get_asset_account
+from erpnext.controllers.accounts_controller import AccountsController
 
-class AssetRepair(Document):
+class AssetRepair(AccountsController):
 	def validate(self):
-		if self.repair_status == "Completed" and not self.completion_date:
-			frappe.throw(_("Please select Completion Date for Completed Repair"))
+		self.asset_doc = frappe.get_doc('Asset', self.asset)
+		self.update_status()
 
+		if self.get('stock_items'):
+			self.set_total_value()
+		self.calculate_total_repair_cost()
+		
+	def update_status(self):
+		if self.repair_status == 'Pending':
+			frappe.db.set_value('Asset', self.asset, 'status', 'Out of Order')
+		else:
+			self.asset_doc.set_status()
+
+	def set_total_value(self):
+		for item in self.get('stock_items'):
+			item.total_value = flt(item.valuation_rate) * flt(item.consumed_quantity)
+
+	def calculate_total_repair_cost(self):
+		self.total_repair_cost = flt(self.repair_cost)
+
+		total_value_of_stock_consumed = self.get_total_value_of_stock_consumed()
+		self.total_repair_cost += total_value_of_stock_consumed
+
+	def before_submit(self):
+		self.check_repair_status()
+
+		if self.get('stock_consumption') or self.get('capitalize_repair_cost'):
+			self.increase_asset_value()
+		if self.get('stock_consumption'):
+			self.check_for_stock_items_and_warehouse()
+			self.decrease_stock_quantity()
+		if self.get('capitalize_repair_cost'):
+			self.make_gl_entries()
+			if frappe.db.get_value('Asset', self.asset, 'calculate_depreciation') and self.increase_in_asset_life:
+				self.modify_depreciation_schedule()
+
+		self.asset_doc.flags.ignore_validate_update_after_submit = True
+		self.asset_doc.prepare_depreciation_data()
+		self.asset_doc.save()
+
+	def before_cancel(self):
+		self.asset_doc = frappe.get_doc('Asset', self.asset)
+
+		if self.get('stock_consumption') or self.get('capitalize_repair_cost'):
+			self.decrease_asset_value()
+		if self.get('stock_consumption'):
+			self.increase_stock_quantity()
+		if self.get('capitalize_repair_cost'):
+			self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry')
+			self.make_gl_entries(cancel=True)
+			if frappe.db.get_value('Asset', self.asset, 'calculate_depreciation') and self.increase_in_asset_life:
+				self.revert_depreciation_schedule_on_cancellation()
+
+		self.asset_doc.flags.ignore_validate_update_after_submit = True
+		self.asset_doc.prepare_depreciation_data()
+		self.asset_doc.save()
+
+	def check_repair_status(self):
+		if self.repair_status == "Pending":
+			frappe.throw(_("Please update Repair Status."))
+
+	def check_for_stock_items_and_warehouse(self):
+		if not self.get('stock_items'):
+			frappe.throw(_("Please enter Stock Items consumed during the Repair."), title=_("Missing Items"))
+		if not self.warehouse:
+			frappe.throw(_("Please enter Warehouse from which Stock Items consumed during the Repair were taken."), title=_("Missing Warehouse"))
+
+	def increase_asset_value(self):
+		total_value_of_stock_consumed = self.get_total_value_of_stock_consumed()
+
+		if self.asset_doc.calculate_depreciation:
+			for row in self.asset_doc.finance_books:
+				row.value_after_depreciation += total_value_of_stock_consumed
+
+				if self.capitalize_repair_cost:
+					row.value_after_depreciation += self.repair_cost
+
+	def decrease_asset_value(self):
+		total_value_of_stock_consumed = self.get_total_value_of_stock_consumed()
+
+		if self.asset_doc.calculate_depreciation:
+			for row in self.asset_doc.finance_books:
+				row.value_after_depreciation -= total_value_of_stock_consumed
+
+				if self.capitalize_repair_cost:
+					row.value_after_depreciation -= self.repair_cost
+		
+	def get_total_value_of_stock_consumed(self):
+		total_value_of_stock_consumed = 0
+		if self.get('stock_consumption'):
+			for item in self.get('stock_items'):
+				total_value_of_stock_consumed += item.total_value
+
+		return total_value_of_stock_consumed
+
+	def decrease_stock_quantity(self):
+		stock_entry = frappe.get_doc({
+			"doctype": "Stock Entry",
+			"stock_entry_type": "Material Issue",
+			"company": self.company
+		})
+
+		for stock_item in self.get('stock_items'):
+			stock_entry.append('items', {
+				"s_warehouse": self.warehouse,
+				"item_code": stock_item.item,
+				"qty": stock_item.consumed_quantity,
+				"basic_rate": stock_item.valuation_rate
+			})
+
+		stock_entry.insert()
+		stock_entry.submit()
+
+		self.db_set('stock_entry', stock_entry.name)
+
+	def increase_stock_quantity(self):
+		stock_entry = frappe.get_doc('Stock Entry', self.stock_entry)
+		stock_entry.flags.ignore_links = True
+		stock_entry.cancel()
+
+	def make_gl_entries(self, cancel=False):
+		if flt(self.repair_cost) > 0:
+			gl_entries = self.get_gl_entries()
+			make_gl_entries(gl_entries, cancel)
+
+	def get_gl_entries(self):
+		gl_entries = []
+		repair_and_maintenance_account = frappe.db.get_value('Company', self.company, 'repair_and_maintenance_account')
+		fixed_asset_account = get_asset_account("fixed_asset_account", asset=self.asset, company=self.company)
+		expense_account = frappe.get_doc('Purchase Invoice', self.purchase_invoice).items[0].expense_account	
+
+		gl_entries.append(
+			self.get_gl_dict({
+				"account": expense_account,
+				"credit": self.repair_cost,
+				"credit_in_account_currency": self.repair_cost,
+				"against": repair_and_maintenance_account,
+				"voucher_type": self.doctype,		
+				"voucher_no": self.name,
+				"cost_center": self.cost_center,
+				"posting_date": getdate(),
+				"company": self.company
+			}, item=self)
+		)
+
+		if self.get('stock_consumption'):
+			# creating GL Entries for each row in Stock Items based on the Stock Entry created for it
+			stock_entry = frappe.get_doc('Stock Entry', self.stock_entry)
+			for item in stock_entry.items:
+				gl_entries.append(
+					self.get_gl_dict({
+						"account": item.expense_account,
+						"credit": item.amount,
+						"credit_in_account_currency": item.amount,
+						"against": repair_and_maintenance_account,
+						"voucher_type": self.doctype,		
+						"voucher_no": self.name,
+						"cost_center": self.cost_center,
+						"posting_date": getdate(),
+						"company": self.company
+					}, item=self)
+				)
+
+		gl_entries.append(
+			self.get_gl_dict({
+				"account": fixed_asset_account,
+				"debit": self.total_repair_cost,
+				"debit_in_account_currency": self.total_repair_cost,
+				"against": expense_account,
+				"voucher_type": self.doctype,
+				"voucher_no": self.name,
+				"cost_center": self.cost_center,
+				"posting_date": getdate(),
+				"against_voucher_type": "Purchase Invoice",
+				"against_voucher": self.purchase_invoice,
+				"company": self.company
+			}, item=self)
+		)
+
+		return gl_entries
+
+	def modify_depreciation_schedule(self):
+		for row in self.asset_doc.finance_books:
+			row.total_number_of_depreciations += self.increase_in_asset_life/row.frequency_of_depreciation
+
+			self.asset_doc.flags.increase_in_asset_life = False
+			extra_months = self.increase_in_asset_life % row.frequency_of_depreciation
+			if extra_months != 0:
+				self.calculate_last_schedule_date(self.asset_doc, row, extra_months)
+
+	# to help modify depreciation schedule when increase_in_asset_life is not a multiple of frequency_of_depreciation
+	def calculate_last_schedule_date(self, asset, row, extra_months):
+		asset.flags.increase_in_asset_life = True
+		number_of_pending_depreciations = cint(row.total_number_of_depreciations) - \
+			cint(asset.number_of_depreciations_booked)
+
+		# the Schedule Date in the final row of the old Depreciation Schedule
+		last_schedule_date = asset.schedules[len(asset.schedules)-1].schedule_date
+
+		# the Schedule Date in the final row of the new Depreciation Schedule
+		asset.to_date = add_months(last_schedule_date, extra_months)
+
+		# the latest possible date at which the depreciation can occur, without increasing the Total Number of Depreciations
+		# if depreciations happen yearly and the Depreciation Posting Date is 01-01-2020, this could be 01-01-2021, 01-01-2022...
+		schedule_date = add_months(row.depreciation_start_date,
+			number_of_pending_depreciations * cint(row.frequency_of_depreciation))
+
+		if asset.to_date > schedule_date:
+			row.total_number_of_depreciations += 1
+
+	def revert_depreciation_schedule_on_cancellation(self):
+		for row in self.asset_doc.finance_books:
+			row.total_number_of_depreciations -= self.increase_in_asset_life/row.frequency_of_depreciation
+
+			self.asset_doc.flags.increase_in_asset_life = False
+			extra_months = self.increase_in_asset_life % row.frequency_of_depreciation
+			if extra_months != 0:
+				self.calculate_last_schedule_date_before_modification(self.asset_doc, row, extra_months)
+
+	def calculate_last_schedule_date_before_modification(self, asset, row, extra_months):
+		asset.flags.increase_in_asset_life = True
+		number_of_pending_depreciations = cint(row.total_number_of_depreciations) - \
+			cint(asset.number_of_depreciations_booked)
+
+		# the Schedule Date in the final row of the modified Depreciation Schedule
+		last_schedule_date = asset.schedules[len(asset.schedules)-1].schedule_date
+
+		# the Schedule Date in the final row of the original Depreciation Schedule
+		asset.to_date = add_months(last_schedule_date, -extra_months)
+
+		# the latest possible date at which the depreciation can occur, without decreasing the Total Number of Depreciations
+		# if depreciations happen yearly and the Depreciation Posting Date is 01-01-2020, this could be 01-01-2021, 01-01-2022...
+		schedule_date = add_months(row.depreciation_start_date,
+			(number_of_pending_depreciations - 1) * cint(row.frequency_of_depreciation))
+
+		if asset.to_date < schedule_date:
+			row.total_number_of_depreciations -= 1
 
 @frappe.whitelist()
 def get_downtime(failure_date, completion_date):
 	downtime = time_diff_in_hours(completion_date, failure_date)
-	return round(downtime, 2)
\ No newline at end of file
+	return round(downtime, 2)
diff --git a/erpnext/assets/doctype/asset_repair/test_asset_repair.py b/erpnext/assets/doctype/asset_repair/test_asset_repair.py
index 3d325a9..30bbb37 100644
--- a/erpnext/assets/doctype/asset_repair/test_asset_repair.py
+++ b/erpnext/assets/doctype/asset_repair/test_asset_repair.py
@@ -2,8 +2,167 @@
 # Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
 # See license.txt
 from __future__ import unicode_literals
-
+import frappe
+from frappe.utils import nowdate, flt
 import unittest
+from erpnext.assets.doctype.asset.test_asset import create_asset_data, create_asset, set_depreciation_settings_in_company
 
 class TestAssetRepair(unittest.TestCase):
-	pass
+	def setUp(self):
+		set_depreciation_settings_in_company()
+		create_asset_data()
+		frappe.db.sql("delete from `tabTax Rule`")
+
+	def test_update_status(self):
+		asset = create_asset()
+		initial_status = asset.status
+		asset_repair = create_asset_repair(asset = asset)
+
+		if asset_repair.repair_status == "Pending":
+			asset.reload()
+			self.assertEqual(asset.status, "Out of Order")
+
+		asset_repair.repair_status = "Completed"
+		asset_repair.save()
+		asset_status = frappe.db.get_value("Asset", asset_repair.asset, "status")
+		self.assertEqual(asset_status, initial_status)
+
+	def test_stock_item_total_value(self):
+		asset_repair = create_asset_repair(stock_consumption = 1)
+
+		for item in asset_repair.stock_items:
+			total_value = flt(item.valuation_rate) * flt(item.consumed_quantity)
+			self.assertEqual(item.total_value, total_value)
+
+	def test_total_repair_cost(self):
+		asset_repair = create_asset_repair(stock_consumption = 1)
+
+		total_repair_cost = asset_repair.repair_cost
+		self.assertEqual(total_repair_cost, asset_repair.repair_cost)
+		for item in asset_repair.stock_items:
+			total_repair_cost += item.total_value
+			
+		self.assertEqual(total_repair_cost, asset_repair.total_repair_cost)
+
+	def test_repair_status_after_submit(self):
+		asset_repair = create_asset_repair(submit = 1)
+		self.assertNotEqual(asset_repair.repair_status, "Pending")
+
+	def test_stock_items(self):
+		asset_repair = create_asset_repair(stock_consumption = 1)
+		self.assertTrue(asset_repair.stock_consumption)
+		self.assertTrue(asset_repair.stock_items)
+
+	def test_warehouse(self):
+		asset_repair = create_asset_repair(stock_consumption = 1)
+		self.assertTrue(asset_repair.stock_consumption)
+		self.assertTrue(asset_repair.warehouse)
+
+	def test_decrease_stock_quantity(self):
+		asset_repair = create_asset_repair(stock_consumption = 1, submit = 1)
+		stock_entry = frappe.get_last_doc('Stock Entry')
+
+		self.assertEqual(stock_entry.stock_entry_type, "Material Issue")
+		self.assertEqual(stock_entry.items[0].s_warehouse, asset_repair.warehouse)
+		self.assertEqual(stock_entry.items[0].item_code, asset_repair.stock_items[0].item)
+		self.assertEqual(stock_entry.items[0].qty, asset_repair.stock_items[0].consumed_quantity)
+
+	def test_increase_in_asset_value_due_to_stock_consumption(self):
+		asset = create_asset(calculate_depreciation = 1)
+		initial_asset_value = get_asset_value(asset)
+		asset_repair = create_asset_repair(asset= asset, stock_consumption = 1, submit = 1)
+		asset.reload()
+
+		increase_in_asset_value = get_asset_value(asset) - initial_asset_value
+		self.assertEqual(asset_repair.stock_items[0].total_value, increase_in_asset_value)
+
+	def test_increase_in_asset_value_due_to_repair_cost_capitalisation(self):
+		asset = create_asset(calculate_depreciation = 1)
+		initial_asset_value = get_asset_value(asset)
+		asset_repair = create_asset_repair(asset= asset, capitalize_repair_cost = 1, submit = 1)
+		asset.reload()
+
+		increase_in_asset_value = get_asset_value(asset) - initial_asset_value
+		self.assertEqual(asset_repair.repair_cost, increase_in_asset_value)
+
+	def test_purchase_invoice(self):
+		asset_repair = create_asset_repair(capitalize_repair_cost = 1, submit = 1)
+		self.assertTrue(asset_repair.purchase_invoice)
+
+	def test_gl_entries(self):
+		asset_repair = create_asset_repair(capitalize_repair_cost = 1, submit = 1)
+		gl_entry = frappe.get_last_doc('GL Entry')
+		self.assertEqual(asset_repair.name, gl_entry.voucher_no)
+
+	def test_increase_in_asset_life(self):
+		asset = create_asset(calculate_depreciation = 1)
+		initial_num_of_depreciations = num_of_depreciations(asset)
+		create_asset_repair(asset= asset, capitalize_repair_cost = 1, submit = 1)
+		asset.reload()
+	
+		self.assertEqual((initial_num_of_depreciations + 1), num_of_depreciations(asset))
+		self.assertEqual(asset.schedules[-1].accumulated_depreciation_amount, asset.finance_books[0].value_after_depreciation)
+
+def get_asset_value(asset):
+	return asset.finance_books[0].value_after_depreciation
+
+def num_of_depreciations(asset):
+	return asset.finance_books[0].total_number_of_depreciations
+
+def create_asset_repair(**args):
+	from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
+	from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
+
+	args = frappe._dict(args)
+
+	if args.asset:
+		asset = args.asset
+	else:
+		asset = create_asset(is_existing_asset = 1)
+	asset_repair = frappe.new_doc("Asset Repair")
+	asset_repair.update({
+		"asset": asset.name,
+		"asset_name": asset.asset_name,
+		"failure_date": nowdate(),
+		"description": "Test Description",
+		"repair_cost": 0,
+		"company": asset.company
+	})
+
+	if args.stock_consumption:
+		asset_repair.stock_consumption = 1
+		asset_repair.warehouse = create_warehouse("Test Warehouse", company = asset.company)
+		asset_repair.append("stock_items", {
+			"item": args.item or args.item_code or "_Test Item",
+			"valuation_rate": args.rate if args.get("rate") is not None else 100,
+			"consumed_quantity": args.qty or 1
+		})
+
+	asset_repair.insert(ignore_if_duplicate=True)
+	
+	if args.submit:
+		asset_repair.repair_status = "Completed"
+		asset_repair.cost_center = "_Test Cost Center - _TC"
+
+		if args.stock_consumption:
+			stock_entry = frappe.get_doc({
+				"doctype": "Stock Entry",
+				"stock_entry_type": "Material Receipt",
+				"company": asset.company
+			})
+			stock_entry.append('items', {
+				"t_warehouse": asset_repair.warehouse,
+				"item_code": asset_repair.stock_items[0].item,
+				"qty": asset_repair.stock_items[0].consumed_quantity
+			})
+			stock_entry.submit()
+
+		if args.capitalize_repair_cost:
+			asset_repair.capitalize_repair_cost = 1
+			asset_repair.repair_cost = 1000
+			if asset.calculate_depreciation:
+				asset_repair.increase_in_asset_life = 12
+			asset_repair.purchase_invoice = make_purchase_invoice().name
+
+		asset_repair.submit()
+	return asset_repair
\ No newline at end of file
diff --git a/erpnext/assets/doctype/asset_repair_consumed_item/__init__.py b/erpnext/assets/doctype/asset_repair_consumed_item/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/assets/doctype/asset_repair_consumed_item/__init__.py
diff --git a/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.json b/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.json
new file mode 100644
index 0000000..528f0ec
--- /dev/null
+++ b/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.json
@@ -0,0 +1,55 @@
+{
+ "actions": [],
+ "creation": "2021-05-12 02:41:54.161024",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "item",
+  "valuation_rate",
+  "consumed_quantity",
+  "total_value"
+ ],
+ "fields": [
+  {
+   "fieldname": "item",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Item",
+   "options": "Item"
+  },
+  {
+   "fetch_from": "item.valuation_rate",
+   "fieldname": "valuation_rate",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Valuation Rate",
+   "read_only": 1
+  },
+  {
+   "fieldname": "consumed_quantity",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Consumed Quantity"
+  },
+  {
+   "fieldname": "total_value",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Total Value",
+   "read_only": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-05-12 03:19:55.006300",
+ "modified_by": "Administrator",
+ "module": "Assets",
+ "name": "Asset Repair Consumed 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_repair_consumed_item/asset_repair_consumed_item.py b/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.py
new file mode 100644
index 0000000..fa22a57
--- /dev/null
+++ b/erpnext/assets/doctype/asset_repair_consumed_item/asset_repair_consumed_item.py
@@ -0,0 +1,8 @@
+# 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 AssetRepairConsumedItem(Document):
+	pass
diff --git a/erpnext/hr/doctype/training_event/training_event.js b/erpnext/hr/doctype/training_event/training_event.js
index b7d34b1..064dfb2 100644
--- a/erpnext/hr/doctype/training_event/training_event.js
+++ b/erpnext/hr/doctype/training_event/training_event.js
@@ -20,11 +20,10 @@
 				frappe.set_route("List", "Training Feedback");
 			});
 		}
-	}
-});
+		frm.events.set_employee_query(frm);
+	},
 
-frappe.ui.form.on("Training Event Employee", {
-	employee: function (frm) {
+	set_employee_query: function(frm) {
 		let emp = [];
 		for (let d in frm.doc.employees) {
 			if (frm.doc.employees[d].employee) {
@@ -40,3 +39,10 @@
 		});
 	}
 });
+
+frappe.ui.form.on("Training Event Employee", {
+	employee: function(frm) {
+		frm.events.set_employee_query(frm);
+	}
+});
+
diff --git a/erpnext/hr/doctype/training_event_employee/training_event_employee.json b/erpnext/hr/doctype/training_event_employee/training_event_employee.json
index 2d313e9..bcb7d5e 100644
--- a/erpnext/hr/doctype/training_event_employee/training_event_employee.json
+++ b/erpnext/hr/doctype/training_event_employee/training_event_employee.json
@@ -19,6 +19,7 @@
    "fieldtype": "Link",
    "in_list_view": 1,
    "label": "Employee",
+   "no_copy": 1,
    "options": "Employee"
   },
   {
@@ -68,7 +69,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2021-05-21 12:41:59.336237",
+ "modified": "2021-07-02 17:20:27.630176",
  "modified_by": "Administrator",
  "module": "HR",
  "name": "Training Event Employee",
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index c58f017..3bd1fe6 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -1100,6 +1100,8 @@
 		},
 		'BOM Item': {
 			'doctype': 'BOM Item',
+			# stop get_mapped_doc copying parent bom_no to children
+			'field_no_map': ['bom_no'],
 			'condition': lambda doc: doc.has_variants == 0
 		},
 	}, target_doc, postprocess)
diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py
index 57a5458..c89f7d6 100644
--- a/erpnext/manufacturing/doctype/bom/test_bom.py
+++ b/erpnext/manufacturing/doctype/bom/test_bom.py
@@ -8,6 +8,7 @@
 from frappe.utils import cstr, flt
 from frappe.test_runner import make_test_records
 from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
+from erpnext.manufacturing.doctype.bom.bom import make_variant_bom
 from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import update_cost
 from erpnext.stock.doctype.item.test_item import make_item
 from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
@@ -248,6 +249,37 @@
 		for reqd_item, created_item in zip(reqd_order, created_order):
 			self.assertEqual(reqd_item, created_item.item_code)
 
+	def test_generated_variant_bom(self):
+		from erpnext.controllers.item_variant import create_variant
+
+		template_item = make_item(
+			"_TestTemplateItem", {"has_variants": 1, "attributes": [{"attribute": "Test Size"},]}
+		)
+		variant = create_variant(template_item.item_code, {"Test Size": "Large"})
+		variant.insert(ignore_if_duplicate=True)
+
+		bom_tree = {
+			template_item.item_code: {
+				"SubAssembly1": {"ChildPart1": {}, "ChildPart2": {},},
+				"ChildPart5": {},
+			}
+		}
+		template_bom = create_nested_bom(bom_tree, prefix="")
+		variant_bom = make_variant_bom(
+			template_bom.name, template_bom.name, variant.item_code, variant_items=[]
+		)
+		variant_bom.save()
+
+		reqd_order = template_bom.get_tree_representation().level_order_traversal()
+		created_order = variant_bom.get_tree_representation().level_order_traversal()
+
+		self.assertEqual(len(reqd_order), len(created_order))
+
+		for reqd_item, created_item in zip(reqd_order, created_order):
+			self.assertEqual(reqd_item.item_code, created_item.item_code)
+			self.assertEqual(reqd_item.qty, created_item.qty)
+			self.assertEqual(reqd_item.exploded_qty, created_item.exploded_qty)
+
 
 def get_default_bom(item_code="_Test FG Item 2"):
 	return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1})
diff --git a/erpnext/regional/doctype/gst_settings/gst_settings.js b/erpnext/regional/doctype/gst_settings/gst_settings.js
index 808f9bc..cd682c5 100644
--- a/erpnext/regional/doctype/gst_settings/gst_settings.js
+++ b/erpnext/regional/doctype/gst_settings/gst_settings.js
@@ -35,6 +35,7 @@
 			return {
 				filters: {
 					company: row.company,
+					account_type: "Tax",
 					is_group: 0
 				}
 			};
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index a4466e7..81c0918 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -834,8 +834,16 @@
 	depreciation_left = flt(row.total_number_of_depreciations) - flt(asset.number_of_depreciations_booked)
 
 	if row.depreciation_method in ("Straight Line", "Manual"):
-		depreciation_amount = (flt(row.value_after_depreciation) -
-			flt(row.expected_value_after_useful_life)) / depreciation_left
+		# if the Depreciation Schedule is being prepared for the first time
+		if not asset.flags.increase_in_asset_life:
+			depreciation_amount = (flt(row.value_after_depreciation) -
+				flt(row.expected_value_after_useful_life)) / depreciation_left
+
+		# 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:
 		rate_of_depreciation = row.rate_of_depreciation
 		# if its the first depreciation
diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json
index 061986d..e6ec496 100644
--- a/erpnext/setup/doctype/company/company.json
+++ b/erpnext/setup/doctype/company/company.json
@@ -74,7 +74,7 @@
   "stock_received_but_not_billed",
   "service_received_but_not_billed",
   "expenses_included_in_valuation",
-  "fixed_asset_depreciation_settings",
+  "fixed_asset_defaults",
   "accumulated_depreciation_account",
   "depreciation_expense_account",
   "series_for_depreciation_entry",
@@ -83,6 +83,7 @@
   "disposal_account",
   "depreciation_cost_center",
   "capital_work_in_progress_account",
+  "repair_and_maintenance_account",
   "asset_received_but_not_billed",
   "budget_detail",
   "exception_budget_approver_role",
@@ -520,12 +521,6 @@
    "options": "Account"
   },
   {
-   "collapsible": 1,
-   "fieldname": "fixed_asset_depreciation_settings",
-   "fieldtype": "Section Break",
-   "label": "Fixed Asset Depreciation Settings"
-  },
-  {
    "fieldname": "accumulated_depreciation_account",
    "fieldtype": "Link",
    "label": "Accumulated Depreciation Account",
@@ -734,6 +729,18 @@
    "fieldtype": "Link",
    "label": "Default Payment Discount Account",
    "options": "Account"
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "fixed_asset_defaults",
+   "fieldtype": "Section Break",
+   "label": "Fixed Asset Defaults"
+  },
+  {
+   "fieldname": "repair_and_maintenance_account",
+   "fieldtype": "Link",
+   "label": "Repair and Maintenance Account",
+   "options": "Account"
   }
  ],
  "icon": "fa fa-building",
@@ -741,7 +748,7 @@
  "image_field": "company_logo",
  "is_tree": 1,
  "links": [],
- "modified": "2021-05-07 03:11:28.189740",
+ "modified": "2021-05-12 16:51:08.187233",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "Company",
diff --git a/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json b/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json
index 014f409..6ca3d63 100644
--- a/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json
+++ b/erpnext/setup/workspace/erpnext_settings/erpnext_settings.json
@@ -11,10 +11,11 @@
  "hide_custom": 0,
  "icon": "settings",
  "idx": 0,
+ "is_default": 0,
  "is_standard": 1,
  "label": "ERPNext Settings",
  "links": [],
- "modified": "2020-12-01 13:38:37.759596",
+ "modified": "2021-06-12 01:58:11.399566",
  "modified_by": "Administrator",
  "module": "Setup",
  "name": "ERPNext Settings",
@@ -109,6 +110,13 @@
    "label": "Domain Settings",
    "link_to": "Domain Settings",
    "type": "DocType"
+  },
+  {
+   "doc_view": "",
+   "icon": "retail",
+   "label": "Products Settings",
+   "link_to": "Products Settings",
+   "type": "DocType"
   }
  ]
-}
\ No newline at end of file
+}
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 5ba9c70..41800e3 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -291,7 +291,7 @@
 							continue
 
 					self.add_gl_entry(gl_entries, warehouse_account_name, d.cost_center, stock_value_diff, 0.0, remarks,
-						stock_rbnb, account_currency=warehouse_account_currency, item=d)					
+						stock_rbnb, account_currency=warehouse_account_currency, item=d)
 
 					# GL Entry for from warehouse or Stock Received but not billed
 					# Intentionally passed negative debit amount to avoid incorrect GL Entry validation
@@ -318,11 +318,11 @@
 									(exchange_rate_map[d.purchase_invoice] - self.conversion_rate)
 
 								self.add_gl_entry(gl_entries, account, d.cost_center, 0.0, discrepancy_caused_by_exchange_rate_difference,
-									remarks, self.supplier, debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference, 
+									remarks, self.supplier, debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference,
 									account_currency=credit_currency, item=d)
 
-								self.add_gl_entry(gl_entries, self.get_company_default("exchange_gain_loss_account"), d.cost_center, discrepancy_caused_by_exchange_rate_difference, 0.0, 
-									remarks, self.supplier, debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference, 
+								self.add_gl_entry(gl_entries, self.get_company_default("exchange_gain_loss_account"), d.cost_center, discrepancy_caused_by_exchange_rate_difference, 0.0,
+									remarks, self.supplier, debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference,
 									account_currency=credit_currency, item=d)
 
 					# Amount added through landed-cos-voucher
@@ -407,6 +407,7 @@
 			against_account = ", ".join([d.account for d in gl_entries if flt(d.debit) > 0])
 			total_valuation_amount = sum(valuation_tax.values())
 			amount_including_divisional_loss = negative_expense_to_be_booked
+			stock_rbnb = self.get_company_default("stock_received_but_not_billed")
 			i = 1
 			for tax in self.get("taxes"):
 				if valuation_tax.get(tax.name):
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index d56822a..dbba21f 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -1054,30 +1054,30 @@
 
 	def test_purchase_receipt_with_exchange_rate_difference(self):
 		from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice as create_purchase_invoice
-
-		pi = create_purchase_invoice(currency = "USD", conversion_rate = 70)
-
-		create_warehouse("_Test Warehouse for Valuation", company="_Test Company with perpetual inventory",
-			properties={"account": '_Test Account Stock In Hand - TCP1'})
+		from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import make_purchase_receipt as create_purchase_receipt
 		
-		pr = make_purchase_receipt(warehouse = '_Test Warehouse for Valuation - TCP1', 
-			company="_Test Company with perpetual inventory", currency = "USD", conversion_rate = 80, 
-			do_not_save = "True")
-
+		pi = create_purchase_invoice(company="_Test Company with perpetual inventory",
+			cost_center = "Main - TCP1",
+			warehouse = "Stores - TCP1",
+			expense_account ="_Test Account Cost for Goods Sold - TCP1",
+			currency = "USD", conversion_rate = 70)
+		
+		pr = create_purchase_receipt(pi.name)
+		pr.conversion_rate = 80
 		pr.items[0].purchase_invoice = pi.name
 		pr.items[0].purchase_invoice_item = pi.items[0].name
 
-		pr.insert()
+		pr.save()
 		pr.submit()
 
-		# fetching the latest GL Entry with 'Exchange Gain/Loss - TCP1' account
-		gl_entries = frappe.get_all('GL Entry', filters = {'account': 'Exchange Gain/Loss - TCP1'})
-		voucher_no = frappe.get_value('GL Entry', gl_entries[0]['name'], 'voucher_no')
-		self.assertEqual(pr.name, voucher_no)
+		# Get exchnage gain and loss account
+		exchange_gain_loss_account = frappe.db.get_value('Company', pr.company, 'exchange_gain_loss_account')
 
-		exchange_gain_loss_amount = frappe.get_value('GL Entry', gl_entries[0]['name'], 'debit')
+		# fetching the latest GL Entry with exchange gain and loss account account
+		amount = frappe.db.get_value('GL Entry', {'account': exchange_gain_loss_account, 'voucher_no': pr.name}, 'credit')
 		discrepancy_caused_by_exchange_rate_diff = abs(pi.items[0].base_net_amount - pr.items[0].base_net_amount)
-		self.assertEqual(exchange_gain_loss_amount, discrepancy_caused_by_exchange_rate_diff)
+
+		self.assertEqual(discrepancy_caused_by_exchange_rate_diff, amount)
 
 def get_sl_entries(voucher_type, voucher_no):
 	return frappe.db.sql(""" select actual_qty, warehouse, stock_value_difference
diff --git a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
index ba31ad7..af2ada8 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
@@ -54,7 +54,7 @@
 		)
 
 		# _Test Item for Reposting transferred from Stores to FG warehouse on 30-04-2020
-		make_stock_entry(
+		se = make_stock_entry(
 			item_code="_Test Item for Reposting",
 			source="Stores - _TC",
 			target="Finished Goods - _TC",
@@ -64,29 +64,29 @@
 			posting_date='2020-04-30',
 			posting_time='14:00'
 		)
-		target_wh_sle = get_previous_sle({
+		target_wh_sle = frappe.db.get_value('Stock Ledger Entry', {
 			"item_code": "_Test Item for Reposting",
 			"warehouse": "Finished Goods - _TC",
-			"posting_date": '2020-04-30',
-			"posting_time": '14:00'
-		})
+			"voucher_type": "Stock Entry",
+			"voucher_no": se.name
+		}, ["valuation_rate"], as_dict=1)
 
 		self.assertEqual(target_wh_sle.get("valuation_rate"), 150)
 
 		# Repack entry on 5-5-2020
 		repack = create_repack_entry(company=company, posting_date='2020-05-05', posting_time='14:00')
 
-		finished_item_sle = get_previous_sle({
+		finished_item_sle = frappe.db.get_value('Stock Ledger Entry', {
 			"item_code": "_Test Finished Item for Reposting",
 			"warehouse": "Finished Goods - _TC",
-			"posting_date": '2020-05-05',
-			"posting_time": '14:00'
-		})
+			"voucher_type": "Stock Entry",
+			"voucher_no": repack.name
+		}, ["incoming_rate", "valuation_rate"], as_dict=1)
 		self.assertEqual(finished_item_sle.get("incoming_rate"), 540)
 		self.assertEqual(finished_item_sle.get("valuation_rate"), 540)
 
 		# Reconciliation for _Test Item for Reposting at Stores on 12-04-2020: Qty = 50, Rate = 150
-		create_stock_reconciliation(
+		sr = create_stock_reconciliation(
 			item_code="_Test Item for Reposting",
 			warehouse="Stores - _TC",
 			qty=50,
@@ -109,12 +109,12 @@
 		self.assertEqual(target_wh_sle.get("valuation_rate"), 175)
 
 		# Check valuation rate of repacked item after back-dated entry at Stores
-		finished_item_sle = get_previous_sle({
+		finished_item_sle = frappe.db.get_value('Stock Ledger Entry', {
 			"item_code": "_Test Finished Item for Reposting",
 			"warehouse": "Finished Goods - _TC",
-			"posting_date": '2020-05-05',
-			"posting_time": '14:00'
-		})
+			"voucher_type": "Stock Entry",
+			"voucher_no": repack.name
+		}, ["incoming_rate", "valuation_rate"], as_dict=1)
 		self.assertEqual(finished_item_sle.get("incoming_rate"), 790)
 		self.assertEqual(finished_item_sle.get("valuation_rate"), 790)
 
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 2956384..3e15d54 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -357,6 +357,7 @@
 			if row.current_qty:
 				data.actual_qty = -1 * row.current_qty
 				data.qty_after_transaction = flt(row.current_qty)
+				data.previous_qty_after_transaction = flt(row.qty)
 				data.valuation_rate = flt(row.current_valuation_rate)
 				data.stock_value = data.qty_after_transaction * data.valuation_rate
 				data.stock_value_difference = -1 * flt(row.amount_difference)
diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
index 36380b8..7b98c7b 100644
--- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
@@ -6,7 +6,7 @@
 
 from __future__ import unicode_literals
 import frappe, unittest
-from frappe.utils import flt, nowdate, nowtime
+from frappe.utils import flt, nowdate, nowtime, add_days
 from erpnext.accounts.utils import get_stock_and_account_balance
 from erpnext.stock.stock_ledger import get_previous_sle, update_entries_after
 from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import EmptyStockReconciliationItemsError, get_items
@@ -14,6 +14,7 @@
 from erpnext.stock.doctype.item.test_item import create_item
 from erpnext.stock.utils import get_incoming_rate, get_stock_value_on, get_valuation_method
 from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
 
 class TestStockReconciliation(unittest.TestCase):
 	@classmethod
@@ -204,6 +205,117 @@
 		self.assertEqual(sr.get("items")[0].valuation_rate, 0)
 		self.assertEqual(sr.get("items")[0].amount, 0)
 
+	def test_backdated_stock_reco_qty_reposting(self):
+		"""
+			Test if a backdated stock reco recalculates future qty until next reco.
+			-------------------------------------------
+			Var		| Doc	|	Qty	| Balance
+			-------------------------------------------
+			SR5		| Reco	|	0	|	8	(posting date: today-4) [backdated]
+			PR1		| PR	|	10	|	18	(posting date: today-3)
+			PR2		| PR	|	1	|	19	(posting date: today-2)
+			SR4		| Reco	|	0	|	6	(posting date: today-1) [backdated]
+			PR3		| PR	|	1	|	7	(posting date: today) # can't post future PR
+		"""
+		item_code = "Backdated-Reco-Item"
+		warehouse = "_Test Warehouse - _TC"
+		create_item(item_code)
+
+		pr1 = make_purchase_receipt(item_code=item_code, warehouse=warehouse, qty=10, rate=100,
+			posting_date=add_days(nowdate(), -3))
+		pr2 = make_purchase_receipt(item_code=item_code, warehouse=warehouse, qty=1, rate=100,
+			posting_date=add_days(nowdate(), -2))
+		pr3 = make_purchase_receipt(item_code=item_code, warehouse=warehouse, qty=1, rate=100,
+			posting_date=nowdate())
+
+		pr1_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": pr1.name, "is_cancelled": 0},
+			"qty_after_transaction")
+		pr3_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": pr3.name, "is_cancelled": 0},
+			"qty_after_transaction")
+		self.assertEqual(pr1_balance, 10)
+		self.assertEqual(pr3_balance, 12)
+
+		# post backdated stock reco in between
+		sr4 = create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=6, rate=100,
+			posting_date=add_days(nowdate(), -1))
+		pr3_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": pr3.name, "is_cancelled": 0},
+			"qty_after_transaction")
+		self.assertEqual(pr3_balance, 7)
+
+		# post backdated stock reco at the start
+		sr5 = create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=8, rate=100,
+			posting_date=add_days(nowdate(), -4))
+		pr1_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": pr1.name, "is_cancelled": 0},
+			"qty_after_transaction")
+		pr2_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": pr2.name, "is_cancelled": 0},
+			"qty_after_transaction")
+		sr4_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": sr4.name, "is_cancelled": 0},
+			"qty_after_transaction")
+		self.assertEqual(pr1_balance, 18)
+		self.assertEqual(pr2_balance, 19)
+		self.assertEqual(sr4_balance, 6) # check if future stock reco is unaffected
+
+		# cancel backdated stock reco and check future impact
+		sr5.cancel()
+		pr1_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": pr1.name, "is_cancelled": 0},
+			"qty_after_transaction")
+		pr2_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": pr2.name, "is_cancelled": 0},
+			"qty_after_transaction")
+		sr4_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": sr4.name, "is_cancelled": 0},
+			"qty_after_transaction")
+		self.assertEqual(pr1_balance, 10)
+		self.assertEqual(pr2_balance, 11)
+		self.assertEqual(sr4_balance, 6) # check if future stock reco is unaffected
+
+		# teardown
+		sr4.cancel()
+		pr3.cancel()
+		pr2.cancel()
+		pr1.cancel()
+
+	def test_backdated_stock_reco_future_negative_stock(self):
+		"""
+			Test if a backdated stock reco causes future negative stock and is blocked.
+			-------------------------------------------
+			Var		| Doc	|	Qty	| Balance
+			-------------------------------------------
+			PR1		| PR	|	10	|	10		(posting date: today-2)
+			SR3		| Reco	|	0	|	1		(posting date: today-1) [backdated & blocked]
+			DN2		| DN	|	-2	|	8(-1)	(posting date: today)
+		"""
+		from erpnext.stock.stock_ledger import NegativeStockError
+		from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
+
+		item_code = "Backdated-Reco-Item"
+		warehouse = "_Test Warehouse - _TC"
+		create_item(item_code)
+
+		negative_stock_setting = frappe.db.get_single_value("Stock Settings", "allow_negative_stock")
+		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 0)
+
+		pr1 = make_purchase_receipt(item_code=item_code, warehouse=warehouse, qty=10, rate=100,
+			posting_date=add_days(nowdate(), -2))
+		dn2 = create_delivery_note(item_code=item_code, warehouse=warehouse, qty=2, rate=120,
+			posting_date=nowdate())
+
+		pr1_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": pr1.name, "is_cancelled": 0},
+			"qty_after_transaction")
+		dn2_balance = frappe.db.get_value("Stock Ledger Entry", {"voucher_no": dn2.name, "is_cancelled": 0},
+			"qty_after_transaction")
+		self.assertEqual(pr1_balance, 10)
+		self.assertEqual(dn2_balance, 8)
+
+		# check if stock reco is blocked
+		sr3 = create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=1, rate=100,
+			posting_date=add_days(nowdate(), -1), do_not_submit=True)
+		self.assertRaises(NegativeStockError, sr3.submit)
+
+		# teardown
+		frappe.db.set_value("Stock Settings", None, "allow_negative_stock", negative_stock_setting)
+		sr3.cancel()
+		dn2.cancel()
+		pr1.cancel()
+
 def insert_existing_sle(warehouse):
 	from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
 
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 9fe89c3..4e9c768 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -55,6 +55,11 @@
 				sle_doc = make_entry(sle, allow_negative_stock, via_landed_cost_voucher)
 
 			args = sle_doc.as_dict()
+
+			if sle.get("voucher_type") == "Stock Reconciliation":
+				# preserve previous_qty_after_transaction for qty reposting
+				args.previous_qty_after_transaction = sle.get("previous_qty_after_transaction")
+
 			update_bin(args, allow_negative_stock, via_landed_cost_voucher)
 
 def get_args_for_future_sle(row):
@@ -215,7 +220,7 @@
 		"""
 		self.data.setdefault(args.warehouse, frappe._dict())
 		warehouse_dict = self.data[args.warehouse]
-		previous_sle = self.get_previous_sle_of_current_voucher(args)
+		previous_sle = get_previous_sle_of_current_voucher(args)
 		warehouse_dict.previous_sle = previous_sle
 
 		for key in ("qty_after_transaction", "valuation_rate", "stock_value"):
@@ -227,29 +232,6 @@
 			"stock_value_difference": 0.0
 		})
 
-	def get_previous_sle_of_current_voucher(self, args):
-		"""get stock ledger entries filtered by specific posting datetime conditions"""
-
-		args['time_format'] = '%H:%i:%s'
-		if not args.get("posting_date"):
-			args["posting_date"] = "1900-01-01"
-		if not args.get("posting_time"):
-			args["posting_time"] = "00:00"
-
-		sle = frappe.db.sql("""
-			select *, timestamp(posting_date, posting_time) as "timestamp"
-			from `tabStock Ledger Entry`
-			where item_code = %(item_code)s
-				and warehouse = %(warehouse)s
-				and is_cancelled = 0
-				and timestamp(posting_date, time_format(posting_time, %(time_format)s)) < timestamp(%(posting_date)s, time_format(%(posting_time)s, %(time_format)s))
-			order by timestamp(posting_date, posting_time) desc, creation desc
-			limit 1
-			for update""", args, as_dict=1)
-
-		return sle[0] if sle else frappe._dict()
-
-
 	def build(self):
 		from erpnext.controllers.stock_controller import future_sle_exists
 
@@ -734,6 +716,35 @@
 			bin_doc.flags.via_stock_ledger_entry = True
 			bin_doc.save(ignore_permissions=True)
 
+
+def get_previous_sle_of_current_voucher(args, exclude_current_voucher=False):
+	"""get stock ledger entries filtered by specific posting datetime conditions"""
+
+	args['time_format'] = '%H:%i:%s'
+	if not args.get("posting_date"):
+		args["posting_date"] = "1900-01-01"
+	if not args.get("posting_time"):
+		args["posting_time"] = "00:00"
+
+	voucher_condition = ""
+	if exclude_current_voucher:
+		voucher_no = args.get("voucher_no")
+		voucher_condition = f"and voucher_no != '{voucher_no}'"
+
+	sle = frappe.db.sql("""
+		select *, timestamp(posting_date, posting_time) as "timestamp"
+		from `tabStock Ledger Entry`
+		where item_code = %(item_code)s
+			and warehouse = %(warehouse)s
+			and is_cancelled = 0
+			{voucher_condition}
+			and timestamp(posting_date, time_format(posting_time, %(time_format)s)) < timestamp(%(posting_date)s, time_format(%(posting_time)s, %(time_format)s))
+		order by timestamp(posting_date, posting_time) desc, creation desc
+		limit 1
+		for update""".format(voucher_condition=voucher_condition), args, as_dict=1)
+
+	return sle[0] if sle else frappe._dict()
+
 def get_previous_sle(args, for_update=False):
 	"""
 		get the last sle on or before the current time-bucket,
@@ -862,9 +873,24 @@
 	return valuation_rate
 
 def update_qty_in_future_sle(args, allow_negative_stock=None):
+	"""Recalculate Qty after Transaction in future SLEs based on current SLE."""
+	datetime_limit_condition = ""
+	qty_shift = args.actual_qty
+
+	# find difference/shift in qty caused by stock reconciliation
+	if args.voucher_type == "Stock Reconciliation":
+		qty_shift = get_stock_reco_qty_shift(args)
+
+	# find the next nearest stock reco so that we only recalculate SLEs till that point
+	next_stock_reco_detail = get_next_stock_reco(args)
+	if next_stock_reco_detail:
+		detail = next_stock_reco_detail[0]
+		# add condition to update SLEs before this date & time
+		datetime_limit_condition = get_datetime_limit_condition(detail)
+
 	frappe.db.sql("""
 		update `tabStock Ledger Entry`
-		set qty_after_transaction = qty_after_transaction + {qty}
+		set qty_after_transaction = qty_after_transaction + {qty_shift}
 		where
 			item_code = %(item_code)s
 			and warehouse = %(warehouse)s
@@ -876,15 +902,70 @@
 					and creation > %(creation)s
 				)
 			)
-	""".format(qty=args.actual_qty), args)
+		{datetime_limit_condition}
+		""".format(qty_shift=qty_shift, datetime_limit_condition=datetime_limit_condition), args)
 
 	validate_negative_qty_in_future_sle(args, allow_negative_stock)
 
+def get_stock_reco_qty_shift(args):
+	stock_reco_qty_shift = 0
+	if args.get("is_cancelled"):
+		if args.get("previous_qty_after_transaction"):
+			# get qty (balance) that was set at submission
+			last_balance = args.get("previous_qty_after_transaction")
+			stock_reco_qty_shift = flt(args.qty_after_transaction) - flt(last_balance)
+		else:
+			stock_reco_qty_shift = flt(args.actual_qty)
+	else:
+		# reco is being submitted
+		last_balance = get_previous_sle_of_current_voucher(args,
+			exclude_current_voucher=True).get("qty_after_transaction")
+
+		if last_balance is not None:
+			stock_reco_qty_shift = flt(args.qty_after_transaction) - flt(last_balance)
+		else:
+			stock_reco_qty_shift = args.qty_after_transaction
+
+	return stock_reco_qty_shift
+
+def get_next_stock_reco(args):
+	"""Returns next nearest stock reconciliaton's details."""
+
+	return frappe.db.sql("""
+		select
+			name, posting_date, posting_time, creation, voucher_no
+		from
+			`tabStock Ledger Entry`
+		where
+			item_code = %(item_code)s
+			and warehouse = %(warehouse)s
+			and voucher_type = 'Stock Reconciliation'
+			and voucher_no != %(voucher_no)s
+			and is_cancelled = 0
+			and (timestamp(posting_date, posting_time) > timestamp(%(posting_date)s, %(posting_time)s)
+				or (
+					timestamp(posting_date, posting_time) = timestamp(%(posting_date)s, %(posting_time)s)
+					and creation > %(creation)s
+				)
+			)
+		limit 1
+	""", args, as_dict=1)
+
+def get_datetime_limit_condition(detail):
+	return f"""
+		and
+		(timestamp(posting_date, posting_time) < timestamp('{detail.posting_date}', '{detail.posting_time}')
+			or (
+				timestamp(posting_date, posting_time) = timestamp('{detail.posting_date}', '{detail.posting_time}')
+				and creation < '{detail.creation}'
+			)
+		)"""
+
 def validate_negative_qty_in_future_sle(args, allow_negative_stock=None):
 	allow_negative_stock = allow_negative_stock \
 		or cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock"))
 
-	if args.actual_qty < 0 and not allow_negative_stock:
+	if (args.actual_qty < 0 or args.voucher_type == "Stock Reconciliation") and not allow_negative_stock:
 		sle = get_future_sle_with_negative_qty(args)
 		if sle:
 			message = _("{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction.").format(
diff --git a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py
index 2a8446d..0d20b98 100644
--- a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py
+++ b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py
@@ -81,10 +81,9 @@
 
 		# check SLA custom fields created for leads
 		sla_fields = get_service_level_agreement_fields()
-		meta = frappe.get_meta(doctype, cached=False)
 
 		for field in sla_fields:
-			self.assertTrue(meta.has_field(field.get("fieldname")))
+			self.assertTrue(frappe.db.exists("Custom Field", {"dt": doctype, "fieldname": field.get("fieldname")}))
 
 	def test_docfield_creation_for_sla_on_custom_dt(self):
 		doctype = create_custom_doctype()
@@ -102,10 +101,9 @@
 
 		# check SLA docfields created
 		sla_fields = get_service_level_agreement_fields()
-		meta = frappe.get_meta(doctype.name, cached=False)
 
 		for field in sla_fields:
-			self.assertTrue(meta.has_field(field.get("fieldname")))
+			self.assertTrue(frappe.db.exists("DocField", {"fieldname": field.get("fieldname"), "parent": doctype.name}))
 
 	def test_sla_application(self):
 		# Default Service Level Agreement