fix: performance issue of sales invoice while save/submit (#19598)

* fix: performace issue of sales invoice while save/submit

* Cached price list data, item group child data, added indexing for blanket order
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
index 1776275..430dce7 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
@@ -181,8 +181,9 @@
 		item_details.serial_no = get_serial_no(args)
 	return item_details
 
-def get_pricing_rule_for_item(args, price_list_rate=0, doc=None):
-	from erpnext.accounts.doctype.pricing_rule.utils import get_pricing_rules
+def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=False):
+	from erpnext.accounts.doctype.pricing_rule.utils import (get_pricing_rules,
+		get_applied_pricing_rules, get_pricing_rule_items)
 
 	if isinstance(doc, string_types):
 		doc = json.loads(doc)
@@ -209,6 +210,55 @@
 				item_details, args.get('item_code'))
 		return item_details
 
+	update_args_for_pricing_rule(args)
+
+	pricing_rules = (get_applied_pricing_rules(args)
+		if for_validate and args.get("pricing_rules") else get_pricing_rules(args, doc))
+
+	if pricing_rules:
+		rules = []
+
+		for pricing_rule in pricing_rules:
+			if not pricing_rule: continue
+
+			if isinstance(pricing_rule, string_types):
+				pricing_rule = frappe.get_cached_doc("Pricing Rule", pricing_rule)
+				pricing_rule.apply_rule_on_other_items = get_pricing_rule_items(pricing_rule)
+
+			if pricing_rule.get('suggestion'): continue
+
+			item_details.validate_applied_rule = pricing_rule.get("validate_applied_rule", 0)
+			item_details.price_or_product_discount = pricing_rule.get("price_or_product_discount")
+
+			rules.append(get_pricing_rule_details(args, pricing_rule))
+
+			if pricing_rule.mixed_conditions or pricing_rule.apply_rule_on_other:
+				item_details.update({
+					'apply_rule_on_other_items': json.dumps(pricing_rule.apply_rule_on_other_items),
+					'apply_rule_on': (frappe.scrub(pricing_rule.apply_rule_on_other)
+						if pricing_rule.apply_rule_on_other else frappe.scrub(pricing_rule.get('apply_on')))
+				})
+
+			if pricing_rule.coupon_code_based==1 and args.coupon_code==None:
+				return item_details
+				
+			if (not pricing_rule.validate_applied_rule and
+				pricing_rule.price_or_product_discount == "Price"):
+				apply_price_discount_pricing_rule(pricing_rule, item_details, args)
+
+		item_details.has_pricing_rule = 1
+
+		item_details.pricing_rules = ','.join([d.pricing_rule for d in rules])
+
+		if not doc: return item_details
+
+	elif args.get("pricing_rules"):
+		item_details = remove_pricing_rule_for_item(args.get("pricing_rules"),
+			item_details, args.get('item_code'))
+
+	return item_details
+
+def update_args_for_pricing_rule(args):
 	if not (args.item_group and args.brand):
 		try:
 			args.item_group, args.brand = frappe.get_cached_value("Item", args.item_code, ["item_group", "brand"])
@@ -235,52 +285,12 @@
 		args.supplier_group = frappe.get_cached_value("Supplier", args.supplier, "supplier_group")
 		args.customer = args.customer_group = args.territory = None
 
-	pricing_rules = get_pricing_rules(args, doc)
-
-	if pricing_rules:
-		rules = []
-
-		for pricing_rule in pricing_rules:
-			if not pricing_rule or pricing_rule.get('suggestion'): continue
-
-			item_details.validate_applied_rule = pricing_rule.get("validate_applied_rule", 0)
-
-			rules.append(get_pricing_rule_details(args, pricing_rule))
-			if pricing_rule.mixed_conditions or pricing_rule.apply_rule_on_other:
-				continue
-
-			if pricing_rule.coupon_code_based==1 and args.coupon_code==None:
-				return item_details
-				
-			if (not pricing_rule.validate_applied_rule and
-				pricing_rule.price_or_product_discount == "Price"):
-				apply_price_discount_pricing_rule(pricing_rule, item_details, args)
-
-		item_details.has_pricing_rule = 1
-
-		# if discount is applied on the rate and not on price list rate
-		# if price_list_rate:
-		# 	set_discount_amount(price_list_rate, item_details)
-
-		item_details.pricing_rules = ','.join([d.pricing_rule for d in rules])
-
-		if not doc: return item_details
-
-		for rule in rules:
-			doc.append('pricing_rules', rule)
-
-	elif args.get("pricing_rules"):
-		item_details = remove_pricing_rule_for_item(args.get("pricing_rules"),
-			item_details, args.get('item_code'))
-
-	return item_details
-
 def get_pricing_rule_details(args, pricing_rule):
 	return frappe._dict({
 		'pricing_rule': pricing_rule.name,
 		'rate_or_discount': pricing_rule.rate_or_discount,
 		'margin_type': pricing_rule.margin_type,
-		'item_code': pricing_rule.item_code or args.get("item_code"),
+		'item_code': args.get("item_code"),
 		'child_docname': args.get('child_docname')
 	})
 
@@ -327,10 +337,10 @@
 			item_details.rate = rate
 
 def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None):
-	from erpnext.accounts.doctype.pricing_rule.utils import get_apply_on_and_items
+	from erpnext.accounts.doctype.pricing_rule.utils import get_pricing_rule_items
 	for d in pricing_rules.split(','):
 		if not d or not frappe.db.exists("Pricing Rule", d): continue
-		pricing_rule = frappe.get_doc('Pricing Rule', d)
+		pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
 
 		if pricing_rule.price_or_product_discount == 'Price':
 			if pricing_rule.rate_or_discount == 'Discount Percentage':
@@ -348,8 +358,9 @@
 				else pricing_rule.get('free_item'))
 
 		if pricing_rule.get("mixed_conditions") or pricing_rule.get("apply_rule_on_other"):
-			apply_on, items = get_apply_on_and_items(pricing_rule, item_details)
-			item_details.apply_on = apply_on
+			items = get_pricing_rule_items(pricing_rule)
+			item_details.apply_on = (frappe.scrub(pricing_rule.apply_rule_on_other)
+				if pricing_rule.apply_rule_on_other else frappe.scrub(pricing_rule.get('apply_on')))
 			item_details.applied_on_items = ','.join(items)
 
 	item_details.pricing_rules = ''
diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py
index ef26c2e..637e503 100644
--- a/erpnext/accounts/doctype/pricing_rule/utils.py
+++ b/erpnext/accounts/doctype/pricing_rule/utils.py
@@ -8,6 +8,7 @@
 from frappe import throw, _
 from six import string_types
 from frappe.utils import flt, cint, get_datetime
+from erpnext.setup.doctype.item_group.item_group import get_child_item_groups
 from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
 from erpnext.stock.get_item_details import get_conversion_factor
 
@@ -173,10 +174,11 @@
 
 			if (field and pricing_rules[0].get('other_' + field) != args.get(field)): return
 
-		pr_doc = frappe.get_doc('Pricing Rule', pricing_rules[0].name)
+		pr_doc = frappe.get_cached_doc('Pricing Rule', pricing_rules[0].name)
 
 		if pricing_rules[0].mixed_conditions and doc:
-			stock_qty, amount = get_qty_and_rate_for_mixed_conditions(doc, pr_doc, args)
+			stock_qty, amount, items = get_qty_and_rate_for_mixed_conditions(doc, pr_doc, args)
+			pricing_rules[0].apply_rule_on_other_items = items
 
 		elif pricing_rules[0].is_cumulative:
 			items = [args.get(frappe.scrub(pr_doc.get('apply_on')))]
@@ -339,17 +341,19 @@
 				sum_qty += data[0]
 				sum_amt += data[1]
 
-	return sum_qty, sum_amt
+	return sum_qty, sum_amt, items
 
 def get_qty_and_rate_for_other_item(doc, pr_doc, pricing_rules):
-	for d in get_pricing_rule_items(pr_doc):
-		for row in doc.items:
-			if d == row.get(frappe.scrub(pr_doc.apply_on)):
-				pricing_rules = filter_pricing_rules_for_qty_amount(row.get("stock_qty"),
-					row.get("amount"), pricing_rules, row)
+	items = get_pricing_rule_items(pr_doc)
 
-				if pricing_rules and pricing_rules[0]:
-					return pricing_rules
+	for row in doc.items:
+		if row.get(frappe.scrub(pr_doc.apply_rule_on_other)) in items:
+			pricing_rules = filter_pricing_rules_for_qty_amount(row.get("stock_qty"),
+				row.get("amount"), pricing_rules, row)
+
+			if pricing_rules and pricing_rules[0]:
+				pricing_rules[0].apply_rule_on_other_items = items
+				return pricing_rules
 
 def get_qty_amount_data_for_cumulative(pr_doc, doc, items=[]):
 	sum_qty, sum_amt = [0, 0]
@@ -397,31 +401,7 @@
 
 	return [sum_qty, sum_amt]
 
-def validate_pricing_rules(doc):
-	validate_pricing_rule_on_transactions(doc)
-
-	for d in doc.items:
-		validate_pricing_rule_on_items(doc, d)
-
-	doc.calculate_taxes_and_totals()
-
-def validate_pricing_rule_on_items(doc, item_row, do_not_validate = False):
-	value = 0
-	for pricing_rule in get_applied_pricing_rules(doc, item_row):
-		pr_doc = frappe.get_doc('Pricing Rule', pricing_rule)
-
-		if pr_doc.get('apply_on') == 'Transaction': continue
-
-		if pr_doc.get('price_or_product_discount') == 'Product':
-			apply_pricing_rule_for_free_items(doc, pr_doc)
-		else:
-			for field in ['discount_percentage', 'discount_amount', 'rate']:
-				if not pr_doc.get(field): continue
-
-				value += pr_doc.get(field)
-			apply_pricing_rule(doc, pr_doc, item_row, value, do_not_validate)
-
-def validate_pricing_rule_on_transactions(doc):
+def apply_pricing_rule_on_transaction(doc):
 	conditions = "apply_on = 'Transaction'"
 
 	values = {}
@@ -453,7 +433,7 @@
 			elif d.price_or_product_discount == 'Product':
 				apply_pricing_rule_for_free_items(doc, d)
 
-def get_applied_pricing_rules(doc, item_row):
+def get_applied_pricing_rules(item_row):
 	return (item_row.get("pricing_rules").split(',')
 		if item_row.get("pricing_rules") else [])
 
@@ -468,70 +448,29 @@
 				'item_code': pricing_rule.get('free_item'),
 				'qty': pricing_rule.get('free_qty'),
 				'uom': pricing_rule.get('free_item_uom'),
-				'rate': pricing_rule.get('free_item_rate'),
+				'rate': pricing_rule.get('free_item_rate') or 0,
 				'is_free_item': 1
 			})
 
 			doc.set_missing_values()
 
-def apply_pricing_rule(doc, pr_doc, item_row, value, do_not_validate=False):
-	apply_on, items = get_apply_on_and_items(pr_doc, item_row)
-
-	rule_applied = {}
-
-	for item in doc.get("items"):
-		if item.get(apply_on) in items:
-			if not item.pricing_rules:
-				item.pricing_rules = item_row.pricing_rules
-
-			for field in ['discount_percentage', 'discount_amount', 'rate']:
-				if not pr_doc.get(field): continue
-
-				key = (item.name, item.pricing_rules)
-				if not pr_doc.validate_applied_rule:
-					rule_applied[key] = 1
-					item.set(field, value)
-				elif item.get(field) < value:
-					if not do_not_validate and item.idx == item_row.idx:
-						rule_applied[key] = 0
-						frappe.msgprint(_("Row {0}: user has not applied rule <b>{1}</b> on the item <b>{2}</b>")
-							.format(item.idx, pr_doc.title, item.item_code))
-
-	if rule_applied and doc.get("pricing_rules"):
-		for d in doc.get("pricing_rules"):
-			key = (d.child_docname, d.pricing_rule)
-			if key in rule_applied:
-				d.rule_applied = 1
-
-def get_apply_on_and_items(pr_doc, item_row):
-	# for mixed or other items conditions
-	apply_on = frappe.scrub(pr_doc.get('apply_on'))
-	items = (get_pricing_rule_items(pr_doc)
-		if pr_doc.mixed_conditions else [item_row.get(apply_on)])
-
-	if pr_doc.apply_rule_on_other:
-		apply_on = frappe.scrub(pr_doc.apply_rule_on_other)
-		items = [pr_doc.get(apply_on)]
-
-	return apply_on, items
-
 def get_pricing_rule_items(pr_doc):
+	apply_on_data = []
 	apply_on = frappe.scrub(pr_doc.get('apply_on'))
 
 	pricing_rule_apply_on = apply_on_table.get(pr_doc.get('apply_on'))
 
-	return [item.get(apply_on) for item in pr_doc.get(pricing_rule_apply_on)] or []
+	for d in pr_doc.get(pricing_rule_apply_on):
+		if apply_on == 'item_group':
+			get_child_item_groups(d.get(apply_on))
+		else:
+			apply_on_data.append(d.get(apply_on))
 
-@frappe.whitelist()
-def validate_pricing_rule_for_different_cond(doc):
-	if isinstance(doc, string_types):
-		doc = json.loads(doc)
+	if pr_doc.apply_rule_on_other:
+		apply_on = frappe.scrub(pr_doc.apply_rule_on_other)
+		apply_on_data.append(pr_doc.get(apply_on))
 
-	doc = frappe.get_doc(doc)
-	for d in doc.get("items"):
-		validate_pricing_rule_on_items(doc, d, True)
-
-	return doc
+	return list(set(apply_on_data))
 
 def validate_coupon_code(coupon_name):
 	from frappe.utils import today,getdate
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index a912ef0..1f8b663 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -5,15 +5,17 @@
 import frappe, erpnext
 import json
 from frappe import _, throw
-from frappe.utils import today, flt, cint, fmt_money, formatdate, getdate, add_days, add_months, get_last_day, nowdate
-from erpnext.stock.get_item_details import get_conversion_factor
+from frappe.utils import (today, flt, cint, fmt_money, formatdate,
+	getdate, add_days, add_months, get_last_day, nowdate, get_link_to_form)
+from erpnext.stock.get_item_details import get_conversion_factor, get_item_details
 from erpnext.setup.utils import get_exchange_rate
 from erpnext.accounts.utils import get_fiscal_years, validate_fiscal_year, get_account_currency
 from erpnext.utilities.transaction_base import TransactionBase
 from erpnext.buying.utils import update_last_purchase_rate
 from erpnext.controllers.sales_and_purchase_return import validate_return
 from erpnext.accounts.party import get_party_account_currency, validate_party_frozen_disabled
-from erpnext.accounts.doctype.pricing_rule.utils import validate_pricing_rules
+from erpnext.accounts.doctype.pricing_rule.utils import (apply_pricing_rule_on_transaction,
+	apply_pricing_rule_for_free_items, get_applied_pricing_rules)
 from erpnext.exceptions import InvalidCurrency
 from six import text_type
 from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
@@ -101,7 +103,7 @@
 
 		validate_regional(self)
 		if self.doctype != 'Material Request':
-			validate_pricing_rules(self)
+			apply_pricing_rule_on_transaction(self)
 
 	def validate_invoice_documents_schedule(self):
 		self.validate_payment_schedule_dates()
@@ -232,7 +234,6 @@
 
 	def set_missing_item_details(self, for_validate=False):
 		"""set missing item values"""
-		from erpnext.stock.get_item_details import get_item_details
 		from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
 
 		if hasattr(self, "items"):
@@ -244,7 +245,6 @@
 				document_type = "{} Item".format(self.doctype)
 				parent_dict.update({"document_type": document_type})
 
-			self.set('pricing_rules', [])
 			# party_name field used for customer in quotation
 			if self.doctype == "Quotation" and self.quotation_to == "Customer" and parent_dict.get("party_name"):
 				parent_dict.update({"customer": parent_dict.get("party_name")})
@@ -264,7 +264,7 @@
 					if self.get("is_subcontracted"):
 						args["is_subcontracted"] = self.is_subcontracted
 
-					ret = get_item_details(args, self, overwrite_warehouse=False)
+					ret = get_item_details(args, self, for_validate=True, overwrite_warehouse=False)
 
 					for fieldname, value in ret.items():
 						if item.meta.get_field(fieldname) and value is not None:
@@ -285,24 +285,42 @@
 					if self.doctype in ["Purchase Invoice", "Sales Invoice"] and item.meta.get_field('is_fixed_asset'):
 						item.set('is_fixed_asset', ret.get('is_fixed_asset', 0))
 
-					if ret.get("pricing_rules") and not ret.get("validate_applied_rule", 0):
-						# if user changed the discount percentage then set user's discount percentage ?
-						item.set("pricing_rules", ret.get("pricing_rules"))
-						item.set("discount_percentage", ret.get("discount_percentage"))
-						item.set("discount_amount", ret.get("discount_amount"))
-						if ret.get("pricing_rule_for") == "Rate":
-							item.set("price_list_rate", ret.get("price_list_rate"))
-
-						if item.get("price_list_rate"):
-							item.rate = flt(item.price_list_rate *
-								(1.0 - (flt(item.discount_percentage) / 100.0)), item.precision("rate"))
-
-							if item.get('discount_amount'):
-								item.rate = item.price_list_rate - item.discount_amount
+					if ret.get("pricing_rules"):
+						self.apply_pricing_rule_on_items(item, ret)
 
 			if self.doctype == "Purchase Invoice":
 				self.set_expense_account(for_validate)
 
+	def apply_pricing_rule_on_items(self, item, pricing_rule_args):
+		if not pricing_rule_args.get("validate_applied_rule", 0):
+			# if user changed the discount percentage then set user's discount percentage ?
+			if pricing_rule_args.get("price_or_product_discount") == 'Price':
+				item.set("pricing_rules", pricing_rule_args.get("pricing_rules"))
+				item.set("discount_percentage", pricing_rule_args.get("discount_percentage"))
+				item.set("discount_amount", pricing_rule_args.get("discount_amount"))
+				if pricing_rule_args.get("pricing_rule_for") == "Rate":
+					item.set("price_list_rate", pricing_rule_args.get("price_list_rate"))
+
+				if item.get("price_list_rate"):
+					item.rate = flt(item.price_list_rate *
+						(1.0 - (flt(item.discount_percentage) / 100.0)), item.precision("rate"))
+
+					if item.get('discount_amount'):
+						item.rate = item.price_list_rate - item.discount_amount
+
+			elif pricing_rule_args.get('free_item'):
+				apply_pricing_rule_for_free_items(self, pricing_rule_args)
+
+		elif pricing_rule_args.get("validate_applied_rule"):
+			for pricing_rule in get_applied_pricing_rules(item):
+				pricing_rule_doc = frappe.get_cached_doc("Pricing Rule", pricing_rule)
+				for field in ['discount_percentage', 'discount_amount', 'rate']:
+					if item.get(field) < pricing_rule_doc.get(field):
+						title = get_link_to_form("Pricing Rule", pricing_rule)
+
+						frappe.msgprint(_("Row {0}: user has not applied the rule {1} on the item {2}")
+							.format(item.idx, frappe.bold(title), frappe.bold(item.item_code)))
+
 	def set_taxes(self):
 		if not self.meta.get_field("taxes"):
 			return
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index d2db9d0..66232d7 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -552,7 +552,7 @@
 		if item.price_list_rate:
 			if item.pricing_rules and not self.doc.ignore_pricing_rule:
 				for d in item.pricing_rules.split(','):
-					pricing_rule = frappe.get_doc('Pricing Rule', d)
+					pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
 
 					if (pricing_rule.margin_type == 'Amount' and pricing_rule.currency == self.doc.currency)\
 							or (pricing_rule.margin_type == 'Percentage'):
diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.json b/erpnext/manufacturing/doctype/blanket_order/blanket_order.json
index 260e0b8..0330e5c 100644
--- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.json
+++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.json
@@ -89,7 +89,8 @@
    "fieldtype": "Link",
    "label": "Company",
    "options": "Company",
-   "reqd": 1
+   "reqd": 1,
+   "search_index": 1
   },
   {
    "fieldname": "section_break_12",
@@ -129,7 +130,7 @@
   }
  ],
  "is_submittable": 1,
- "modified": "2019-10-16 13:38:32.302316",
+ "modified": "2019-11-18 19:37:37.151686",
  "modified_by": "Administrator",
  "module": "Manufacturing",
  "name": "Blanket Order",
diff --git a/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.json b/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.json
index 099eed4..977ad54 100644
--- a/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.json
+++ b/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.json
@@ -1,298 +1,78 @@
 {
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "beta": 0, 
- "creation": "2018-05-24 07:20:04.255236", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "document_type": "", 
- "editable_grid": 1, 
- "engine": "InnoDB", 
+ "creation": "2018-05-24 07:20:04.255236",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "item_code",
+  "item_name",
+  "column_break_3",
+  "qty",
+  "rate",
+  "ordered_qty",
+  "section_break_7",
+  "terms_and_conditions"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "item_code", 
-   "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": "Item Code", 
-   "length": 0, 
-   "no_copy": 0, 
-   "options": "Item", 
-   "permlevel": 0, 
-   "precision": "", 
-   "print_hide": 0, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 0, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 0, 
-   "reqd": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "item_code",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Item Code",
+   "options": "Item",
+   "reqd": 1,
+   "search_index": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fetch_from": "item_code.item_name", 
-   "fieldname": "item_name", 
-   "fieldtype": "Data", 
-   "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": "Item Name", 
-   "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
-  }, 
+   "fetch_from": "item_code.item_name",
+   "fieldname": "item_name",
+   "fieldtype": "Data",
+   "label": "Item Name"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "column_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": "column_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": "qty", 
-   "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": "Quantity", 
-   "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": "qty",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Quantity"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "rate", 
-   "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": "Rate", 
-   "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": 1, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "rate",
+   "fieldtype": "Currency",
+   "in_list_view": 1,
+   "label": "Rate",
+   "reqd": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "ordered_qty", 
-   "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": "Ordered Quantity", 
-   "length": 0, 
-   "no_copy": 1, 
-   "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": "ordered_qty",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Ordered Quantity",
+   "no_copy": 1,
+   "read_only": 1
+  },
   {
-   "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, 
-   "fieldname": "terms_and_conditions", 
-   "fieldtype": "Text", 
-   "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": "Terms and Conditions", 
-   "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": "terms_and_conditions",
+   "fieldtype": "Text",
+   "label": "Terms and Conditions"
   }
- ], 
- "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": "2018-06-14 07:04:14.050836", 
- "modified_by": "Administrator", 
- "module": "Manufacturing", 
- "name": "Blanket Order Item", 
- "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
+ ],
+ "istable": 1,
+ "modified": "2019-11-18 19:37:46.245878",
+ "modified_by": "Administrator",
+ "module": "Manufacturing",
+ "name": "Blanket Order Item",
+ "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/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index ca492ba..5da9493 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -516,7 +516,7 @@
 									}
 								},
 								() => me.conversion_factor(doc, cdt, cdn, true),
-								() => me.validate_pricing_rule(item)
+								() => me.remove_pricing_rule(item)
 							]);
 						}
 					}
@@ -1174,7 +1174,7 @@
 				callback: function(r) {
 					if (!r.exc && r.message) {
 						r.message.forEach(row_item => {
-							me.validate_pricing_rule(row_item);
+							me.remove_pricing_rule(row_item);
 						});
 						me._set_values_for_item_list(r.message);
 						me.calculate_taxes_and_totals();
@@ -1283,6 +1283,8 @@
 	_set_values_for_item_list: function(children) {
 		var me = this;
 		var price_list_rate_changed = false;
+		var items_rule_dict = {};
+
 		for(var i=0, l=children.length; i<l; i++) {
 			var d = children[i];
 			var existing_pricing_rule = frappe.model.get_value(d.doctype, d.name, "pricing_rules");
@@ -1299,14 +1301,39 @@
 			// if pricing rule set as blank from an existing value, apply price_list
 			if(!me.frm.doc.ignore_pricing_rule && existing_pricing_rule && !d.pricing_rules) {
 				me.apply_price_list(frappe.get_doc(d.doctype, d.name));
-			} else {
-				me.validate_pricing_rule(frappe.get_doc(d.doctype, d.name));
+			} else if(!d.pricing_rules) {
+				me.remove_pricing_rule(frappe.get_doc(d.doctype, d.name));
+			}
+
+			if (d.apply_rule_on_other_items) {
+				items_rule_dict[d.name] = d;
 			}
 		}
 
+		me.apply_rule_on_other_items(items_rule_dict);
+
 		if(!price_list_rate_changed) me.calculate_taxes_and_totals();
 	},
 
+	apply_rule_on_other_items: function(args) {
+		const me = this;
+		const fields = ["discount_percentage", "discount_amount", "rate"];
+
+		for(var k in args) {
+			let data = args[k];
+
+			me.frm.doc.items.forEach(d => {
+				if (in_list(data.apply_rule_on_other_items, d[data.apply_rule_on])) {
+					for(var k in data) {
+						if (in_list(fields, k)) {
+							frappe.model.set_value(d.doctype, d.name, k, data[k]);
+						}
+					}
+				}
+			});
+		}
+	},
+
 	apply_price_list: function(item, reset_plc_conversion) {
 		// We need to reset plc_conversion_rate sometimes because the call to
 		// `erpnext.stock.get_item_details.apply_price_list` is sensitive to its value
@@ -1348,33 +1375,11 @@
 		});
 	},
 
-	validate_pricing_rule: function(item) {
+	remove_pricing_rule: function(item) {
 		let me = this;
 		const fields = ["discount_percentage", "discount_amount", "pricing_rules"];
 
-		if (item.pricing_rules) {
-			frappe.call({
-				method: "erpnext.accounts.doctype.pricing_rule.utils.validate_pricing_rule_for_different_cond",
-				args: {
-					doc: me.frm.doc
-				},
-				callback: function(r) {
-					if (r.message) {
-						r.message.items.forEach(d => {
-							me.frm.doc.items.forEach(row => {
-								if(d.name == row.name) {
-									fields.forEach(f => {
-										row[f] = d[f];
-									});
-								}
-							});
-						});
-
-						me.trigger_price_list_rate();
-					}
-				}
-			});
-		} else if(item.remove_free_item) {
+		if(item.remove_free_item) {
 			var items = [];
 
 			me.frm.doc.items.forEach(d => {
diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py
index f78246f..22375ae 100644
--- a/erpnext/setup/doctype/item_group/item_group.py
+++ b/erpnext/setup/doctype/item_group/item_group.py
@@ -39,6 +39,7 @@
 		invalidate_cache_for(self)
 		self.validate_name_with_item()
 		self.validate_one_root()
+		self.delete_child_item_groups_key()
 
 	def make_route(self):
 		'''Make website route'''
@@ -58,6 +59,7 @@
 	def on_trash(self):
 		NestedSet.on_trash(self)
 		WebsiteGenerator.on_trash(self)
+		self.delete_child_item_groups_key()
 
 	def validate_name_with_item(self):
 		if frappe.db.exists("Item", self.name):
@@ -83,6 +85,9 @@
 
 		return context
 
+	def delete_child_item_groups_key(self):
+		frappe.cache().hdel("child_item_groups", self.name)
+
 @frappe.whitelist(allow_guest=True)
 def get_product_list_for_group(product_group=None, start=0, limit=10, search=None):
 	if product_group:
@@ -168,6 +173,19 @@
 		from `tabItem Group` where lft>=%(lft)s and rgt<=%(rgt)s
 			and show_in_website = 1""", {"lft": item_group.lft, "rgt": item_group.rgt})
 
+def get_child_item_groups(item_group_name):
+	child_item_groups = frappe.cache().hget("child_item_groups", item_group_name)
+
+	if not child_item_groups:
+		item_group = frappe.get_cached_doc("Item Group", item_group_name)
+
+		child_item_groups = [d.name for d in frappe.get_all('Item Group',
+			filters= {'lft': ('>=', item_group.lft),'rgt': ('>=', item_group.rgt)})]
+
+		frappe.cache().hset("child_item_groups", item_group_name, child_item_groups)
+
+	return child_item_groups or {}
+
 def get_item_for_list_in_html(context):
 	# add missing absolute link in files
 	# user may forget it during upload
diff --git a/erpnext/stock/doctype/bin/bin.json b/erpnext/stock/doctype/bin/bin.json
index e17429b..04d624e 100644
--- a/erpnext/stock/doctype/bin/bin.json
+++ b/erpnext/stock/doctype/bin/bin.json
@@ -1,599 +1,200 @@
 {
- "allow_copy": 0, 
- "allow_guest_to_view": 0, 
- "allow_import": 0, 
- "allow_rename": 0, 
- "autoname": "MAT-BIN-.YYYY.-.#####", 
- "beta": 0, 
- "creation": "2013-01-10 16:34:25", 
- "custom": 0, 
- "docstatus": 0, 
- "doctype": "DocType", 
- "editable_grid": 0, 
- "engine": "InnoDB", 
+ "autoname": "MAT-BIN-.YYYY.-.#####",
+ "creation": "2013-01-10 16:34:25",
+ "doctype": "DocType",
+ "engine": "InnoDB",
+ "field_order": [
+  "warehouse",
+  "item_code",
+  "reserved_qty",
+  "actual_qty",
+  "ordered_qty",
+  "indented_qty",
+  "planned_qty",
+  "projected_qty",
+  "reserved_qty_for_production",
+  "reserved_qty_for_sub_contract",
+  "ma_rate",
+  "stock_uom",
+  "fcfs_rate",
+  "valuation_rate",
+  "stock_value"
+ ],
  "fields": [
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "warehouse", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 1, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 1, 
-   "label": "Warehouse", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "warehouse", 
-   "oldfieldtype": "Link", 
-   "options": "Warehouse", 
-   "permlevel": 0, 
-   "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": "warehouse",
+   "fieldtype": "Link",
+   "in_filter": 1,
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Warehouse",
+   "oldfieldname": "warehouse",
+   "oldfieldtype": "Link",
+   "options": "Warehouse",
+   "read_only": 1,
+   "search_index": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "item_code", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 1, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 1, 
-   "label": "Item Code", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "item_code", 
-   "oldfieldtype": "Link", 
-   "options": "Item", 
-   "permlevel": 0, 
-   "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": "item_code",
+   "fieldtype": "Link",
+   "in_filter": 1,
+   "in_list_view": 1,
+   "in_standard_filter": 1,
+   "label": "Item Code",
+   "oldfieldname": "item_code",
+   "oldfieldtype": "Link",
+   "options": "Item",
+   "read_only": 1,
+   "search_index": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "0.00", 
-   "fieldname": "reserved_qty", 
-   "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": "Reserved Quantity", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "reserved_qty", 
-   "oldfieldtype": "Currency", 
-   "permlevel": 0, 
-   "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
-  }, 
+   "default": "0.00",
+   "fieldname": "reserved_qty",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Reserved Quantity",
+   "oldfieldname": "reserved_qty",
+   "oldfieldtype": "Currency",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "0.00", 
-   "fieldname": "actual_qty", 
-   "fieldtype": "Float", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 1, 
-   "in_global_search": 0, 
-   "in_list_view": 1, 
-   "in_standard_filter": 0, 
-   "label": "Actual Quantity", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "actual_qty", 
-   "oldfieldtype": "Currency", 
-   "permlevel": 0, 
-   "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
-  }, 
+   "default": "0.00",
+   "fieldname": "actual_qty",
+   "fieldtype": "Float",
+   "in_filter": 1,
+   "in_list_view": 1,
+   "label": "Actual Quantity",
+   "oldfieldname": "actual_qty",
+   "oldfieldtype": "Currency",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "0.00", 
-   "fieldname": "ordered_qty", 
-   "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": "Ordered Quantity", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "ordered_qty", 
-   "oldfieldtype": "Currency", 
-   "permlevel": 0, 
-   "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
-  }, 
+   "default": "0.00",
+   "fieldname": "ordered_qty",
+   "fieldtype": "Float",
+   "in_list_view": 1,
+   "label": "Ordered Quantity",
+   "oldfieldname": "ordered_qty",
+   "oldfieldtype": "Currency",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "default": "0.00", 
-   "fieldname": "indented_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": "Requested Quantity", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "indented_qty", 
-   "oldfieldtype": "Currency", 
-   "permlevel": 0, 
-   "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
-  }, 
+   "default": "0.00",
+   "fieldname": "indented_qty",
+   "fieldtype": "Float",
+   "label": "Requested Quantity",
+   "oldfieldname": "indented_qty",
+   "oldfieldtype": "Currency",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "planned_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": "Planned Qty", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "planned_qty", 
-   "oldfieldtype": "Currency", 
-   "permlevel": 0, 
-   "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": "planned_qty",
+   "fieldtype": "Float",
+   "label": "Planned Qty",
+   "oldfieldname": "planned_qty",
+   "oldfieldtype": "Currency",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "projected_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": "Projected Qty", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "projected_qty", 
-   "oldfieldtype": "Currency", 
-   "permlevel": 0, 
-   "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": "projected_qty",
+   "fieldtype": "Float",
+   "label": "Projected Qty",
+   "oldfieldname": "projected_qty",
+   "oldfieldtype": "Currency",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "reserved_qty_for_production", 
-   "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": "Reserved Qty for Production", 
-   "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": "reserved_qty_for_production",
+   "fieldtype": "Float",
+   "label": "Reserved Qty for Production",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "reserved_qty_for_sub_contract", 
-   "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": "Reserved Qty for sub contract", 
-   "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": "reserved_qty_for_sub_contract",
+   "fieldtype": "Float",
+   "label": "Reserved Qty for sub contract"
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "ma_rate", 
-   "fieldtype": "Float", 
-   "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": "Moving Average Rate", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "ma_rate", 
-   "oldfieldtype": "Currency", 
-   "permlevel": 0, 
-   "print_hide": 1, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 1, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "ma_rate",
+   "fieldtype": "Float",
+   "hidden": 1,
+   "label": "Moving Average Rate",
+   "oldfieldname": "ma_rate",
+   "oldfieldtype": "Currency",
+   "print_hide": 1,
+   "read_only": 1,
+   "report_hide": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "stock_uom", 
-   "fieldtype": "Link", 
-   "hidden": 0, 
-   "ignore_user_permissions": 0, 
-   "ignore_xss_filter": 0, 
-   "in_filter": 1, 
-   "in_global_search": 0, 
-   "in_list_view": 0, 
-   "in_standard_filter": 0, 
-   "label": "UOM", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "stock_uom", 
-   "oldfieldtype": "Data", 
-   "options": "UOM", 
-   "permlevel": 0, 
-   "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": "stock_uom",
+   "fieldtype": "Link",
+   "in_filter": 1,
+   "label": "UOM",
+   "oldfieldname": "stock_uom",
+   "oldfieldtype": "Data",
+   "options": "UOM",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "fcfs_rate", 
-   "fieldtype": "Float", 
-   "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": "FCFS Rate", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "fcfs_rate", 
-   "oldfieldtype": "Currency", 
-   "permlevel": 0, 
-   "print_hide": 1, 
-   "print_hide_if_no_value": 0, 
-   "read_only": 1, 
-   "remember_last_selected_value": 0, 
-   "report_hide": 1, 
-   "reqd": 0, 
-   "search_index": 0, 
-   "set_only_once": 0, 
-   "translatable": 0, 
-   "unique": 0
-  }, 
+   "fieldname": "fcfs_rate",
+   "fieldtype": "Float",
+   "hidden": 1,
+   "label": "FCFS Rate",
+   "oldfieldname": "fcfs_rate",
+   "oldfieldtype": "Currency",
+   "print_hide": 1,
+   "read_only": 1,
+   "report_hide": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "valuation_rate", 
-   "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": "Valuation Rate", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "valuation_rate", 
-   "oldfieldtype": "Currency", 
-   "permlevel": 0, 
-   "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": "valuation_rate",
+   "fieldtype": "Float",
+   "label": "Valuation Rate",
+   "oldfieldname": "valuation_rate",
+   "oldfieldtype": "Currency",
+   "read_only": 1
+  },
   {
-   "allow_bulk_edit": 0, 
-   "allow_in_quick_entry": 0, 
-   "allow_on_submit": 0, 
-   "bold": 0, 
-   "collapsible": 0, 
-   "columns": 0, 
-   "fieldname": "stock_value", 
-   "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": "Stock Value", 
-   "length": 0, 
-   "no_copy": 0, 
-   "oldfieldname": "stock_value", 
-   "oldfieldtype": "Currency", 
-   "permlevel": 0, 
-   "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": "stock_value",
+   "fieldtype": "Float",
+   "label": "Stock Value",
+   "oldfieldname": "stock_value",
+   "oldfieldtype": "Currency",
+   "read_only": 1
   }
- ], 
- "has_web_view": 0, 
- "hide_heading": 0, 
- "hide_toolbar": 1, 
- "idx": 1, 
- "image_view": 0, 
- "in_create": 1, 
- "is_submittable": 0, 
- "issingle": 0, 
- "istable": 0, 
- "max_attachments": 0, 
- "modified": "2018-08-21 16:15:39.356230", 
- "modified_by": "Administrator", 
- "module": "Stock", 
- "name": "Bin", 
- "owner": "Administrator", 
+ ],
+ "hide_toolbar": 1,
+ "idx": 1,
+ "in_create": 1,
+ "modified": "2019-11-18 18:34:59.456882",
+ "modified_by": "Administrator",
+ "module": "Stock",
+ "name": "Bin",
+ "owner": "Administrator",
  "permissions": [
   {
-   "amend": 0, 
-   "cancel": 0, 
-   "create": 0, 
-   "delete": 0, 
-   "email": 1, 
-   "export": 0, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "Sales User", 
-   "set_user_permissions": 0, 
-   "share": 0, 
-   "submit": 0, 
-   "write": 0
-  }, 
+   "email": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Sales User"
+  },
   {
-   "amend": 0, 
-   "cancel": 0, 
-   "create": 0, 
-   "delete": 0, 
-   "email": 1, 
-   "export": 0, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "Purchase User", 
-   "set_user_permissions": 0, 
-   "share": 0, 
-   "submit": 0, 
-   "write": 0
-  }, 
+   "email": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Purchase User"
+  },
   {
-   "amend": 0, 
-   "cancel": 0, 
-   "create": 0, 
-   "delete": 0, 
-   "email": 1, 
-   "export": 0, 
-   "if_owner": 0, 
-   "import": 0, 
-   "permlevel": 0, 
-   "print": 1, 
-   "read": 1, 
-   "report": 1, 
-   "role": "Stock User", 
-   "set_user_permissions": 0, 
-   "share": 0, 
-   "submit": 0, 
-   "write": 0
+   "email": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Stock User"
   }
- ], 
- "quick_entry": 1, 
- "read_only": 0, 
- "read_only_onload": 0, 
- "search_fields": "item_code,warehouse", 
- "show_name_in_global_search": 0, 
- "sort_order": "ASC", 
- "track_changes": 0, 
- "track_seen": 0, 
- "track_views": 0
+ ],
+ "quick_entry": 1,
+ "search_fields": "item_code,warehouse",
+ "sort_order": "ASC"
 }
\ No newline at end of file
diff --git a/erpnext/stock/doctype/price_list/price_list.py b/erpnext/stock/doctype/price_list/price_list.py
index 8773b9c..33713fa 100644
--- a/erpnext/stock/doctype/price_list/price_list.py
+++ b/erpnext/stock/doctype/price_list/price_list.py
@@ -16,6 +16,7 @@
 	def on_update(self):
 		self.set_default_if_missing()
 		self.update_item_price()
+		self.delete_price_list_details_key()
 
 	def set_default_if_missing(self):
 		if cint(self.selling):
@@ -32,6 +33,8 @@
 			(self.currency, cint(self.buying), cint(self.selling), self.name))
 
 	def on_trash(self):
+		self.delete_price_list_details_key()
+
 		def _update_default_price_list(module):
 			b = frappe.get_doc(module + " Settings")
 			price_list_fieldname = module.lower() + "_price_list"
@@ -43,3 +46,20 @@
 
 		for module in ["Selling", "Buying"]:
 			_update_default_price_list(module)
+
+	def delete_price_list_details_key(self):
+		frappe.cache().hdel("price_list_details", self.name)
+
+def get_price_list_details(price_list):
+	price_list_details = frappe.cache().hget("price_list_details", price_list)
+
+	if not price_list_details:
+		price_list_details = frappe.get_cached_value("Price List", price_list,
+			["currency", "price_not_uom_dependent", "enabled"], as_dict=1)
+
+		if not price_list_details or not price_list_details.get("enabled"):
+			throw(_("Price List {0} is disabled or does not exist").format(price_list))
+
+		frappe.cache().hset("price_list_details", price_list, price_list_details)
+
+	return price_list_details or {}
\ No newline at end of file
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 7c2e09e..9f47edc 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -12,6 +12,7 @@
 from erpnext.stock.doctype.batch.batch import get_batch_no
 from erpnext import get_company_currency
 from erpnext.stock.doctype.item.item import get_item_defaults, get_uom_conv_factor
+from erpnext.stock.doctype.price_list.price_list import get_price_list_details
 from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
 from erpnext.setup.doctype.brand.brand import get_brand_defaults
 from erpnext.stock.doctype.item_manufacturer.item_manufacturer import get_item_manufacturer_part_no
@@ -22,7 +23,7 @@
 purchase_doctypes = ['Material Request', 'Supplier Quotation', 'Purchase Order', 'Purchase Receipt', 'Purchase Invoice']
 
 @frappe.whitelist()
-def get_item_details(args, doc=None, overwrite_warehouse=True):
+def get_item_details(args, doc=None, for_validate=False, overwrite_warehouse=True):
 	"""
 		args = {
 			"item_code": "",
@@ -74,7 +75,9 @@
 		if args.get(key) is None:
 			args[key] = value
 
-	data = get_pricing_rule_for_item(args, out.price_list_rate, doc)
+	data = get_pricing_rule_for_item(args, out.price_list_rate,
+		doc, for_validate=for_validate)
+
 	out.update(data)
 
 	update_stock(args, out)
@@ -479,7 +482,6 @@
 	if meta.get_field("currency") or args.get('currency'):
 		pl_details = get_price_list_currency_and_exchange_rate(args)
 		args.update(pl_details)
-		validate_price_list(args)
 		if meta.get_field("currency"):
 			validate_conversion_rate(args, meta)
 
@@ -634,14 +636,6 @@
 
 	return flag
 
-def validate_price_list(args):
-	if args.get("price_list"):
-		if not frappe.db.get_value("Price List",
-			{"name": args.price_list, args.transaction_type: 1, "enabled": 1}):
-			throw(_("Price List {0} is disabled or does not exist").format(args.price_list))
-	elif args.get("customer"):
-		throw(_("Price List not selected"))
-
 def validate_conversion_rate(args, meta):
 	from erpnext.controllers.accounts_controller import validate_conversion_rate
 
@@ -905,27 +899,6 @@
 
 	return item_details
 
-def get_price_list_currency(price_list):
-	if price_list:
-		result = frappe.db.get_value("Price List", {"name": price_list,
-			"enabled": 1}, ["name", "currency"], as_dict=True)
-
-		if not result:
-			throw(_("Price List {0} is disabled or does not exist").format(price_list))
-
-		return result.currency
-
-def get_price_list_uom_dependant(price_list):
-	if price_list:
-		result = frappe.db.get_value("Price List", {"name": price_list,
-			"enabled": 1}, ["name", "price_not_uom_dependent"], as_dict=True)
-
-		if not result:
-			throw(_("Price List {0} is disabled or does not exist").format(price_list))
-
-		return not result.price_not_uom_dependent
-
-
 def get_price_list_currency_and_exchange_rate(args):
 	if not args.price_list:
 		return {}
@@ -935,8 +908,11 @@
 	elif args.doctype in ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']:
 		args.update({"exchange_rate": "for_buying"})
 
-	price_list_currency = get_price_list_currency(args.price_list)
-	price_list_uom_dependant = get_price_list_uom_dependant(args.price_list)
+	price_list_details = get_price_list_details(args.price_list)
+
+	price_list_currency = price_list_details.get("currency")
+	price_list_uom_dependant = price_list_details.get("price_list_uom_dependant")
+
 	plc_conversion_rate = args.plc_conversion_rate
 	company_currency = get_company_currency(args.company)