Merge pull request #25776 from rohitwaghchaure/fixed-warehouse-not-found-issue

fix: warehouse not found in stock entry
diff --git a/.pylintrc b/.pylintrc
deleted file mode 100644
index 4b2ea0a..0000000
--- a/.pylintrc
+++ /dev/null
@@ -1 +0,0 @@
-disable=access-member-before-definition
\ No newline at end of file
diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py
index d5ab1c1..dd346bc 100644
--- a/erpnext/accounts/deferred_revenue.py
+++ b/erpnext/accounts/deferred_revenue.py
@@ -41,7 +41,7 @@
 	if account:
 		conditions += "AND %s='%s'"%(deferred_account, account)
 	elif company:
-		conditions += "AND p.company='%s'"%(company)
+		conditions += f"AND p.company = {frappe.db.escape(company)}"
 
 	return conditions
 
@@ -360,12 +360,10 @@
 			frappe.flags.deferred_accounting_error = True
 
 def send_mail(deferred_process):
-	title = _("Error while processing deferred accounting for {0}".format(deferred_process))
-	content = _("""
-		Deferred accounting failed for some invoices:
-		Please check Process Deferred Accounting {0}
-		and submit manually after resolving errors
-	""").format(get_link_to_form('Process Deferred Accounting', deferred_process))
+	title = _("Error while processing deferred accounting for {0}").format(deferred_process)
+	link = get_link_to_form('Process Deferred Accounting', deferred_process)
+	content = _("Deferred accounting failed for some invoices:") + "\n"
+	content += _("Please check Process Deferred Accounting {0} and submit manually after resolving errors.").format(link)
 	sendmail_to_system_managers(title, content)
 
 def book_revenue_via_journal_entry(doc, credit_account, debit_account, against,
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py
index 78febf9..948c513 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py
@@ -75,8 +75,13 @@
 	def pl_must_have_cost_center(self):
 		if frappe.get_cached_value("Account", self.account, "report_type") == "Profit and Loss":
 			if not self.cost_center and self.voucher_type != 'Period Closing Voucher':
-				frappe.throw(_("{0} {1}: Cost Center is required for 'Profit and Loss' account {2}. Please set up a default Cost Center for the Company.")
-					.format(self.voucher_type, self.voucher_no, self.account))
+				msg = _("{0} {1}: Cost Center is required for 'Profit and Loss' account {2}.").format(
+					self.voucher_type, self.voucher_no, self.account)
+				msg += " "
+				msg += _("Please set the cost center field in {0} or setup a default Cost Center for the Company.").format(
+					self.voucher_type)
+
+				frappe.throw(msg, title=_("Missing Cost Center"))
 
 	def validate_dimensions_for_pl_and_bs(self):
 		account_type = frappe.db.get_value("Account", self.account, "report_type")
diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
index aa0c53e..8c5a34a 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
@@ -101,15 +101,24 @@
 	},
 
 	before_save: function(frm) {
+		frm.set_value("grand_total", 0);
+		frm.set_value("net_total", 0);
+		frm.set_value("total_quantity", 0);
+		frm.set_value("taxes", []);
+
+		for (let row of frm.doc.payment_reconciliation) {
+			row.expected_amount = 0;
+		}
+
 		for (let row of frm.doc.pos_transactions) {
 			frappe.db.get_doc("POS Invoice", row.pos_invoice).then(doc => {
-				cur_frm.doc.grand_total -= flt(doc.grand_total);
-				cur_frm.doc.net_total -= flt(doc.net_total);
-				cur_frm.doc.total_quantity -= flt(doc.total_qty);
-				refresh_payments(doc, cur_frm, 1);
-				refresh_taxes(doc, cur_frm, 1);
-				refresh_fields(cur_frm);
-				set_html_data(cur_frm);
+				frm.doc.grand_total += flt(doc.grand_total);
+				frm.doc.net_total += flt(doc.net_total);
+				frm.doc.total_quantity += flt(doc.total_qty);
+				refresh_payments(doc, frm);
+				refresh_taxes(doc, frm);
+				refresh_fields(frm);
+				set_html_data(frm);
 			});
 		}
 	}
@@ -118,7 +127,7 @@
 frappe.ui.form.on('POS Closing Entry Detail', {
 	closing_amount: (frm, cdt, cdn) => {
 		const row = locals[cdt][cdn];
-		frappe.model.set_value(cdt, cdn, "difference", flt(row.expected_amount - row.closing_amount))
+		frappe.model.set_value(cdt, cdn, "difference", flt(row.expected_amount - row.closing_amount));
 	}
 })
 
@@ -142,28 +151,28 @@
 	})
 }
 
-function refresh_payments(d, frm, remove) {
+function refresh_payments(d, frm) {
 	d.payments.forEach(p => {
 		const payment = frm.doc.payment_reconciliation.find(pay => pay.mode_of_payment === p.mode_of_payment);
 		if (payment) {
-			if (!remove) payment.expected_amount += flt(p.amount);
-			else payment.expected_amount -= flt(p.amount);
+			payment.expected_amount += flt(p.amount);
+			payment.difference = payment.closing_amount - payment.expected_amount;
 		} else {
 			frm.add_child("payment_reconciliation", {
 				mode_of_payment: p.mode_of_payment,
 				opening_amount: 0,
-				expected_amount: p.amount
+				expected_amount: p.amount,
+				closing_amount: 0
 			})
 		}
 	})
 }
 
-function refresh_taxes(d, frm, remove) {
+function refresh_taxes(d, frm) {
 	d.taxes.forEach(t => {
 		const tax = frm.doc.taxes.find(tx => tx.account_head === t.account_head && tx.rate === t.rate);
 		if (tax) {
-			if (!remove) tax.amount += flt(t.tax_amount);
-			else tax.amount -= flt(t.tax_amount);
+			tax.amount += flt(t.tax_amount);
 		} else {
 			frm.add_child("taxes", {
 				account_head: t.account_head,
diff --git a/erpnext/accounts/doctype/pos_closing_entry_detail/pos_closing_entry_detail.json b/erpnext/accounts/doctype/pos_closing_entry_detail/pos_closing_entry_detail.json
index 6e7768d..bbf1ba0 100644
--- a/erpnext/accounts/doctype/pos_closing_entry_detail/pos_closing_entry_detail.json
+++ b/erpnext/accounts/doctype/pos_closing_entry_detail/pos_closing_entry_detail.json
@@ -46,6 +46,7 @@
    "reqd": 1
   },
   {
+   "default": "0",
    "fieldname": "closing_amount",
    "fieldtype": "Currency",
    "in_list_view": 1,
@@ -57,7 +58,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-10-23 16:45:43.662034",
+ "modified": "2021-05-19 20:08:44.523861",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "POS Closing Entry Detail",
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
index 473db56..f55fdab 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
@@ -455,32 +455,26 @@
 
 @frappe.whitelist()
 def get_stock_availability(item_code, warehouse):
-	latest_sle = frappe.db.sql("""select qty_after_transaction
-		from `tabStock Ledger Entry`
+	bin_qty = frappe.db.sql("""select actual_qty from `tabBin`
 		where item_code = %s and warehouse = %s
-		order by posting_date desc, posting_time desc
 		limit 1""", (item_code, warehouse), as_dict=1)
 
 	pos_sales_qty = get_pos_reserved_qty(item_code, warehouse)
 
-	sle_qty = latest_sle[0].qty_after_transaction or 0 if latest_sle else 0
+	bin_qty = bin_qty[0].actual_qty or 0 if bin_qty else 0
 
-	if sle_qty and pos_sales_qty:
-		return sle_qty - pos_sales_qty
-	else:
-		return sle_qty
+	return bin_qty - pos_sales_qty
 
 def get_pos_reserved_qty(item_code, warehouse):
 	reserved_qty = frappe.db.sql("""select sum(p_item.qty) as qty
 		from `tabPOS Invoice` p, `tabPOS Invoice Item` p_item
 		where p.name = p_item.parent
-		and p.consolidated_invoice is NULL
-		and p.docstatus = 1
+		and ifnull(p.consolidated_invoice, '') = ''
 		and p_item.docstatus = 1
 		and p_item.item_code = %s
 		and p_item.warehouse = %s
 		""", (item_code, warehouse), as_dict=1)
-	
+
 	return reserved_qty[0].qty or 0 if reserved_qty else 0
 
 @frappe.whitelist()
diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
index bc78743..b0ddea3 100644
--- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
+++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
@@ -56,12 +56,12 @@
 		sales = [d for d in pos_invoice_docs if d.get('is_return') == 0]
 
 		sales_invoice, credit_note = "", ""
-		if sales:
-			sales_invoice = self.process_merging_into_sales_invoice(sales)
-
 		if returns:
 			credit_note = self.process_merging_into_credit_note(returns)
 
+		if sales:
+			sales_invoice = self.process_merging_into_sales_invoice(sales)
+
 		self.save() # save consolidated_sales_invoice & consolidated_credit_note ref in merge log
 
 		self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_note)
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index 5538568..1808005 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -17,7 +17,7 @@
 		var me = this;
 		this._super();
 
-		this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice'];
+		this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice', 'Timesheet'];
 		if(!this.frm.doc.__islocal && !this.frm.doc.customer && this.frm.doc.debit_to) {
 			// show debit_to in print format
 			this.frm.set_df_property("debit_to", "print_hide", 0);
@@ -695,14 +695,16 @@
 	},
 
 	project: function(frm){
-		frm.call({
-			method: "add_timesheet_data",
-			doc: frm.doc,
-			callback: function(r, rt) {
-				refresh_field(['timesheets'])
-			}
-		})
-		frm.refresh();
+		if (!frm.doc.is_return) {
+			frm.call({
+				method: "add_timesheet_data",
+				doc: frm.doc,
+				callback: function(r, rt) {
+					refresh_field(['timesheets'])
+				}
+			})
+			frm.refresh();
+		}
 	},
 
 	onload: function(frm) {
@@ -817,14 +819,27 @@
 		}
 	},
 
+	add_timesheet_row: function(frm, row, exchange_rate) {
+		frm.add_child('timesheets', {
+			'activity_type': row.activity_type,
+			'description': row.description,
+			'time_sheet': row.parent,
+			'billing_hours': row.billing_hours,
+			'billing_amount': flt(row.billing_amount) * flt(exchange_rate),
+			'timesheet_detail': row.name
+		});
+		frm.refresh_field('timesheets');
+		calculate_total_billing_amount(frm);
+	},
+
 	refresh: function(frm) {
-		if (frm.doc.project) {
+		if (frm.doc.docstatus===0 && !frm.doc.is_return) {
 			frm.add_custom_button(__('Fetch Timesheet'), function() {
 				let d = new frappe.ui.Dialog({
 					title: __('Fetch Timesheet'),
 					fields: [
 						{
-							"label" : "From",
+							"label" : __("From"),
 							"fieldname": "from_time",
 							"fieldtype": "Date",
 							"reqd": 1,
@@ -834,11 +849,18 @@
 							fieldname: 'col_break_1',
 						},
 						{
-							"label" : "To",
+							"label" : __("To"),
 							"fieldname": "to_time",
 							"fieldtype": "Date",
 							"reqd": 1,
-						}
+						},
+						{
+							"label" : __("Project"),
+							"fieldname": "project",
+							"fieldtype": "Link",
+							"options": "Project",
+							"default": frm.doc.project
+						},
 					],
 					primary_action: function() {
 						let data = d.get_values();
@@ -847,27 +869,35 @@
 							args: {
 								from_time: data.from_time,
 								to_time: data.to_time,
-								project: frm.doc.project
+								project: data.project
 							},
 							callback: function(r) {
-								if(!r.exc) {
-									if(r.message.length > 0) {
-										frm.clear_table('timesheets')
-										r.message.forEach((d) => {
-											frm.add_child('timesheets',{
-												'time_sheet': d.parent,
-												'billing_hours': d.billing_hours,
-												'billing_amount': d.billing_amt,
-												'timesheet_detail': d.name
+								if (!r.exc && r.message.length > 0) {
+									frm.clear_table('timesheets')
+									r.message.forEach((d) => {
+										let exchange_rate = 1.0;
+										if (frm.doc.currency != d.currency) {
+											frappe.call({
+												method: 'erpnext.setup.utils.get_exchange_rate',
+												args: {
+													from_currency: d.currency,
+													to_currency: frm.doc.currency
+												},
+												callback: function(r) {
+													if (r.message) {
+														exchange_rate = r.message;
+														frm.events.add_timesheet_row(frm, d, exchange_rate);
+													}
+												}
 											});
-										});
-										frm.refresh_field('timesheets')
-									}
-									else {
-										frappe.msgprint(__('No Timesheet Found.'))
-									}
-									d.hide();
+										} else {
+											frm.events.add_timesheet_row(frm, d, exchange_rate);
+										}
+									});
+								} else {
+									frappe.msgprint(__('No Timesheets found with the selected filters.'))
 								}
+								d.hide();
 							}
 						});
 					},
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index 7ae2089..e7dd6b8 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -749,6 +749,7 @@
   {
    "collapsible": 1,
    "collapsible_depends_on": "eval:doc.total_billing_amount > 0",
+   "depends_on": "eval: !doc.is_return",
    "fieldname": "time_sheet_list",
    "fieldtype": "Section Break",
    "hide_days": 1,
@@ -771,6 +772,7 @@
    "hide_days": 1,
    "hide_seconds": 1,
    "label": "Total Billing Amount",
+   "options": "currency",
    "print_hide": 1,
    "read_only": 1
   },
@@ -1959,7 +1961,7 @@
    "label": "Is Debit Note"
   },
   {
-   "default": 0,
+   "default": "0",
    "depends_on": "grand_total",
    "fieldname": "disable_rounded_total",
    "fieldtype": "Check",
@@ -1976,7 +1978,7 @@
    "link_fieldname": "consolidated_invoice"
   }
  ],
- "modified": "2021-04-23 22:36:32.916354",
+ "modified": "2021-05-20 22:48:33.988881",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Sales Invoice",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index bb74a02..a008742 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -125,6 +125,8 @@
 		self.validate_multiple_billing("Delivery Note", "dn_detail", "amount", "items")
 		if not self.is_return:
 			self.validate_serial_numbers()
+		else:
+			self.timesheets = []
 		self.update_packing_list()
 		self.set_billing_hours_and_amount()
 		self.update_timesheet_billing_for_project()
@@ -337,7 +339,7 @@
 
 		if "Healthcare" in active_domains:
 			manage_invoice_submit_cancel(self, "on_cancel")
-
+		self.unlink_sales_invoice_from_timesheets()
 		self.ignore_linked_doctypes = ('GL Entry', 'Stock Ledger Entry', 'Repost Item Valuation')
 
 	def update_status_updater_args(self):
@@ -393,6 +395,18 @@
 		if validate_against_credit_limit:
 			check_credit_limit(self.customer, self.company, bypass_credit_limit_check_at_sales_order)
 
+	def unlink_sales_invoice_from_timesheets(self):
+		for row in self.timesheets:
+			timesheet = frappe.get_doc('Timesheet', row.time_sheet)
+			for time_log in timesheet.time_logs:
+				if time_log.sales_invoice == self.name:
+					time_log.sales_invoice = None
+			timesheet.calculate_total_amounts()
+			timesheet.calculate_percentage_billed()
+			timesheet.flags.ignore_validate_update_after_submit = True
+			timesheet.set_status()
+			timesheet.db_update_all()
+
 	@frappe.whitelist()
 	def set_missing_values(self, for_validate=False):
 		pos = self.set_pos_fields(for_validate)
@@ -427,7 +441,7 @@
 				timesheet.calculate_percentage_billed()
 				timesheet.flags.ignore_validate_update_after_submit = True
 				timesheet.set_status()
-				timesheet.save()
+				timesheet.db_update_all()
 
 	def update_time_sheet_detail(self, timesheet, args, sales_invoice):
 		for data in timesheet.time_logs:
@@ -741,8 +755,10 @@
 				self.append('timesheets', {
 						'time_sheet': data.parent,
 						'billing_hours': data.billing_hours,
-						'billing_amount': data.billing_amt,
-						'timesheet_detail': data.name
+						'billing_amount': data.billing_amount,
+						'timesheet_detail': data.name,
+						'activity_type': data.activity_type,
+						'description': data.description
 					})
 
 			self.calculate_billing_amount_for_timesheet()
diff --git a/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json b/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json
index f7b9aef..f069e8d 100644
--- a/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json
+++ b/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json
@@ -1,172 +1,78 @@
 {
- "allow_copy": 0, 
- "allow_events_in_timeline": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2016-06-14 19:21:34.321662", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
+ "actions": [],
+ "creation": "2016-06-14 19:21:34.321662",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "activity_type",
+  "description",
+  "billing_hours",
+  "billing_amount",
+  "time_sheet",
+  "timesheet_detail"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "time_sheet", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 1, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Time Sheet", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Timesheet", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "time_sheet",
+   "fieldtype": "Link",
+   "in_global_search": 1,
+   "in_list_view": 1,
+   "label": "Time Sheet",
+   "options": "Timesheet",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "billing_hours", 
-   "fieldtype": "Float", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Billing Hours", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "billing_hours",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Billing Hours",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "billing_amount", 
-   "fieldtype": "Currency", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Billing Amount", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "billing_amount",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Billing Amount",
+   "options": "currency",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 1, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "timesheet_detail", 
-   "fieldtype": "Data", 
-   "hidden": 1, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Timesheet Detail", 
-   "length": 0, 
-   "no_copy": 1, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 1, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
+   "allow_on_submit": 1,
+   "fieldname": "timesheet_detail",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Timesheet Detail",
+   "no_copy": 1,
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "activity_type",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Activity Type",
+   "options": "Activity Type",
+   "read_only": 1
+  },
+  {
+   "fieldname": "description",
+   "fieldtype": "Small Text",
+   "in_list_view": 1,
+   "label": "Description",
+   "read_only": 1
   }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 0, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2019-02-18 18:50:44.770361", 
- "modified_by": "Administrator", 
- "module": "Accounts", 
- "name": "Sales Invoice Timesheet", 
- "name_case": "", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 0, 
- "sort_field": "modified", 
- "sort_order": "DESC", 
- "track_changes": 1, 
- "track_seen": 0, 
- "track_views": 0
+ ],
+ "istable": 1,
+ "links": [],
+ "modified": "2021-05-20 22:33:57.234846",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Sales Invoice Timesheet",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
 }
\ No newline at end of file
diff --git a/erpnext/assets/doctype/asset_category/asset_category.js b/erpnext/assets/doctype/asset_category/asset_category.js
index 74963c2..51ce157 100644
--- a/erpnext/assets/doctype/asset_category/asset_category.js
+++ b/erpnext/assets/doctype/asset_category/asset_category.js
@@ -4,7 +4,7 @@
 frappe.ui.form.on('Asset Category', {
 	onload: function(frm) {
 		frm.add_fetch('company_name', 'accumulated_depreciation_account', 'accumulated_depreciation_account');
-		frm.add_fetch('company_name', 'depreciation_expense_account', 'accumulated_depreciation_account');
+		frm.add_fetch('company_name', 'depreciation_expense_account', 'depreciation_expense_account');
 
 		frm.set_query('fixed_asset_account', 'accounts', function(doc, cdt, cdn) {
 			var d  = locals[cdt][cdn];
diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json
index 4cc5753..38b8dfd 100644
--- a/erpnext/buying/doctype/supplier/supplier.json
+++ b/erpnext/buying/doctype/supplier/supplier.json
@@ -383,8 +383,14 @@
  "icon": "fa fa-user",
  "idx": 370,
  "image_field": "image",
- "links": [],
- "modified": "2021-01-06 19:51:40.939087",
+ "links": [
+  {
+   "group": "Item Group",
+   "link_doctype": "Supplier Item Group",
+   "link_fieldname": "supplier"
+  }
+ ],
+ "modified": "2021-05-18 15:10:11.087191",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Supplier",
diff --git a/erpnext/buying/doctype/supplier_item_group/__init__.py b/erpnext/buying/doctype/supplier_item_group/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_item_group/__init__.py
diff --git a/erpnext/buying/doctype/supplier_item_group/supplier_item_group.js b/erpnext/buying/doctype/supplier_item_group/supplier_item_group.js
new file mode 100644
index 0000000..f7da90d
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_item_group/supplier_item_group.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Supplier Item Group', {
+	// refresh: function(frm) {
+
+	// }
+});
diff --git a/erpnext/buying/doctype/supplier_item_group/supplier_item_group.json b/erpnext/buying/doctype/supplier_item_group/supplier_item_group.json
new file mode 100644
index 0000000..1971458
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_item_group/supplier_item_group.json
@@ -0,0 +1,77 @@
+{
+ "actions": [],
+ "creation": "2021-05-07 18:16:40.621421",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "supplier",
+  "item_group"
+ ],
+ "fields": [
+  {
+   "fieldname": "supplier",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Supplier",
+   "options": "Supplier",
+   "reqd": 1
+  },
+  {
+   "fieldname": "item_group",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Item Group",
+   "options": "Item Group",
+   "reqd": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2021-05-19 13:48:16.742303",
+ "modified_by": "Administrator",
+ "module": "Buying",
+ "name": "Supplier Item Group",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Purchase User",
+   "share": 1,
+   "write": 1
+  },
+  {
+   "create": 1,
+   "delete": 1,
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Purchase Manager",
+   "share": 1,
+   "write": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/buying/doctype/supplier_item_group/supplier_item_group.py b/erpnext/buying/doctype/supplier_item_group/supplier_item_group.py
new file mode 100644
index 0000000..3a2e5d6
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_item_group/supplier_item_group.py
@@ -0,0 +1,18 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe import _
+from frappe.model.document import Document
+
+class SupplierItemGroup(Document):
+	def validate(self):
+		exists = frappe.db.exists({
+			'doctype': 'Supplier Item Group',
+			'supplier': self.supplier,
+			'item_group': self.item_group
+		})
+		if exists:
+			frappe.throw(_("Item Group has already been linked to this supplier."))
\ No newline at end of file
diff --git a/erpnext/buying/doctype/supplier_item_group/test_supplier_item_group.py b/erpnext/buying/doctype/supplier_item_group/test_supplier_item_group.py
new file mode 100644
index 0000000..c75044d
--- /dev/null
+++ b/erpnext/buying/doctype/supplier_item_group/test_supplier_item_group.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+class TestSupplierItemGroup(unittest.TestCase):
+	pass
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 996c4ed..544e624 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -1011,7 +1011,7 @@
 				else:
 					grand_total -= self.get("total_advance")
 					base_grand_total = flt(grand_total * self.get("conversion_rate"), self.precision("base_grand_total"))
-			print(grand_total, base_grand_total)
+
 			if total != flt(grand_total, self.precision("grand_total")) or \
 				base_total != flt(base_grand_total, self.precision("base_grand_total")):
 				frappe.throw(_("Total Payment Amount in Payment Schedule must be equal to Grand / Rounded Total"))
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index b31724f..46301b7 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -204,7 +204,7 @@
 
 	if "description" in searchfields:
 		searchfields.remove("description")
-
+	
 	columns = ''
 	extra_searchfields = [field for field in searchfields
 		if not field in ["name", "item_group", "description"]]
@@ -216,11 +216,22 @@
 		if not field in searchfields]
 	searchfields = " or ".join([field + " like %(txt)s" for field in searchfields])
 
+	if filters.get('supplier'):
+		item_group_list = frappe.get_all('Supplier Item Group', filters = {'supplier': filters.get('supplier')}, fields = ['item_group'])
+		
+		item_groups = []
+		for i in item_group_list:
+			item_groups.append(i.item_group)
+
+		del filters['supplier']
+
+		if item_groups:
+			filters['item_group'] = ['in', item_groups]
+		
 	description_cond = ''
 	if frappe.db.count('Item', cache=True) < 50000:
 		# scan description only if items are less than 50000
 		description_cond = 'or tabItem.description LIKE %(txt)s'
-
 	return frappe.db.sql("""select tabItem.name,
 		if(length(tabItem.item_name) > 40,
 			concat(substr(tabItem.item_name, 1, 40), "..."), item_name) as item_name,
diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_api.py b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_api.py
index f713684..7fd3b34 100755
--- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_api.py
+++ b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_api.py
@@ -7,6 +7,7 @@
 from __future__ import unicode_literals
 
 import urllib
+from urllib.parse import quote
 import hashlib
 import hmac
 import base64
@@ -68,8 +69,9 @@
 	"""
 	md = hashlib.md5()
 	md.update(string)
-	return base64.encodestring(md.digest()).strip('\n') if six.PY2 \
-		else base64.encodebytes(md.digest()).decode().strip()
+	return base64.encodebytes(md.digest()).decode().strip()
+
+
 
 def remove_empty(d):
 	"""
@@ -177,7 +179,6 @@
 			'SignatureMethod': 'HmacSHA256',
 		}
 		params.update(extra_data)
-		quote = urllib.quote if six.PY2 else urllib.parse.quote
 		request_description = '&'.join(['%s=%s' % (k, quote(params[k], safe='-_.~')) for k in sorted(params)])
 		signature = self.calc_signature(method, request_description)
 		url = '%s%s?%s&Signature=%s' % (self.domain, self.uri, request_description, quote(signature))
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 9b3ddd0..d4655e1 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -779,3 +779,4 @@
 erpnext.patches.v13_0.germany_make_custom_fields
 erpnext.patches.v13_0.germany_fill_debtor_creditor_number
 erpnext.patches.v13_0.set_pos_closing_as_failed
+erpnext.patches.v13_0.update_timesheet_changes
diff --git a/erpnext/patches/v13_0/update_timesheet_changes.py b/erpnext/patches/v13_0/update_timesheet_changes.py
new file mode 100644
index 0000000..93b7f8e
--- /dev/null
+++ b/erpnext/patches/v13_0/update_timesheet_changes.py
@@ -0,0 +1,25 @@
+from __future__ import unicode_literals
+import frappe
+from frappe.model.utils.rename_field import rename_field
+
+def execute():
+	frappe.reload_doc("projects", "doctype", "timesheet")
+	frappe.reload_doc("projects", "doctype", "timesheet_detail")
+
+	if frappe.db.has_column("Timesheet Detail", "billable"):
+		rename_field("Timesheet Detail", "billable", "is_billable")
+
+	base_currency = frappe.defaults.get_global_default('currency')
+
+	frappe.db.sql("""UPDATE `tabTimesheet Detail`
+			SET base_billing_rate = billing_rate,
+			base_billing_amount = billing_amount,
+			base_costing_rate = costing_rate,
+			base_costing_amount = costing_amount""")
+
+	frappe.db.sql("""UPDATE `tabTimesheet`
+			SET currency = '{0}',
+			exchange_rate = 1.0,
+			base_total_billable_amount = total_billable_amount,
+			base_total_billed_amount = total_billed_amount,
+			base_total_costing_amount = total_costing_amount""".format(base_currency))
\ No newline at end of file
diff --git a/erpnext/patches/v7_0/convert_timelog_to_timesheet.py b/erpnext/patches/v7_0/convert_timelog_to_timesheet.py
index 3af6622..8c60b5b 100644
--- a/erpnext/patches/v7_0/convert_timelog_to_timesheet.py
+++ b/erpnext/patches/v7_0/convert_timelog_to_timesheet.py
@@ -51,7 +51,7 @@
 
 def get_timelog_data(data):
 	return {
-		'billable': data.billable,
+		'is_billable': data.billable,
 		'from_time': data.from_time,
 		'hours': data.hours,
 		'to_time': data.to_time,
diff --git a/erpnext/projects/doctype/activity_type/activity_type.js b/erpnext/projects/doctype/activity_type/activity_type.js
index 7eb3571..f1ba882 100644
--- a/erpnext/projects/doctype/activity_type/activity_type.js
+++ b/erpnext/projects/doctype/activity_type/activity_type.js
@@ -1,4 +1,8 @@
 frappe.ui.form.on("Activity Type", {
+	onload: function(frm) {
+		frm.set_currency_labels(["billing_rate", "costing_rate"], frappe.defaults.get_global_default('currency'));
+	},
+
 	refresh: function(frm) {
 		frm.add_custom_button(__("Activity Cost per Employee"), function() {
 			frappe.route_options = {"activity_type": frm.doc.name};
diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js
index c5265e2..31460f6 100644
--- a/erpnext/projects/doctype/project/project.js
+++ b/erpnext/projects/doctype/project/project.js
@@ -87,7 +87,7 @@
 
 				frm.add_custom_button(__("Kanban Board"), () => {
 					frappe.call('erpnext.projects.doctype.project.project.create_kanban_board_if_not_exists', {
-						project: frm.doc.project_name
+						project: frm.doc.name
 					}).then(() => {
 						frappe.set_route('List', 'Task', 'Kanban', frm.doc.project_name);
 					});
diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py
index 55c5149..c8fbe0b 100644
--- a/erpnext/projects/doctype/project/project.py
+++ b/erpnext/projects/doctype/project/project.py
@@ -523,8 +523,9 @@
 def create_kanban_board_if_not_exists(project):
 	from frappe.desk.doctype.kanban_board.kanban_board import quick_kanban_board
 
-	if not frappe.db.exists('Kanban Board', project):
-		quick_kanban_board('Task', project, 'status', project)
+	project = frappe.get_doc('Project', project)
+	if not frappe.db.exists('Kanban Board', project.project_name):
+		quick_kanban_board('Task', project.project_name, 'status', project.name)
 
 	return True
 
diff --git a/erpnext/projects/doctype/task/task.js b/erpnext/projects/doctype/task/task.js
index 6a9d2d1..3cd92ee 100644
--- a/erpnext/projects/doctype/task/task.js
+++ b/erpnext/projects/doctype/task/task.js
@@ -5,12 +5,6 @@
 
 frappe.ui.form.on("Task", {
 	setup: function (frm) {
-		frm.set_query("project", function () {
-			return {
-				query: "erpnext.projects.doctype.task.task.get_project"
-			}
-		});
-
 		frm.make_methods = {
 			'Timesheet': () => frappe.model.open_mapped_doc({
 				method: 'erpnext.projects.doctype.task.task.make_timesheet',
diff --git a/erpnext/projects/doctype/timesheet/test_timesheet.py b/erpnext/projects/doctype/timesheet/test_timesheet.py
index d21ac0f..2b0c3ab 100644
--- a/erpnext/projects/doctype/timesheet/test_timesheet.py
+++ b/erpnext/projects/doctype/timesheet/test_timesheet.py
@@ -37,7 +37,7 @@
 		emp = make_employee("test_employee_6@salary.com")
 
 		make_salary_structure_for_timesheet(emp)
-		timesheet = make_timesheet(emp, simulate=True, billable=1)
+		timesheet = make_timesheet(emp, simulate=True, is_billable=1)
 
 		self.assertEqual(timesheet.total_hours, 2)
 		self.assertEqual(timesheet.total_billable_hours, 2)
@@ -49,7 +49,7 @@
 		emp = make_employee("test_employee_6@salary.com")
 
 		make_salary_structure_for_timesheet(emp)
-		timesheet = make_timesheet(emp, simulate=True, billable=0)
+		timesheet = make_timesheet(emp, simulate=True, is_billable=0)
 
 		self.assertEqual(timesheet.total_hours, 2)
 		self.assertEqual(timesheet.total_billable_hours, 0)
@@ -61,7 +61,7 @@
 		emp = make_employee("test_employee_6@salary.com", company="_Test Company")
 
 		salary_structure = make_salary_structure_for_timesheet(emp)
-		timesheet = make_timesheet(emp, simulate = True, billable=1)
+		timesheet = make_timesheet(emp, simulate = True, is_billable=1)
 		salary_slip = make_salary_slip(timesheet.name)
 		salary_slip.submit()
 
@@ -82,7 +82,7 @@
 	def test_sales_invoice_from_timesheet(self):
 		emp = make_employee("test_employee_6@salary.com")
 
-		timesheet = make_timesheet(emp, simulate=True, billable=1)
+		timesheet = make_timesheet(emp, simulate=True, is_billable=1)
 		sales_invoice = make_sales_invoice(timesheet.name, '_Test Item', '_Test Customer')
 		sales_invoice.due_date = nowdate()
 		sales_invoice.submit()
@@ -100,7 +100,7 @@
 		emp = make_employee("test_employee_6@salary.com")
 		project = frappe.get_value("Project", {"project_name": "_Test Project"})
 
-		timesheet = make_timesheet(emp, simulate=True, billable=1, project=project, company='_Test Company')
+		timesheet = make_timesheet(emp, simulate=True, is_billable=1, project=project, company='_Test Company')
 		sales_invoice = create_sales_invoice(do_not_save=True)
 		sales_invoice.project = project
 		sales_invoice.submit()
@@ -171,13 +171,13 @@
 	return salary_structure
 
 
-def make_timesheet(employee, simulate=False, billable = 0, activity_type="_Test Activity Type", project=None, task=None, company=None):
+def make_timesheet(employee, simulate=False, is_billable = 0, activity_type="_Test Activity Type", project=None, task=None, company=None):
 	update_activity_type(activity_type)
 	timesheet = frappe.new_doc("Timesheet")
 	timesheet.employee = employee
 	timesheet.company = company or '_Test Company'
 	timesheet_detail = timesheet.append('time_logs', {})
-	timesheet_detail.billable = billable
+	timesheet_detail.is_billable = is_billable
 	timesheet_detail.activity_type = activity_type
 	timesheet_detail.from_time = now_datetime()
 	timesheet_detail.hours = 2
diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js
index b123af5..84c7b81 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.js
+++ b/erpnext/projects/doctype/timesheet/timesheet.js
@@ -90,17 +90,99 @@
 		}
 		if(frm.doc.per_billed > 0) {
 			frm.fields_dict["time_logs"].grid.toggle_enable("billing_hours", false);
-			frm.fields_dict["time_logs"].grid.toggle_enable("billable", false);
+			frm.fields_dict["time_logs"].grid.toggle_enable("is_billable", false);
 		}
+		frm.trigger('setup_filters');
+		frm.trigger('set_dynamic_field_label');
+	},
+
+	customer: function(frm) {
+		frm.set_query('parent_project', function(doc) {
+			return {
+				filters: {
+					"customer": doc.customer
+				}
+			};
+		});
+		frm.set_query('project', 'time_logs', function(doc) {
+			return {
+				filters: {
+					"customer": doc.customer
+				}
+			};
+		});
+		frm.refresh();
+	},
+
+	currency: function(frm) {
+		let base_currency = frappe.defaults.get_global_default('currency');
+		if (base_currency != frm.doc.currency) {
+			frappe.call({
+				method: "erpnext.setup.utils.get_exchange_rate",
+				args: {
+					from_currency: frm.doc.currency,
+					to_currency: base_currency
+				},
+				callback: function(r) {
+					if (r.message) {
+						frm.set_value('exchange_rate', flt(r.message));
+						frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency + " = [?] " + base_currency);
+					}
+				}
+			});
+		}
+		frm.trigger('set_dynamic_field_label');
+	},
+
+	exchange_rate: function(frm) {
+		$.each(frm.doc.time_logs, function(i, d) {
+			calculate_billing_costing_amount(frm, d.doctype, d.name);
+		});
+		calculate_time_and_amount(frm);
+	},
+
+	set_dynamic_field_label: function(frm) {
+		let base_currency = frappe.defaults.get_global_default('currency');
+		frm.set_currency_labels(["base_total_costing_amount", "base_total_billable_amount", "base_total_billed_amount"], base_currency);
+		frm.set_currency_labels(["total_costing_amount", "total_billable_amount", "total_billed_amount"], frm.doc.currency);
+
+		frm.toggle_display(["base_total_costing_amount", "base_total_billable_amount", "base_total_billed_amount"],
+			frm.doc.currency != base_currency);
+
+		if (frm.doc.time_logs.length > 0) {
+			frm.set_currency_labels(["base_billing_rate", "base_billing_amount", "base_costing_rate", "base_costing_amount"], base_currency, "time_logs");
+			frm.set_currency_labels(["billing_rate", "billing_amount", "costing_rate", "costing_amount"], frm.doc.currency, "time_logs");
+
+			let time_logs_grid = frm.fields_dict.time_logs.grid;
+			$.each(["base_billing_rate", "base_billing_amount", "base_costing_rate", "base_costing_amount"], function(i, d) {
+				if (frappe.meta.get_docfield(time_logs_grid.doctype, d))
+					time_logs_grid.set_column_disp(d, frm.doc.currency != base_currency);
+			});
+		}
+		frm.refresh_fields();
 	},
 
 	make_invoice: function(frm) {
+		let fields = [{
+			"fieldtype": "Link",
+			"label": __("Item Code"),
+			"fieldname": "item_code",
+			"options": "Item"
+		}];
+
+		if (!frm.doc.customer) {
+			fields.push({
+				"fieldtype": "Link",
+				"label": __("Customer"),
+				"fieldname": "customer",
+				"options": "Customer",
+				"default": frm.doc.customer
+			});
+		}
+
 		let dialog = new frappe.ui.Dialog({
-			title: __("Select Item (optional)"),
-			fields: [
-				{"fieldtype": "Link", "label": __("Item Code"), "fieldname": "item_code", "options":"Item"},
-				{"fieldtype": "Link", "label": __("Customer"), "fieldname": "customer", "options":"Customer"}
-			]
+			title: __("Create Sales Invoice"),
+			fields: fields
 		});
 
 		dialog.set_primary_action(__('Create Sales Invoice'), () => {
@@ -113,7 +195,8 @@
 				args: {
 					"source_name": frm.doc.name,
 					"item_code": args.item_code,
-					"customer": args.customer
+					"customer": frm.doc.customer || args.customer,
+					"currency": frm.doc.currency
 				},
 				freeze: true,
 				callback: function(r) {
@@ -136,8 +219,7 @@
 
 	parent_project: function(frm) {
 		set_project_in_timelog(frm);
-	},
-
+	}
 });
 
 frappe.ui.form.on("Timesheet Detail", {
@@ -171,35 +253,34 @@
 		if(frm.doc.parent_project) {
 			frappe.model.set_value(cdt, cdn, 'project', frm.doc.parent_project);
 		}
-
-		var $trigger_again = $('.form-grid').find('.grid-row').find('.btn-open-row');
-		$trigger_again.on('click', () => {
-			$('.form-grid')
-				.find('[data-fieldname="timer"]')
-				.append(frappe.render_template("timesheet"));
-			frm.trigger("control_timer");
-		});
 	},
+
 	hours: function(frm, cdt, cdn) {
 		calculate_end_time(frm, cdt, cdn);
+		calculate_billing_costing_amount(frm, cdt, cdn);
+		calculate_time_and_amount(frm);
 	},
 
 	billing_hours: function(frm, cdt, cdn) {
 		calculate_billing_costing_amount(frm, cdt, cdn);
+		calculate_time_and_amount(frm);
 	},
 
 	billing_rate: function(frm, cdt, cdn) {
 		calculate_billing_costing_amount(frm, cdt, cdn);
+		calculate_time_and_amount(frm);
 	},
 
 	costing_rate: function(frm, cdt, cdn) {
 		calculate_billing_costing_amount(frm, cdt, cdn);
+		calculate_time_and_amount(frm);
 	},
 
-	billable: function(frm, cdt, cdn) {
+	is_billable: function(frm, cdt, cdn) {
 		update_billing_hours(frm, cdt, cdn);
 		update_time_rates(frm, cdt, cdn);
 		calculate_billing_costing_amount(frm, cdt, cdn);
+		calculate_time_and_amount(frm);
 	},
 
 	activity_type: function(frm, cdt, cdn) {
@@ -207,7 +288,8 @@
 			method: "erpnext.projects.doctype.timesheet.timesheet.get_activity_cost",
 			args: {
 				employee: frm.doc.employee,
-				activity_type: frm.selected_doc.activity_type
+				activity_type: frm.selected_doc.activity_type,
+				currency: frm.doc.currency
 			},
 			callback: function(r){
 				if(r.message){
@@ -239,9 +321,9 @@
 	}
 };
 
-var update_billing_hours = function(frm, cdt, cdn){
-	var child = locals[cdt][cdn];
-	if(!child.billable) {
+var update_billing_hours = function(frm, cdt, cdn) {
+	let child = frappe.get_doc(cdt, cdn);
+	if (!child.is_billable) {
 		frappe.model.set_value(cdt, cdn, 'billing_hours', 0.0);
 	} else {
 		// bill all hours by default
@@ -249,40 +331,44 @@
 	}
 };
 
-var update_time_rates = function(frm, cdt, cdn){
-	var child = locals[cdt][cdn];
-	if(!child.billable){
+var update_time_rates = function(frm, cdt, cdn) {
+	let child = frappe.get_doc(cdt, cdn);
+	if (!child.is_billable) {
 		frappe.model.set_value(cdt, cdn, 'billing_rate', 0.0);
 	}
 };
 
-var calculate_billing_costing_amount = function(frm, cdt, cdn){
-	var child = locals[cdt][cdn];
-	var billing_amount = 0.0;
-	var costing_amount = 0.0;
-
-	if(child.billing_hours && child.billable){
-		billing_amount = (child.billing_hours * child.billing_rate);
+var calculate_billing_costing_amount = function(frm, cdt, cdn) {
+	let row = frappe.get_doc(cdt, cdn);
+	let billing_amount = 0.0;
+	let base_billing_amount = 0.0;
+	let exchange_rate = flt(frm.doc.exchange_rate);
+	frappe.model.set_value(cdt, cdn, 'base_billing_rate', flt(row.billing_rate) * exchange_rate);
+	frappe.model.set_value(cdt, cdn, 'base_costing_rate', flt(row.costing_rate) * exchange_rate);
+	if (row.billing_hours && row.is_billable) {
+		base_billing_amount = flt(row.billing_hours) * flt(row.base_billing_rate);
+		billing_amount = flt(row.billing_hours) * flt(row.billing_rate);
 	}
-	costing_amount = flt(child.costing_rate * child.hours);
+
+	frappe.model.set_value(cdt, cdn, 'base_billing_amount', base_billing_amount);
+	frappe.model.set_value(cdt, cdn, 'base_costing_amount', flt(row.base_costing_rate) * flt(row.hours));
 	frappe.model.set_value(cdt, cdn, 'billing_amount', billing_amount);
-	frappe.model.set_value(cdt, cdn, 'costing_amount', costing_amount);
-	calculate_time_and_amount(frm);
+	frappe.model.set_value(cdt, cdn, 'costing_amount', flt(row.costing_rate) * flt(row.hours));
 };
 
 var calculate_time_and_amount = function(frm) {
-	var tl = frm.doc.time_logs || [];
-	var total_working_hr = 0;
-	var total_billing_hr = 0;
-	var total_billable_amount = 0;
-	var total_costing_amount = 0;
+	let tl = frm.doc.time_logs || [];
+	let total_working_hr = 0;
+	let total_billing_hr = 0;
+	let total_billable_amount = 0;
+	let total_costing_amount = 0;
 	for(var i=0; i<tl.length; i++) {
 		if (tl[i].hours) {
 			total_working_hr += tl[i].hours;
 			total_billable_amount += tl[i].billing_amount;
 			total_costing_amount += tl[i].costing_amount;
 
-			if(tl[i].billable){
+			if (tl[i].is_billable) {
 				total_billing_hr += tl[i].billing_hours;
 			}
 		}
diff --git a/erpnext/projects/doctype/timesheet/timesheet.json b/erpnext/projects/doctype/timesheet/timesheet.json
index b286821..75f7478 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.json
+++ b/erpnext/projects/doctype/timesheet/timesheet.json
@@ -11,6 +11,9 @@
   "title",
   "naming_series",
   "company",
+  "customer",
+  "currency",
+  "exchange_rate",
   "sales_invoice",
   "column_break_3",
   "salary_slip",
@@ -30,11 +33,14 @@
   "total_hours",
   "billing_details",
   "total_billable_hours",
-  "total_billed_hours",
-  "total_costing_amount",
+  "base_total_billable_amount",
+  "base_total_billed_amount",
+  "base_total_costing_amount",
   "column_break_10",
+  "total_billed_hours",
   "total_billable_amount",
   "total_billed_amount",
+  "total_costing_amount",
   "per_billed",
   "section_break_18",
   "note",
@@ -176,7 +182,6 @@
    "default": "0",
    "fieldname": "total_hours",
    "fieldtype": "Float",
-   "in_list_view": 1,
    "label": "Total Working Hours",
    "read_only": 1
   },
@@ -199,7 +204,6 @@
    "allow_on_submit": 1,
    "fieldname": "total_billed_hours",
    "fieldtype": "Float",
-   "in_list_view": 1,
    "label": "Total Billed Hours",
    "print_hide": 1,
    "read_only": 1
@@ -209,6 +213,7 @@
    "fieldname": "total_costing_amount",
    "fieldtype": "Currency",
    "label": "Total Costing Amount",
+   "options": "currency",
    "print_hide": 1,
    "read_only": 1
   },
@@ -222,6 +227,7 @@
    "fieldname": "total_billable_amount",
    "fieldtype": "Currency",
    "label": "Total Billable Amount",
+   "options": "currency",
    "read_only": 1
   },
   {
@@ -229,6 +235,7 @@
    "fieldname": "total_billed_amount",
    "fieldtype": "Currency",
    "label": "Total Billed Amount",
+   "options": "currency",
    "print_hide": 1,
    "read_only": 1
   },
@@ -236,6 +243,7 @@
    "allow_on_submit": 1,
    "fieldname": "per_billed",
    "fieldtype": "Percent",
+   "in_list_view": 1,
    "label": "% Amount Billed",
    "no_copy": 1,
    "print_hide": 1,
@@ -265,13 +273,53 @@
    "fieldtype": "Link",
    "label": "Project",
    "options": "Project"
+  },
+  {
+   "fieldname": "customer",
+   "fieldtype": "Link",
+   "label": "Customer",
+   "options": "Customer"
+  },
+  {
+   "fetch_from": "customer.default_currency",
+   "fetch_if_empty": 1,
+   "fieldname": "currency",
+   "fieldtype": "Link",
+   "label": "Currency",
+   "options": "Currency"
+  },
+  {
+   "fieldname": "base_total_costing_amount",
+   "fieldtype": "Currency",
+   "label": "Total Costing Amount",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "base_total_billable_amount",
+   "fieldtype": "Currency",
+   "label": "Total Billable Amount",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "base_total_billed_amount",
+   "fieldtype": "Currency",
+   "label": "Total Billed Amount",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "exchange_rate",
+   "fieldtype": "Float",
+   "label": "Exchange Rate"
   }
  ],
  "icon": "fa fa-clock-o",
  "idx": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2021-01-08 20:51:14.590080",
+ "modified": "2021-05-18 16:10:08.249619",
  "modified_by": "Administrator",
  "module": "Projects",
  "name": "Timesheet",
diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py
index 8d99b48..a3e4577 100644
--- a/erpnext/projects/doctype/timesheet/timesheet.py
+++ b/erpnext/projects/doctype/timesheet/timesheet.py
@@ -14,6 +14,7 @@
 from erpnext.manufacturing.doctype.workstation.workstation import (check_if_within_operating_hours,
 	WorkstationHolidayError)
 from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations
+from erpnext.setup.utils import get_exchange_rate
 
 class OverlapError(frappe.ValidationError): pass
 class OverWorkLoggedError(frappe.ValidationError): pass
@@ -37,9 +38,9 @@
 		self.total_hours = 0.0
 		self.total_billable_hours = 0.0
 		self.total_billed_hours = 0.0
-		self.total_billable_amount = 0.0
-		self.total_costing_amount = 0.0
-		self.total_billed_amount = 0.0
+		self.total_billable_amount = self.base_total_billable_amount = 0.0
+		self.total_costing_amount = self.base_total_costing_amount = 0.0
+		self.total_billed_amount = self.base_total_billed_amount = 0.0
 
 		for d in self.get("time_logs"):
 			self.update_billing_hours(d)
@@ -47,10 +48,13 @@
 
 			self.total_hours += flt(d.hours)
 			self.total_costing_amount += flt(d.costing_amount)
-			if d.billable:
+			self.base_total_costing_amount += flt(d.base_costing_amount)
+			if d.is_billable:
 				self.total_billable_hours += flt(d.billing_hours)
 				self.total_billable_amount += flt(d.billing_amount)
+				self.base_total_billable_amount += flt(d.base_billing_amount)
 				self.total_billed_amount += flt(d.billing_amount) if d.sales_invoice else 0.0
+				self.base_total_billed_amount += flt(d.base_billing_amount) if d.sales_invoice else 0.0
 				self.total_billed_hours += flt(d.billing_hours) if d.sales_invoice else 0.0
 
 	def calculate_percentage_billed(self):
@@ -59,7 +63,7 @@
 			self.per_billed = (self.total_billed_amount * 100) / self.total_billable_amount
 
 	def update_billing_hours(self, args):
-		if args.billable:
+		if args.is_billable:
 			if flt(args.billing_hours) == 0.0:
 				args.billing_hours = args.hours
 		else:
@@ -133,16 +137,20 @@
 	def validate_time_logs(self):
 		for data in self.get('time_logs'):
 			self.validate_overlap(data)
-			self.validate_task_project()
+			self.set_project(data)
+			self.validate_project(data)
 
 	def validate_overlap(self, data):
 		settings = frappe.get_single('Projects Settings')
 		self.validate_overlap_for("user", data, self.user, settings.ignore_user_time_overlap)
 		self.validate_overlap_for("employee", data, self.employee, settings.ignore_employee_time_overlap)
 
-	def validate_task_project(self):
-		for log in self.time_logs:
-			log.project = log.project or frappe.db.get_value("Task", log.task, "project")
+	def set_project(self, data):
+		data.project = data.project or frappe.db.get_value("Task", data.task, "project")
+
+	def validate_project(self, data):
+		if self.parent_project and self.parent_project != data.project:
+			frappe.throw(_("Row {0}: Project must be same as the one set in the Timesheet: {1}.").format(data.idx, self.parent_project))
 
 	def validate_overlap_for(self, fieldname, args, value, ignore_validation=False):
 		if not value or ignore_validation:
@@ -189,7 +197,7 @@
 
 	def update_cost(self):
 		for data in self.time_logs:
-			if data.activity_type or data.billable:
+			if data.activity_type or data.is_billable:
 				rate = get_activity_cost(self.employee, data.activity_type)
 				hours = data.billing_hours or 0
 				costing_hours = data.billing_hours or data.hours or 0
@@ -200,20 +208,29 @@
 					data.costing_amount = data.costing_rate * costing_hours
 
 	def update_time_rates(self, ts_detail):
-		if not ts_detail.billable:
+		if not ts_detail.is_billable:
 			ts_detail.billing_rate = 0.0
 
 @frappe.whitelist()
-def get_projectwise_timesheet_data(project, parent=None, from_time=None, to_time=None):
+def get_projectwise_timesheet_data(project=None, parent=None, from_time=None, to_time=None):
 	condition = ''
+	if project:
+		condition += "and tsd.project = %(project)s"
 	if parent:
-		condition = "AND parent = %(parent)s"
+		condition += "AND tsd.parent = %(parent)s"
 	if from_time and to_time:
-		condition += "AND CAST(from_time as DATE) BETWEEN %(from_time)s AND %(to_time)s"
+		condition += "AND CAST(tsd.from_time as DATE) BETWEEN %(from_time)s AND %(to_time)s"
 
-	return frappe.db.sql("""select name, parent, billing_hours, billing_amount as billing_amt
-			from `tabTimesheet Detail` where parenttype = 'Timesheet' and docstatus=1 and project = %(project)s {0} and billable = 1
-			and sales_invoice is null""".format(condition), {'project': project, 'parent': parent, 'from_time': from_time, 'to_time': to_time}, as_dict=1)
+	return frappe.db.sql("""SELECT tsd.name as name,
+				tsd.parent as parent, tsd.billing_hours as billing_hours,
+				tsd.billing_amount as billing_amount, tsd.activity_type as activity_type,
+				tsd.description as description, ts.currency as currency
+			FROM `tabTimesheet Detail` tsd
+			INNER JOIN `tabTimesheet` ts ON ts.name = tsd.parent
+			WHERE tsd.parenttype = 'Timesheet'
+				and tsd.docstatus=1 {0}
+				and tsd.is_billable = 1
+				and tsd.sales_invoice is null""".format(condition), {'project': project, 'parent': parent, 'from_time': from_time, 'to_time': to_time}, as_dict=1)
 
 @frappe.whitelist()
 @frappe.validate_and_sanitize_search_inputs
@@ -250,7 +267,7 @@
 	}
 
 @frappe.whitelist()
-def make_sales_invoice(source_name, item_code=None, customer=None):
+def make_sales_invoice(source_name, item_code=None, customer=None, currency=None):
 	target = frappe.new_doc("Sales Invoice")
 	timesheet = frappe.get_doc('Timesheet', source_name)
 
@@ -268,6 +285,9 @@
 	if customer:
 		target.customer = customer
 
+	if currency:
+		target.currency = currency
+
 	if item_code:
 		target.append('items', {
 			'item_code': item_code,
@@ -275,11 +295,16 @@
 			'rate': billing_rate
 		})
 
-	target.append('timesheets', {
-		'time_sheet': timesheet.name,
-		'billing_hours': hours,
-		'billing_amount': billing_amount
-	})
+	for time_log in timesheet.time_logs:
+		if time_log.is_billable:
+			target.append('timesheets', {
+				'time_sheet': timesheet.name,
+				'billing_hours': time_log.billing_hours,
+				'billing_amount': time_log.billing_amount,
+				'timesheet_detail': time_log.name,
+				'activity_type': time_log.activity_type,
+				'description': time_log.description
+			})
 
 	target.run_method("calculate_billing_amount_for_timesheet")
 	target.run_method("set_missing_values")
@@ -309,12 +334,17 @@
 	})
 
 @frappe.whitelist()
-def get_activity_cost(employee=None, activity_type=None):
+def get_activity_cost(employee=None, activity_type=None, currency=None):
+	base_currency = frappe.defaults.get_global_default('currency')
 	rate = frappe.db.get_values("Activity Cost", {"employee": employee,
 		"activity_type": activity_type}, ["costing_rate", "billing_rate"], as_dict=True)
 	if not rate:
 		rate = frappe.db.get_values("Activity Type", {"activity_type": activity_type},
 			["costing_rate", "billing_rate"], as_dict=True)
+		if rate and currency and currency!=base_currency:
+			exchange_rate = get_exchange_rate(base_currency, currency)
+			rate[0]["costing_rate"] = rate[0]["costing_rate"] * exchange_rate
+			rate[0]["billing_rate"] = rate[0]["billing_rate"] * exchange_rate
 
 	return rate[0] if rate else {}
 
diff --git a/erpnext/projects/doctype/timesheet_detail/timesheet_detail.json b/erpnext/projects/doctype/timesheet_detail/timesheet_detail.json
index a9b3bfb..ee04c61 100644
--- a/erpnext/projects/doctype/timesheet_detail/timesheet_detail.json
+++ b/erpnext/projects/doctype/timesheet_detail/timesheet_detail.json
@@ -1,979 +1,279 @@
 {
- "allow_copy": 0, 
- "allow_events_in_timeline": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2013-03-05 09:11:06", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "Document", 
- "editable_grid": 1, 
+ "actions": [],
+ "creation": "2013-03-05 09:11:06",
+ "doctype": "DocType",
+ "document_type": "Document",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "activity_type",
+  "from_time",
+  "description",
+  "section_break_3",
+  "expected_hours",
+  "to_time",
+  "hours",
+  "completed",
+  "section_break_7",
+  "completed_qty",
+  "workstation",
+  "column_break_12",
+  "operation",
+  "operation_id",
+  "project_details",
+  "project",
+  "project_name",
+  "column_break_2",
+  "task",
+  "section_break_6",
+  "is_billable",
+  "sales_invoice",
+  "column_break_8",
+  "billing_hours",
+  "section_break_11",
+  "base_billing_rate",
+  "base_billing_amount",
+  "base_costing_rate",
+  "base_costing_amount",
+  "column_break_14",
+  "billing_rate",
+  "billing_amount",
+  "costing_rate",
+  "costing_amount"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "activity_type", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Activity Type", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Activity Type", 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "activity_type",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Activity Type",
+   "options": "Activity Type"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 2, 
-   "fieldname": "from_time", 
-   "fieldtype": "Datetime", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "From Time", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "columns": 2,
+   "fieldname": "from_time",
+   "fieldtype": "Datetime",
+   "in_list_view": 1,
+   "label": "From Time"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "section_break_3", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "section_break_3",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "expected_hours", 
-   "fieldtype": "Float", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Expected Hrs", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "expected_hours",
+   "fieldtype": "Float",
+   "label": "Expected Hrs"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 1, 
-   "fieldname": "hours", 
-   "fieldtype": "Float", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Hrs", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "columns": 1,
+   "fieldname": "hours",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Hrs"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "to_time", 
-   "fieldtype": "Datetime", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "To Time", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "to_time",
+   "fieldtype": "Datetime",
+   "label": "To Time"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "0", 
-   "fieldname": "completed", 
-   "fieldtype": "Check", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Completed", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "default": "0",
+   "fieldname": "completed",
+   "fieldtype": "Check",
+   "label": "Completed"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "section_break_7", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "section_break_7",
+   "fieldtype": "Section Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "eval:parent.work_order", 
-   "fieldname": "completed_qty", 
-   "fieldtype": "Float", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Completed Qty", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "depends_on": "eval:parent.work_order",
+   "fieldname": "completed_qty",
+   "fieldtype": "Float",
+   "label": "Completed Qty"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "eval:parent.work_order", 
-   "fieldname": "workstation", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Workstation", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Workstation", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "depends_on": "eval:parent.work_order",
+   "fieldname": "workstation",
+   "fieldtype": "Link",
+   "label": "Workstation",
+   "options": "Workstation",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_12", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_12",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "eval:parent.work_order", 
-   "fieldname": "operation", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Operation", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Operation", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "depends_on": "eval:parent.work_order",
+   "fieldname": "operation",
+   "fieldtype": "Link",
+   "label": "Operation",
+   "options": "Operation",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "eval:parent.work_order", 
-   "fieldname": "operation_id", 
-   "fieldtype": "Data", 
-   "hidden": 1, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Operation Id", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "depends_on": "eval:parent.work_order",
+   "fieldname": "operation_id",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "label": "Operation Id"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "project_details", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "project_details",
+   "fieldtype": "Section Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 3, 
-   "fieldname": "project", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Project", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Project", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "columns": 3,
+   "fieldname": "project",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Project",
+   "options": "Project",
+   "read_only_depends_on": "eval: parent.parent_project"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_2", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_2",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "", 
-   "fieldname": "task", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Task", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Task", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 1, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "task",
+   "fieldtype": "Link",
+   "label": "Task",
+   "options": "Task",
+   "remember_last_selected_value": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "section_break_6", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "section_break_6",
+   "fieldtype": "Section Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 1, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 1, 
-   "depends_on": "", 
-   "fieldname": "billable", 
-   "fieldtype": "Check", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Bill", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 1, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_8",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_8", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "allow_on_submit": 1,
+   "depends_on": "is_billable",
+   "fieldname": "billing_hours",
+   "fieldtype": "Float",
+   "label": "Billing Hours",
+   "permlevel": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 1, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "billable", 
-   "fieldname": "billing_hours", 
-   "fieldtype": "Float", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Billing Hours", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 1, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "depends_on": "is_billable",
+   "fieldname": "section_break_11",
+   "fieldtype": "Section Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "billable", 
-   "fieldname": "section_break_11", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "billing_rate",
+   "fieldtype": "Currency",
+   "label": "Billing Rate",
+   "options": "currency",
+   "permlevel": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "depends_on": "", 
-   "fieldname": "billing_rate", 
-   "fieldtype": "Currency", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Billing Rate", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 1, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "allow_on_submit": 1,
+   "default": "0",
+   "fieldname": "billing_amount",
+   "fieldtype": "Currency",
+   "label": "Billing Amount",
+   "options": "currency",
+   "permlevel": 1,
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 1, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "0", 
-   "depends_on": "", 
-   "description": "", 
-   "fieldname": "billing_amount", 
-   "fieldtype": "Currency", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Billing Amount", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 1, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "column_break_14",
+   "fieldtype": "Column Break"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_break_14", 
-   "fieldtype": "Column Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "costing_rate",
+   "fieldtype": "Currency",
+   "label": "Costing Rate",
+   "options": "currency",
+   "permlevel": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "costing_rate", 
-   "fieldtype": "Currency", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Costing Rate", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 1, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "allow_on_submit": 1,
+   "default": "0",
+   "fieldname": "costing_amount",
+   "fieldtype": "Currency",
+   "label": "Costing Amount",
+   "options": "currency",
+   "permlevel": 1,
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 1, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "0", 
-   "description": "", 
-   "fieldname": "costing_amount", 
-   "fieldtype": "Currency", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Costing Amount", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 1, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "allow_on_submit": 1,
+   "fieldname": "sales_invoice",
+   "fieldtype": "Link",
+   "label": "Sales Invoice",
+   "no_copy": 1,
+   "options": "Sales Invoice",
+   "print_hide": 1,
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "reference", 
-   "fieldtype": "Section Break", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Reference", 
-   "length": 0, 
-   "no_copy": 0, 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "allow_on_submit": 1,
+   "columns": 1,
+   "default": "0",
+   "fieldname": "is_billable",
+   "fieldtype": "Check",
+   "in_list_view": 1,
+   "label": "Is Billable",
+   "print_hide": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 1, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "sales_invoice", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 0, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "Sales Invoice", 
-   "length": 0, 
-   "no_copy": 1, 
-   "options": "Sales Invoice", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 1, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
+   "fetch_from": "project.project_name",
+   "fieldname": "project_name",
+   "fieldtype": "Data",
+   "label": "Project Name",
+   "read_only": 1
+  },
+  {
+   "fieldname": "description",
+   "fieldtype": "Small Text",
+   "label": "Description"
+  },
+  {
+   "fieldname": "base_billing_rate",
+   "fieldtype": "Currency",
+   "label": "Billing Rate",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "base_billing_amount",
+   "fieldtype": "Currency",
+   "label": "Billing Amount",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "base_costing_rate",
+   "fieldtype": "Currency",
+   "label": "Costing Rate",
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "base_costing_amount",
+   "fieldtype": "Currency",
+   "label": "Costing Amount",
+   "print_hide": 1,
+   "read_only": 1
   }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 0, 
- "idx": 1, 
- "image_view": 0, 
- "in_create": 0, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 1, 
- "max_attachments": 0, 
- "modified": "2019-02-18 18:55:53.190526", 
- "modified_by": "Administrator", 
- "module": "Projects", 
- "name": "Timesheet Detail", 
- "owner": "Administrator", 
- "permissions": [], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "show_name_in_global_search": 0, 
- "sort_order": "ASC", 
- "track_changes": 0, 
- "track_seen": 0, 
- "track_views": 0
+ ],
+ "idx": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-05-18 12:19:33.205940",
+ "modified_by": "Administrator",
+ "module": "Projects",
+ "name": "Timesheet Detail",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "ASC"
 }
\ No newline at end of file
diff --git a/erpnext/projects/report/billing_summary.py b/erpnext/projects/report/billing_summary.py
index 6c3c05f..5efde41 100644
--- a/erpnext/projects/report/billing_summary.py
+++ b/erpnext/projects/report/billing_summary.py
@@ -126,7 +126,7 @@
 	timesheet_details = frappe.get_all(
 		"Timesheet Detail",
 		filters = timesheet_details_filter,
-		fields=["from_time", "to_time", "hours", "billable", "billing_hours", "billing_rate", "parent"]
+		fields=["from_time", "to_time", "hours", "is_billable", "billing_hours", "billing_rate", "parent"]
 	)
 
 	timesheet_details_map = frappe._dict()
@@ -139,7 +139,7 @@
 	precision = frappe.get_precision("Timesheet Detail", "hours")
 	activity_duration = time_diff_in_hours(end_time, start_time)
 	billing_duration = 0.0
-	if activity.billable:
+	if activity.is_billable:
 		billing_duration = activity.billing_hours
 		if activity_duration != activity.billing_hours:
 			billing_duration = activity_duration * activity.billing_hours / activity.hours
diff --git a/erpnext/projects/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.py b/erpnext/projects/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.py
index 842fd4d..4d22f46 100644
--- a/erpnext/projects/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.py
+++ b/erpnext/projects/report/employee_hours_utilization_based_on_timesheet/employee_hours_utilization_based_on_timesheet.py
@@ -140,7 +140,7 @@
 					additional_filters += f"AND tt.{field} = '{self.filters.get(field)}'"
 
 		self.filtered_time_logs = frappe.db.sql('''
-			SELECT tt.employee AS employee, ttd.hours AS hours, ttd.billable AS billable, ttd.project AS project
+			SELECT tt.employee AS employee, ttd.hours AS hours, ttd.is_billable AS is_billable, ttd.project AS project
 			FROM `tabTimesheet Detail` AS ttd
 			JOIN `tabTimesheet` AS tt
 				ON ttd.parent = tt.name
@@ -153,14 +153,14 @@
 	def generate_stats_by_employee(self):
 		self.stats_by_employee = frappe._dict()
 
-		for emp, hours, billable, project in self.filtered_time_logs:
+		for emp, hours, is_billable, project in self.filtered_time_logs:
 			self.stats_by_employee.setdefault(
 				emp, frappe._dict()
 			).setdefault('billed_hours', 0.0)
 
 			self.stats_by_employee[emp].setdefault('non_billed_hours', 0.0)
 
-			if billable:
+			if is_billable:
 				self.stats_by_employee[emp]['billed_hours'] += flt(hours, 2)
 			else:
 				self.stats_by_employee[emp]['non_billed_hours'] += flt(hours, 2)
diff --git a/erpnext/projects/report/employee_hours_utilization_based_on_timesheet/test_employee_util.py b/erpnext/projects/report/employee_hours_utilization_based_on_timesheet/test_employee_util.py
index fa87827..0e5a597 100644
--- a/erpnext/projects/report/employee_hours_utilization_based_on_timesheet/test_employee_util.py
+++ b/erpnext/projects/report/employee_hours_utilization_based_on_timesheet/test_employee_util.py
@@ -31,7 +31,7 @@
         timesheet1.append("time_logs", {
             "activity_type": get_random("Activity Type"),
             "hours": 5,
-            "billable": 1,
+            "is_billable": 1,
             "from_time": '2021-04-01 13:30:00.000000',
             "to_time": '2021-04-01 18:30:00.000000'
         })
@@ -46,7 +46,7 @@
         timesheet2.append("time_logs", {
             "activity_type": get_random("Activity Type"),
             "hours": 10,
-            "billable": 0,
+            "is_billable": 0,
             "from_time": '2021-04-01 13:30:00.000000',
             "to_time": '2021-04-01 23:30:00.000000',
             "project": cls.test_project.name
diff --git a/erpnext/projects/report/project_profitability/test_project_profitability.py b/erpnext/projects/report/project_profitability/test_project_profitability.py
index 7fe28b1..f2b9c25 100644
--- a/erpnext/projects/report/project_profitability/test_project_profitability.py
+++ b/erpnext/projects/report/project_profitability/test_project_profitability.py
@@ -14,7 +14,7 @@
 		if not frappe.db.exists('Salary Component', 'Timesheet Component'):
 			frappe.get_doc({'doctype': 'Salary Component', 'salary_component': 'Timesheet Component'}).insert()
 		make_salary_structure_for_timesheet(emp, company='_Test Company')
-		self.timesheet = make_timesheet(emp, simulate = True, billable=1)
+		self.timesheet = make_timesheet(emp, simulate = True, is_billable=1)
 		self.salary_slip = make_salary_slip(self.timesheet.name)
 		self.salary_slip.submit()
 		self.sales_invoice = make_sales_invoice(self.timesheet.name, '_Test Item', '_Test Customer')
diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js
index cdfd909..e7dcd41 100644
--- a/erpnext/public/js/controllers/buying.js
+++ b/erpnext/public/js/controllers/buying.js
@@ -84,13 +84,13 @@
 			if (me.frm.doc.is_subcontracted == "Yes") {
 				return{
 					query: "erpnext.controllers.queries.item_query",
-					filters:{ 'is_sub_contracted_item': 1 }
+					filters:{ 'supplier': me.frm.doc.supplier, 'is_sub_contracted_item': 1 }
 				}
 			}
 			else {
 				return{
 					query: "erpnext.controllers.queries.item_query",
-					filters: {'is_purchase_item': 1}
+					filters: { 'supplier': me.frm.doc.supplier, 'is_purchase_item': 1 }
 				}
 			}
 		});
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index 472746a..ce40ced 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -724,6 +724,18 @@
 	}
 }
 
+frappe.form.link_formatters['Project'] = function(value, doc) {
+	if (doc && value && doc.project_name && doc.project_name !== value && doc.project === value) {
+		return value + ': ' + doc.project_name;
+	} else if (!value && doc.doctype && doc.project_name) {
+		// format blank value in child table
+		return doc.project;
+	} else {
+		// if value is blank in report view or project name and name are the same, return as is
+		return value;
+	}
+};
+
 // add description on posting time
 $(document).on('app_ready', function() {
 	if(!frappe.datetime.is_timezone_same()) {
diff --git a/erpnext/regional/address_template/templates/united_states.html b/erpnext/regional/address_template/templates/united_states.html
index 089315e..77fce46 100644
--- a/erpnext/regional/address_template/templates/united_states.html
+++ b/erpnext/regional/address_template/templates/united_states.html
@@ -1,4 +1,4 @@
 {{ address_line1 }}<br>
 {% if address_line2 %}{{ address_line2 }}<br>{% endif -%}
 {{ city }}, {% if state %}{{ state }}{% endif -%}{% if pincode %} {{ pincode }}<br>{% endif -%}
-{% if country != "United States" %}{{ country|upper }}{% endif -%}
+{% if country != "United States" %}{{ country }}{% endif -%}
diff --git a/erpnext/regional/germany/utils/datev/datev_csv.py b/erpnext/regional/germany/utils/datev/datev_csv.py
index 826d51f..122c15f 100644
--- a/erpnext/regional/germany/utils/datev/datev_csv.py
+++ b/erpnext/regional/germany/utils/datev/datev_csv.py
@@ -55,8 +55,7 @@
 		quoting=QUOTE_NONNUMERIC
 	)
 
-	if not six.PY2:
-		data = data.encode('latin_1', errors='replace')
+	data = data.encode('latin_1', errors='replace')
 
 	header = get_header(filters, csv_class)
 	header = ';'.join(header).encode('latin_1', errors='replace')
diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py
index b4e7a88..843fb01 100644
--- a/erpnext/regional/india/e_invoice/utils.py
+++ b/erpnext/regional/india/e_invoice/utils.py
@@ -43,8 +43,9 @@
 	invalid_supply_type = doc.get('gst_category') not in ['Registered Regular', 'SEZ', 'Overseas', 'Deemed Export']
 	company_transaction = doc.get('billing_address_gstin') == doc.get('company_gstin')
 	no_taxes_applied = not doc.get('taxes')
+	has_non_gst_item = any(d for d in doc.get('items', []) if d.get('is_non_gst'))
 
-	if invalid_company or invalid_supply_type or company_transaction or no_taxes_applied:
+	if invalid_company or invalid_supply_type or company_transaction or no_taxes_applied or has_non_gst_item:
 		return False
 
 	return True
@@ -533,11 +534,9 @@
 	return einvoice
 
 def safe_json_load(json_string):
-	JSONDecodeError = ValueError if six.PY2 else json.JSONDecodeError
-
 	try:
 		return json.loads(json_string)
-	except JSONDecodeError as e:
+	except json.JSONDecodeError as e:
 		# print a snippet of 40 characters around the location where error occured
 		pos = e.pos
 		start, end = max(0, pos-20), min(len(json_string)-1, pos+20)
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index ca679e4..fc227de 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -695,7 +695,7 @@
 		filters=filters,
 		fields=["cgst_account", "sgst_account", "igst_account", "cess_account"])
 
-	if not gst_settings_accounts and not frappe.flags.in_test:
+	if not gst_settings_accounts and not frappe.flags.in_test and not frappe.flags.in_migrate:
 		frappe.throw(_("Please set GST Accounts in GST Settings"))
 
 	for d in gst_settings_accounts:
diff --git a/erpnext/stock/dashboard/item_dashboard.js b/erpnext/stock/dashboard/item_dashboard.js
index 933ca8a..a657ecf 100644
--- a/erpnext/stock/dashboard/item_dashboard.js
+++ b/erpnext/stock/dashboard/item_dashboard.js
@@ -268,7 +268,9 @@
 		frappe.call({
 			method: 'erpnext.stock.doctype.stock_entry.stock_entry_utils.make_stock_entry',
 			args: values,
+			btn: dialog.get_primary_btn(),
 			freeze: true,
+			freeze_message: __('Creating Stock Entry'),
 			callback: function (r) {
 				frappe.show_alert(__('Stock Entry {0} created',
 					['<a href="/app/stock-entry/' + r.message.name + '">' + r.message.name + '</a>']));
diff --git a/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json b/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json
index 4fcdb4c..9c59c13 100644
--- a/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json
+++ b/erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json
@@ -10,8 +10,8 @@
   "exchange_rate",
   "description",
   "col_break3",
-  "base_amount",
-  "amount"
+  "amount",
+  "base_amount"
  ],
  "fields": [
   {
@@ -59,7 +59,7 @@
   {
    "fieldname": "base_amount",
    "fieldtype": "Currency",
-   "label": "Base Amount",
+   "label": "Amount (Company Currency)",
    "options": "Company:company:default_currency",
    "read_only": 1
   }
@@ -67,7 +67,7 @@
  "index_web_pages_for_search": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-12-26 01:07:23.233604",
+ "modified": "2021-05-17 13:57:10.807980",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Landed Cost Taxes and Charges",
diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
index 27b8729..5b626ea 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
@@ -4,8 +4,9 @@
 
 from __future__ import unicode_literals
 import frappe, erpnext
+from rq.timeouts import JobTimeoutException
 from frappe.model.document import Document
-from frappe.utils import cint, get_link_to_form, add_to_date, now, today
+from frappe.utils import cint, get_link_to_form, add_to_date, now, today, time_diff_in_hours
 from erpnext.stock.stock_ledger import repost_future_sle
 from erpnext.accounts.utils import update_gl_entries_after, check_if_stock_and_account_balance_synced
 from frappe.utils.user import get_users_with_role
@@ -57,7 +58,8 @@
 		repost_gl_entries(doc)
 
 		doc.set_status('Completed')
-	except Exception:
+
+	except (Exception, JobTimeoutException):
 		frappe.db.rollback()
 		traceback = frappe.get_traceback()
 		frappe.log_error(traceback)
@@ -113,6 +115,12 @@
 	frappe.sendmail(recipients=recipients, subject=subject, message=message)
 
 def repost_entries():
+	job_log = frappe.get_all('Scheduled Job Log', fields = ['status', 'creation'],
+		filters = {'scheduled_job_type': 'repost_item_valuation.repost_entries'}, order_by='creation desc', limit=1)
+
+	if job_log and job_log[0]['status'] == 'Start' and time_diff_in_hours(now(), job_log[0]['creation']) < 2:
+		return
+
 	riv_entries = get_repost_item_valuation_entries()
 
 	for row in riv_entries:
diff --git a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
index 85c7ebe..6bbba05 100644
--- a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
+++ b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
@@ -1,4 +1,5 @@
 {
+ "actions": [],
  "creation": "2015-02-17 01:06:05.072764",
  "doctype": "DocType",
  "document_type": "Other",
@@ -170,6 +171,7 @@
   },
   {
    "default": "0",
+   "depends_on": "allow_zero_valuation_rate",
    "fieldname": "allow_zero_valuation_rate",
    "fieldtype": "Check",
    "label": "Allow Zero Valuation Rate",
@@ -179,7 +181,7 @@
  ],
  "istable": 1,
  "links": [],
- "modified": "2021-03-23 11:09:44.407157",
+ "modified": "2021-05-21 12:13:33.041266",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Reconciliation Item",
@@ -189,4 +191,4 @@
  "sort_field": "modified",
  "sort_order": "DESC",
  "track_changes": 1
-}
+}
\ No newline at end of file
diff --git a/erpnext/stock/report/item_variant_details/item_variant_details.py b/erpnext/stock/report/item_variant_details/item_variant_details.py
index e8449cc..d8563d7 100644
--- a/erpnext/stock/report/item_variant_details/item_variant_details.py
+++ b/erpnext/stock/report/item_variant_details/item_variant_details.py
@@ -14,47 +14,58 @@
 	if not item:
 		return []
 	item_dicts = []
-	variants = None
 
-	variant_results = frappe.db.sql("""select name from `tabItem`
-		where variant_of = %s""", item, as_dict=1)
+	variant_results = frappe.db.get_all(
+		"Item",
+		fields=["name"],
+		filters={
+			"variant_of": ["=", item],
+			"disabled": 0
+		}
+	)
+
 	if not variant_results:
-		frappe.msgprint(_("There isn't any item variant for the selected item"))
+		frappe.msgprint(_("There aren't any item variants for the selected item"))
 		return []
 	else:
-		variants = ", ".join([frappe.db.escape(variant['name']) for variant in variant_results])
+		variant_list = [variant['name'] for variant in variant_results]
 
-	order_count_map = get_open_sales_orders_map(variants)
-	stock_details_map = get_stock_details_map(variants)
-	buying_price_map = get_buying_price_map(variants)
-	selling_price_map = get_selling_price_map(variants)
-	attr_val_map = get_attribute_values_map(variants)
+	order_count_map = get_open_sales_orders_count(variant_list)
+	stock_details_map = get_stock_details_map(variant_list)
+	buying_price_map = get_buying_price_map(variant_list)
+	selling_price_map = get_selling_price_map(variant_list)
+	attr_val_map = get_attribute_values_map(variant_list)
 
-	attribute_list = [d[0] for d in frappe.db.sql("""select attribute
-		from `tabItem Variant Attribute`
-		where parent in ({variants}) group by attribute""".format(variants=variants))]
+	attributes = frappe.db.get_all(
+		"Item Variant Attribute",
+		fields=["attribute"],
+		filters={
+			"parent": ["in", variant_list]
+		},
+		group_by="attribute"
+	)
+	attribute_list = [row.get("attribute") for row in attributes]
 
 	# Prepare dicts
 	variant_dicts = [{"variant_name": d['name']} for d in variant_results]
 	for item_dict in variant_dicts:
-		name = item_dict["variant_name"]
+		name = item_dict.get("variant_name")
 
-		for d in attribute_list:
-			attr_dict = attr_val_map[name]
-			if attr_dict and attr_dict.get(d):
-				item_dict[d] = attr_val_map[name][d]
+		for attribute in attribute_list:
+			attr_dict = attr_val_map.get(name)
+			if attr_dict and attr_dict.get(attribute):
+				item_dict[frappe.scrub(attribute)] = attr_val_map.get(name).get(attribute)
 
-		item_dict["Open Orders"] = order_count_map.get(name) or 0
+		item_dict["open_orders"] = order_count_map.get(name) or 0
 
 		if stock_details_map.get(name):
-			item_dict["Inventory"] = stock_details_map.get(name)["Inventory"] or 0
-			item_dict["In Production"] = stock_details_map.get(name)["In Production"] or 0
-			item_dict["Available Selling"] = stock_details_map.get(name)["Available Selling"] or 0
+			item_dict["current_stock"] = stock_details_map.get(name)["Inventory"] or 0
+			item_dict["in_production"] = stock_details_map.get(name)["In Production"] or 0
 		else:
-			item_dict["Inventory"] = item_dict["In Production"] = item_dict["Available Selling"] = 0
+			item_dict["current_stock"] = item_dict["in_production"] = 0
 
-		item_dict["Avg. Buying Price List Rate"] = buying_price_map.get(name) or 0
-		item_dict["Avg. Selling Price List Rate"] = selling_price_map.get(name) or 0
+		item_dict["avg_buying_price_list_rate"] = buying_price_map.get(name) or 0
+		item_dict["avg_selling_price_list_rate"] = selling_price_map.get(name) or 0
 
 		item_dicts.append(item_dict)
 
@@ -71,117 +82,158 @@
 
 	item_doc = frappe.get_doc("Item", item)
 
-	for d in item_doc.attributes:
-		columns.append(d.attribute + ":Data:100")
+	for entry in item_doc.attributes:
+		columns.append({
+			"fieldname": frappe.scrub(entry.attribute),
+			"label": entry.attribute,
+			"fieldtype": "Data",
+			"width": 100
+		})
 
-	columns += [_("Avg. Buying Price List Rate") + ":Currency:110", _("Avg. Selling Price List Rate") + ":Currency:110",
-		_("Inventory") + ":Float:100", _("In Production") + ":Float:100",
-		_("Open Orders") + ":Float:100", _("Available Selling") + ":Float:100"
+	additional_columns = [
+		{
+			"fieldname": "avg_buying_price_list_rate",
+			"label": _("Avg. Buying Price List Rate"),
+			"fieldtype": "Currency",
+			"width": 150
+		},
+		{
+			"fieldname": "avg_selling_price_list_rate",
+			"label": _("Avg. Selling Price List Rate"),
+			"fieldtype": "Currency",
+			"width": 150
+		},
+		{
+			"fieldname": "current_stock",
+			"label": _("Current Stock"),
+			"fieldtype": "Float",
+			"width": 120
+		},
+		{
+			"fieldname": "in_production",
+			"label": _("In Production"),
+			"fieldtype": "Float",
+			"width": 150
+		},
+		{
+			"fieldname": "open_orders",
+			"label": _("Open Sales Orders"),
+			"fieldtype": "Float",
+			"width": 150
+		}
 	]
+	columns.extend(additional_columns)
 
 	return columns
 
-def get_open_sales_orders_map(variants):
-	open_sales_orders = frappe.db.sql("""
-		select
-			count(*) as count,
-			item_code
-		from
-			`tabSales Order Item`
-		where
-			docstatus = 1 and
-			qty > ifnull(delivered_qty, 0) and
-			item_code in ({variants})
-		group by
-			item_code
-	""".format(variants=variants), as_dict=1)
+def get_open_sales_orders_count(variants_list):
+	open_sales_orders = frappe.db.get_list(
+		"Sales Order",
+		fields=[
+			"name",
+			"`tabSales Order Item`.item_code"
+		],
+		filters=[
+			["Sales Order", "docstatus", "=", 1],
+			["Sales Order Item", "item_code", "in", variants_list]
+		],
+		distinct=1
+	)
 
 	order_count_map = {}
-	for d in open_sales_orders:
-		order_count_map[d["item_code"]] = d["count"]
+	for row in open_sales_orders:
+		item_code = row.get("item_code")
+		if order_count_map.get(item_code) is None:
+			order_count_map[item_code] = 1
+		else:
+			order_count_map[item_code] += 1
 
 	return order_count_map
 
-def get_stock_details_map(variants):
-	stock_details = frappe.db.sql("""
-		select
-			sum(planned_qty) as planned_qty,
-			sum(actual_qty) as actual_qty,
-			sum(projected_qty) as projected_qty,
-			item_code
-		from
-			`tabBin`
-		where
-			item_code in ({variants})
-		group by
-			item_code
-	""".format(variants=variants), as_dict=1)
+def get_stock_details_map(variant_list):
+	stock_details = frappe.db.get_all(
+		"Bin",
+		fields=[
+			"sum(planned_qty) as planned_qty",
+			"sum(actual_qty) as actual_qty",
+			"sum(projected_qty) as projected_qty",
+			"item_code",
+		],
+		filters={
+			"item_code": ["in", variant_list]
+		},
+		group_by="item_code"
+	)
 
 	stock_details_map = {}
-	for d in stock_details:
-		name = d["item_code"]
+	for row in stock_details:
+		name = row.get("item_code")
 		stock_details_map[name] = {
-			"Inventory" :d["actual_qty"],
-			"In Production" :d["planned_qty"],
-			"Available Selling" :d["projected_qty"]
+			"Inventory": row.get("actual_qty"),
+			"In Production": row.get("planned_qty")
 		}
 
 	return stock_details_map
 
-def get_buying_price_map(variants):
-	buying = frappe.db.sql("""
-		select
-			avg(price_list_rate) as avg_rate,
-			item_code
-		from
-			`tabItem Price`
-		where
-			item_code in ({variants}) and buying=1
-		group by
-			item_code
-		""".format(variants=variants), as_dict=1)
+def get_buying_price_map(variant_list):
+	buying = frappe.db.get_all(
+		"Item Price",
+		fields=[
+			"avg(price_list_rate) as avg_rate",
+			"item_code",
+		],
+		filters={
+			"item_code": ["in", variant_list],
+			"buying": 1
+		},
+		group_by="item_code"
+	)
 
 	buying_price_map = {}
-	for d in buying:
-		buying_price_map[d["item_code"]] = d["avg_rate"]
+	for row in buying:
+		buying_price_map[row.get("item_code")] = row.get("avg_rate")
 
 	return buying_price_map
 
-def get_selling_price_map(variants):
-	selling = frappe.db.sql("""
-		select
-			avg(price_list_rate) as avg_rate,
-			item_code
-		from
-			`tabItem Price`
-		where
-			item_code in ({variants}) and selling=1
-		group by
-			item_code
-		""".format(variants=variants), as_dict=1)
+def get_selling_price_map(variant_list):
+	selling = frappe.db.get_all(
+		"Item Price",
+		fields=[
+			"avg(price_list_rate) as avg_rate",
+			"item_code",
+		],
+		filters={
+			"item_code": ["in", variant_list],
+			"selling": 1
+		},
+		group_by="item_code"
+	)
 
 	selling_price_map = {}
-	for d in selling:
-		selling_price_map[d["item_code"]] = d["avg_rate"]
+	for row in selling:
+		selling_price_map[row.get("item_code")] = row.get("avg_rate")
 
 	return selling_price_map
 
-def get_attribute_values_map(variants):
-	list_attr = frappe.db.sql("""
-		select
-			attribute, attribute_value, parent
-		from
-			`tabItem Variant Attribute`
-		where
-			parent in ({variants})
-		""".format(variants=variants), as_dict=1)
+def get_attribute_values_map(variant_list):
+	attribute_list = frappe.db.get_all(
+		"Item Variant Attribute",
+		fields=[
+			"attribute",
+			"attribute_value",
+			"parent"
+		],
+		filters={
+			"parent": ["in", variant_list]
+		}
+	)
 
 	attr_val_map = {}
-	for d in list_attr:
-		name = d["parent"]
+	for row in attribute_list:
+		name = row.get("parent")
 		if not attr_val_map.get(name):
 			attr_val_map[name] = {}
 
-		attr_val_map[name][d["attribute"]] = d["attribute_value"]
+		attr_val_map[name][row.get("attribute")] = row.get("attribute_value")
 
 	return attr_val_map
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 9729987..b2825fc 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -232,7 +232,8 @@
 				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""", args, as_dict=1)
+			limit 1
+			for update""", args, as_dict=1)
 
 		return sle[0] if sle else frappe._dict()
 
@@ -623,7 +624,7 @@
 							break
 
 					# If no entry found with outgoing rate, collapse stack
-					if index == None:
+					if index is None:  # nosemgrep
 						new_stock_value = sum((d[0]*d[1] for d in self.wh_data.stock_queue)) - qty_to_pop*outgoing_rate
 						new_stock_qty = sum((d[0] for d in self.wh_data.stock_queue)) - qty_to_pop
 						self.wh_data.stock_queue = [[new_stock_qty, new_stock_value/new_stock_qty if new_stock_qty > 0 else outgoing_rate]]
diff --git a/requirements.txt b/requirements.txt
index f1ffeb8..32da48e 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -10,4 +10,3 @@
 taxjar~=1.9.2
 tweepy~=3.10.0
 Unidecode~=1.2.0
-WooCommerce~=3.0.0