Merge pull request #25777 from Anuja-pawar/fix-process-SOA-format

fix: Process Statement of Accounts formatting
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/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..08e072e 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
@@ -42,8 +42,9 @@
 				if return_against_status != "Consolidated":
 					# if return entry is not getting merged in the current pos closing and if it is not consolidated
 					bold_unconsolidated = frappe.bold("not Consolidated")
-					msg = (_("Row #{}: Original Invoice {} of return invoice {} is {}. ")
+					msg = (_("Row #{}: Original Invoice {} of return invoice {} is {}.")
 								.format(d.idx, bold_return_against, bold_pos_invoice, bold_unconsolidated))
+					msg += " "
 					msg += _("Original invoice should be consolidated before or along with the return invoice.")
 					msg += "<br><br>"
 					msg += _("You can add original invoice {} manually to proceed.").format(bold_return_against)
@@ -56,12 +57,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)
@@ -274,9 +275,9 @@
 			closing_entry.db_set('error_message', '')
 			closing_entry.update_opening_entry()
 
-	except Exception:
+	except Exception as e:
 		frappe.db.rollback()
-		message_log = frappe.message_log.pop()
+		message_log = frappe.message_log.pop() if frappe.message_log else str(e)
 		error_message = safe_load_json(message_log)
 
 		if closing_entry:
@@ -300,9 +301,9 @@
 			closing_entry.db_set('error_message', '')
 			closing_entry.update_opening_entry(for_cancel=True)
 
-	except Exception:
+	except Exception as e:
 		frappe.db.rollback()
-		message_log = frappe.message_log.pop()
+		message_log = frappe.message_log.pop() if frappe.message_log else str(e)
 		error_message = safe_load_json(message_log)
 
 		if closing_entry:
@@ -348,11 +349,9 @@
 		return True
 
 def safe_load_json(message):
-	JSONDecodeError = ValueError if six.PY2 else json.JSONDecodeError
-
 	try:
 		json_message = json.loads(message).get('message')
-	except JSONDecodeError:
+	except Exception:
 		json_message = message
 
 	return json_message
\ No newline at end of file
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/projects/report/project_profitability/test_project_profitability.py b/erpnext/projects/report/project_profitability/test_project_profitability.py
index f2b9c25..ea6bdb5 100644
--- a/erpnext/projects/report/project_profitability/test_project_profitability.py
+++ b/erpnext/projects/report/project_profitability/test_project_profitability.py
@@ -8,7 +8,7 @@
 from erpnext.projects.report.project_profitability.project_profitability import execute
 
 class TestProjectProfitability(unittest.TestCase):
-	@classmethod
+
 	def setUp(self):
 		emp = make_employee('test_employee_9@salary.com', company='_Test Company')
 		if not frappe.db.exists('Salary Component', 'Timesheet Component'):
@@ -21,7 +21,7 @@
 		self.sales_invoice.due_date = nowdate()
 		self.sales_invoice.submit()
 
-		frappe.db.set_value("HR Settings", "HR Settings", "standard_working_hours", 8)
+		frappe.db.set_value('HR Settings', None, 'standard_working_hours', 8)
 
 	def test_project_profitability(self):
 		filters = {
@@ -55,4 +55,4 @@
 	def tearDown(self):
 		frappe.get_doc("Sales Invoice", self.sales_invoice.name).cancel()
 		frappe.get_doc("Salary Slip", self.salary_slip.name).cancel()
-		frappe.get_doc("Timesheet", self.timesheet.name).cancel()
\ No newline at end of file
+		frappe.get_doc("Timesheet", self.timesheet.name).cancel()
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/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 70e6d07..843fb01 100644
--- a/erpnext/regional/india/e_invoice/utils.py
+++ b/erpnext/regional/india/e_invoice/utils.py
@@ -43,7 +43,7 @@
 	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'))
+	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 or has_non_gst_item:
 		return False
@@ -534,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/setup/doctype/global_defaults/global_defaults.py b/erpnext/setup/doctype/global_defaults/global_defaults.py
index e587217..a0ba1ef 100644
--- a/erpnext/setup/doctype/global_defaults/global_defaults.py
+++ b/erpnext/setup/doctype/global_defaults/global_defaults.py
@@ -59,13 +59,15 @@
 
 		# Make property setters to hide rounded total fields
 		for doctype in ("Quotation", "Sales Order", "Sales Invoice", "Delivery Note",
-			"Supplier Quotation", "Purchase Order", "Purchase Invoice"):
+			"Supplier Quotation", "Purchase Order", "Purchase Invoice", "Purchase Receipt"):
 			make_property_setter(doctype, "base_rounded_total", "hidden", self.disable_rounded_total, "Check", validate_fields_for_doctype=False)
 			make_property_setter(doctype, "base_rounded_total", "print_hide", 1, "Check", validate_fields_for_doctype=False)
 
 			make_property_setter(doctype, "rounded_total", "hidden", self.disable_rounded_total, "Check", validate_fields_for_doctype=False)
 			make_property_setter(doctype, "rounded_total", "print_hide", self.disable_rounded_total, "Check", validate_fields_for_doctype=False)
 
+			make_property_setter(doctype, "disable_rounded_total", "default", cint(self.disable_rounded_total), "Text", validate_fields_for_doctype=False)
+
 	def toggle_in_words(self):
 		self.disable_in_words = cint(self.disable_in_words)
 
diff --git a/erpnext/stock/dashboard/item_dashboard.js b/erpnext/stock/dashboard/item_dashboard.js
index db05739..a657ecf 100644
--- a/erpnext/stock/dashboard/item_dashboard.js
+++ b/erpnext/stock/dashboard/item_dashboard.js
@@ -230,7 +230,6 @@
 		},
 		],
 	});
-	var submitted = false;
 	dialog.show();
 	dialog.get_field('item_code').set_input(item);
 
@@ -254,7 +253,6 @@
 	}
 
 	dialog.set_primary_action(__('Submit'), function () {
-		if(submitted) return;
 		var values = dialog.get_values();
 		if (!values) {
 			return;
@@ -267,11 +265,12 @@
 			frappe.msgprint(__('Source and target warehouse must be different'));
 		}
 
-		submitted = true;
 		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/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index daa1e51..de23e76 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -600,7 +600,6 @@
 	add_to_transit: function(frm) {
 		if(frm.doc.add_to_transit && frm.doc.purpose=='Material Transfer') {
 			frm.set_value('to_warehouse', '');
-			frm.set_value('stock_entry_type', 'Material Transfer');
 			frm.fields_dict.to_warehouse.get_query = function() {
 				return {
 					filters:{
@@ -610,12 +609,13 @@
 					}
 				};
 			};
-			frm.trigger('set_tansit_warehouse');
+			frm.trigger('set_transit_warehouse');
 		}
 	},
 
-	set_tansit_warehouse: function(frm) {
-		if(frm.doc.add_to_transit && frm.doc.purpose == 'Material Transfer' && !frm.doc.to_warehouse) {
+	set_transit_warehouse: function(frm) {
+		if(frm.doc.add_to_transit && frm.doc.purpose == 'Material Transfer' && !frm.doc.to_warehouse
+			&& frm.doc.from_warehouse) {
 			let dt = frm.doc.from_warehouse ? 'Warehouse' : 'Company';
 			let dn = frm.doc.from_warehouse ? frm.doc.from_warehouse : frm.doc.company;
 			frappe.db.get_value(dt, dn, 'default_in_transit_warehouse', (r) => {
@@ -985,7 +985,7 @@
 	},
 
 	from_warehouse: function(doc) {
-		this.frm.trigger('set_tansit_warehouse');
+		this.frm.trigger('set_transit_warehouse');
 		this.set_warehouse_in_children(doc.items, "s_warehouse", doc.from_warehouse);
 	},
 
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json
index 98c047a..7f94591 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.json
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.json
@@ -637,6 +637,8 @@
   {
    "default": "0",
    "depends_on": "eval: doc.purpose=='Material Transfer' && !doc.outgoing_stock_entry",
+   "fetch_from": "stock_entry_type.add_to_transit",
+   "fetch_if_empty": 1,
    "fieldname": "add_to_transit",
    "fieldtype": "Check",
    "label": "Add to Transit",
@@ -655,7 +657,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2020-12-09 14:58:13.267321",
+ "modified": "2021-05-21 11:29:11.917161",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Entry",
diff --git a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.json b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.json
index 0f2b55e..eee38be 100644
--- a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.json
+++ b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.json
@@ -6,7 +6,8 @@
  "editable_grid": 1,
  "engine": "InnoDB",
  "field_order": [
-  "purpose"
+  "purpose",
+  "add_to_transit"
  ],
  "fields": [
   {
@@ -18,10 +19,17 @@
    "options": "\nMaterial Issue\nMaterial Receipt\nMaterial Transfer\nMaterial Transfer for Manufacture\nMaterial Consumption for Manufacture\nManufacture\nRepack\nSend to Subcontractor",
    "reqd": 1,
    "set_only_once": 1
+  },
+  {
+   "default": "0",
+   "depends_on": "eval: doc.purpose == 'Material Transfer'",
+   "fieldname": "add_to_transit",
+   "fieldtype": "Check",
+   "label": "Add to Transit"
   }
  ],
  "links": [],
- "modified": "2020-08-10 23:24:37.160817",
+ "modified": "2021-05-21 11:27:01.144110",
  "modified_by": "Administrator",
  "module": "Stock",
  "name": "Stock Entry Type",
diff --git a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py
index a4116ab..1069ec8 100644
--- a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py
+++ b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.py
@@ -7,4 +7,6 @@
 from frappe.model.document import Document
 
 class StockEntryType(Document):
-	pass
+	def validate(self):
+		if self.add_to_transit and self.purpose != 'Material Transfer':
+			self.add_to_transit = 0
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