Merge branch 'develop' of https://github.com/frappe/erpnext into lead_details_qb
diff --git a/erpnext/crm/report/lead_details/lead_details.py b/erpnext/crm/report/lead_details/lead_details.py
index 7b8c43b..98dfbec 100644
--- a/erpnext/crm/report/lead_details/lead_details.py
+++ b/erpnext/crm/report/lead_details/lead_details.py
@@ -4,6 +4,7 @@
 
 import frappe
 from frappe import _
+from frappe.query_builder.functions import Concat_ws, Date
 
 
 def execute(filters=None):
@@ -69,53 +70,41 @@
 
 
 def get_data(filters):
-	return frappe.db.sql(
-		"""
-		SELECT
-			`tabLead`.name,
-			`tabLead`.lead_name,
-			`tabLead`.status,
-			`tabLead`.lead_owner,
-			`tabLead`.territory,
-			`tabLead`.source,
-			`tabLead`.email_id,
-			`tabLead`.mobile_no,
-			`tabLead`.phone,
-			`tabLead`.owner,
-			`tabLead`.company,
-			concat_ws(', ',
-				trim(',' from `tabAddress`.address_line1),
-				trim(',' from tabAddress.address_line2)
-			) AS address,
-			`tabAddress`.state,
-			`tabAddress`.pincode,
-			`tabAddress`.country
-		FROM
-			`tabLead` left join `tabDynamic Link` on (
-			`tabLead`.name = `tabDynamic Link`.link_name and
-			`tabDynamic Link`.parenttype = 'Address')
-			left join `tabAddress` on (
-			`tabAddress`.name=`tabDynamic Link`.parent)
-		WHERE
-			company = %(company)s
-			AND DATE(`tabLead`.creation) BETWEEN %(from_date)s AND %(to_date)s
-			{conditions}
-		ORDER BY
-			`tabLead`.creation asc """.format(
-			conditions=get_conditions(filters)
-		),
-		filters,
-		as_dict=1,
+	lead = frappe.qb.DocType("Lead")
+	address = frappe.qb.DocType("Address")
+	dynamic_link = frappe.qb.DocType("Dynamic Link")
+
+	query = (
+		frappe.qb.from_(lead)
+		.left_join(dynamic_link)
+		.on((lead.name == dynamic_link.link_name) & (dynamic_link.parenttype == "Address"))
+		.left_join(address)
+		.on(address.name == dynamic_link.parent)
+		.select(
+			lead.name,
+			lead.lead_name,
+			lead.status,
+			lead.lead_owner,
+			lead.territory,
+			lead.source,
+			lead.email_id,
+			lead.mobile_no,
+			lead.phone,
+			lead.owner,
+			lead.company,
+			(Concat_ws(", ", address.address_line1, address.address_line2)).as_("address"),
+			address.state,
+			address.pincode,
+			address.country,
+		)
+		.where(lead.company == filters.company)
+		.where(Date(lead.creation).between(filters.from_date, filters.to_date))
 	)
 
-
-def get_conditions(filters):
-	conditions = []
-
 	if filters.get("territory"):
-		conditions.append(" and `tabLead`.territory=%(territory)s")
+		query = query.where(lead.territory == filters.get("territory"))
 
 	if filters.get("status"):
-		conditions.append(" and `tabLead`.status=%(status)s")
+		query = query.where(lead.status == filters.get("status"))
 
-	return " ".join(conditions) if conditions else ""
+	return query.run(as_dict=1)
diff --git a/erpnext/e_commerce/shopping_cart/test_shopping_cart.py b/erpnext/e_commerce/shopping_cart/test_shopping_cart.py
index 951039d..8210f97 100644
--- a/erpnext/e_commerce/shopping_cart/test_shopping_cart.py
+++ b/erpnext/e_commerce/shopping_cart/test_shopping_cart.py
@@ -17,7 +17,6 @@
 	request_for_quotation,
 	update_cart,
 )
-from erpnext.tests.utils import create_test_contact_and_address
 
 
 class TestShoppingCart(unittest.TestCase):
@@ -28,7 +27,6 @@
 
 	def setUp(self):
 		frappe.set_user("Administrator")
-		create_test_contact_and_address()
 		self.enable_shopping_cart()
 		if not frappe.db.exists("Website Item", {"item_code": "_Test Item"}):
 			make_website_item(frappe.get_cached_doc("Item", "_Test Item"))
@@ -46,48 +44,57 @@
 		frappe.db.sql("delete from `tabTax Rule`")
 
 	def test_get_cart_new_user(self):
-		self.login_as_new_user()
-
+		self.login_as_customer(
+			"test_contact_two_customer@example.com", "_Test Contact 2 For _Test Customer"
+		)
+		create_address_and_contact(
+			address_title="_Test Address for Customer 2",
+			first_name="_Test Contact for Customer 2",
+			email="test_contact_two_customer@example.com",
+			customer="_Test Customer 2",
+		)
 		# test if lead is created and quotation with new lead is fetched
-		quotation = _get_cart_quotation()
+		customer = frappe.get_doc("Customer", "_Test Customer 2")
+		quotation = _get_cart_quotation(party=customer)
 		self.assertEqual(quotation.quotation_to, "Customer")
 		self.assertEqual(
 			quotation.contact_person,
-			frappe.db.get_value("Contact", dict(email_id="test_cart_user@example.com")),
+			frappe.db.get_value("Contact", dict(email_id="test_contact_two_customer@example.com")),
 		)
 		self.assertEqual(quotation.contact_email, frappe.session.user)
 
 		return quotation
 
-	def test_get_cart_customer(self):
-		def validate_quotation():
+	def test_get_cart_customer(self, customer="_Test Customer 2"):
+		def validate_quotation(customer_name):
 			# test if quotation with customer is fetched
-			quotation = _get_cart_quotation()
+			party = frappe.get_doc("Customer", customer_name)
+			quotation = _get_cart_quotation(party=party)
 			self.assertEqual(quotation.quotation_to, "Customer")
-			self.assertEqual(quotation.party_name, "_Test Customer")
+			self.assertEqual(quotation.party_name, customer_name)
 			self.assertEqual(quotation.contact_email, frappe.session.user)
 			return quotation
 
-		self.login_as_customer(
-			"test_contact_two_customer@example.com", "_Test Contact 2 For _Test Customer"
-		)
-		validate_quotation()
-
-		self.login_as_customer()
-		quotation = validate_quotation()
-
+		quotation = validate_quotation(customer)
 		return quotation
 
 	def test_add_to_cart(self):
-		self.login_as_customer()
-
+		self.login_as_customer(
+			"test_contact_two_customer@example.com", "_Test Contact 2 For _Test Customer"
+		)
+		create_address_and_contact(
+			address_title="_Test Address for Customer 2",
+			first_name="_Test Contact for Customer 2",
+			email="test_contact_two_customer@example.com",
+			customer="_Test Customer 2",
+		)
 		# clear existing quotations
 		self.clear_existing_quotations()
 
 		# add first item
 		update_cart("_Test Item", 1)
 
-		quotation = self.test_get_cart_customer()
+		quotation = self.test_get_cart_customer("_Test Customer 2")
 
 		self.assertEqual(quotation.get("items")[0].item_code, "_Test Item")
 		self.assertEqual(quotation.get("items")[0].qty, 1)
@@ -95,7 +102,7 @@
 
 		# add second item
 		update_cart("_Test Item 2", 1)
-		quotation = self.test_get_cart_customer()
+		quotation = self.test_get_cart_customer("_Test Customer 2")
 		self.assertEqual(quotation.get("items")[1].item_code, "_Test Item 2")
 		self.assertEqual(quotation.get("items")[1].qty, 1)
 		self.assertEqual(quotation.get("items")[1].amount, 20)
@@ -108,7 +115,7 @@
 
 		# update first item
 		update_cart("_Test Item", 5)
-		quotation = self.test_get_cart_customer()
+		quotation = self.test_get_cart_customer("_Test Customer 2")
 		self.assertEqual(quotation.get("items")[0].item_code, "_Test Item")
 		self.assertEqual(quotation.get("items")[0].qty, 5)
 		self.assertEqual(quotation.get("items")[0].amount, 50)
@@ -121,7 +128,7 @@
 
 		# remove first item
 		update_cart("_Test Item", 0)
-		quotation = self.test_get_cart_customer()
+		quotation = self.test_get_cart_customer("_Test Customer 2")
 
 		self.assertEqual(quotation.get("items")[0].item_code, "_Test Item 2")
 		self.assertEqual(quotation.get("items")[0].qty, 1)
@@ -132,7 +139,17 @@
 	@unittest.skip("Flaky in CI")
 	def test_tax_rule(self):
 		self.create_tax_rule()
-		self.login_as_customer()
+
+		self.login_as_customer(
+			"test_contact_two_customer@example.com", "_Test Contact 2 For _Test Customer"
+		)
+		create_address_and_contact(
+			address_title="_Test Address for Customer 2",
+			first_name="_Test Contact for Customer 2",
+			email="test_contact_two_customer@example.com",
+			customer="_Test Customer 2",
+		)
+
 		quotation = self.create_quotation()
 
 		from erpnext.accounts.party import set_taxes
@@ -320,7 +337,7 @@
 		if frappe.db.exists("User", email):
 			return
 
-		frappe.get_doc(
+		user = frappe.get_doc(
 			{
 				"doctype": "User",
 				"user_type": "Website User",
@@ -330,6 +347,40 @@
 			}
 		).insert(ignore_permissions=True)
 
+		user.add_roles("Customer")
+
+
+def create_address_and_contact(**kwargs):
+	if not frappe.db.get_value("Address", {"address_title": kwargs.get("address_title")}):
+		frappe.get_doc(
+			{
+				"doctype": "Address",
+				"address_title": kwargs.get("address_title"),
+				"address_type": kwargs.get("address_type") or "Office",
+				"address_line1": kwargs.get("address_line1") or "Station Road",
+				"city": kwargs.get("city") or "_Test City",
+				"state": kwargs.get("state") or "Test State",
+				"country": kwargs.get("country") or "India",
+				"links": [
+					{"link_doctype": "Customer", "link_name": kwargs.get("customer") or "_Test Customer"}
+				],
+			}
+		).insert()
+
+	if not frappe.db.get_value("Contact", {"first_name": kwargs.get("first_name")}):
+		contact = frappe.get_doc(
+			{
+				"doctype": "Contact",
+				"first_name": kwargs.get("first_name"),
+				"links": [
+					{"link_doctype": "Customer", "link_name": kwargs.get("customer") or "_Test Customer"}
+				],
+			}
+		)
+		contact.add_email(kwargs.get("email") or "test_contact_customer@example.com", is_primary=True)
+		contact.add_phone(kwargs.get("phone") or "+91 0000000000", is_primary_phone=True)
+		contact.insert()
+
 
 test_dependencies = [
 	"Sales Taxes and Charges Template",