Merge branch 'develop' into po_supplier_skip
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index d661bcb..76eb56f 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -19,6 +19,7 @@
 from erpnext.exceptions import InvalidCurrency
 from six import text_type
 from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
+from erpnext.stock.get_item_details import get_item_warehouse
 
 force_item_fields = ("item_group", "brand", "stock_uom", "is_fixed_asset", "item_tax_rate", "pricing_rules")
 
@@ -1126,16 +1127,16 @@
 	"""
 	Returns a Sales Order Item child item containing the default values
 	"""
-	p_doctype = frappe.get_doc(parent_doctype, parent_doctype_name)
-	child_item = frappe.new_doc('Sales Order Item', p_doctype, child_docname)
+	p_doc = frappe.get_doc(parent_doctype, parent_doctype_name)
+	child_item = frappe.new_doc('Sales Order Item', p_doc, child_docname)
 	item = frappe.get_doc("Item", item_code)
 	child_item.item_code = item.item_code
 	child_item.item_name = item.item_name
 	child_item.description = item.description
-	child_item.reqd_by_date = p_doctype.delivery_date
+	child_item.reqd_by_date = p_doc.delivery_date
 	child_item.uom = item.stock_uom
 	child_item.conversion_factor = get_conversion_factor(item_code, item.stock_uom).get("conversion_factor") or 1.0
-	child_item.warehouse = p_doctype.set_warehouse or p_doctype.items[0].warehouse
+	child_item.warehouse = get_item_warehouse(item, p_doc, overwrite_warehouse=True)
 	return child_item
 
 
@@ -1143,13 +1144,13 @@
 	"""
 	Returns a Purchase Order Item child item containing the default values
 	"""
-	p_doctype = frappe.get_doc(parent_doctype, parent_doctype_name)
-	child_item = frappe.new_doc('Purchase Order Item', p_doctype, child_docname)
+	p_doc = frappe.get_doc(parent_doctype, parent_doctype_name)
+	child_item = frappe.new_doc('Purchase Order Item', p_doc, child_docname)
 	item = frappe.get_doc("Item", item_code)
 	child_item.item_code = item.item_code
 	child_item.item_name = item.item_name
 	child_item.description = item.description
-	child_item.schedule_date = p_doctype.schedule_date
+	child_item.schedule_date = p_doc.schedule_date
 	child_item.uom = item.stock_uom
 	child_item.conversion_factor = get_conversion_factor(item_code, item.stock_uom).get("conversion_factor") or 1.0
 	child_item.base_rate = 1 # Initiallize value will update in parent validation
diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.js b/erpnext/manufacturing/doctype/blanket_order/blanket_order.js
index 1cd9446..4c31bd0 100644
--- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.js
+++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.js
@@ -14,27 +14,37 @@
 	refresh: function(frm) {
 		erpnext.hide_company();
 		if (frm.doc.customer && frm.doc.docstatus === 1) {
-			frm.add_custom_button(__('View Orders'), function() {
-				frappe.set_route('List', 'Sales Order', {blanket_order: frm.doc.name});
-			});
-			frm.add_custom_button(__("Create Sales Order"), function(){
+			frm.add_custom_button(__("Sales Order"), function() {
 				frappe.model.open_mapped_doc({
-					method: "erpnext.manufacturing.doctype.blanket_order.blanket_order.make_sales_order",
-					frm: frm
+					method: "erpnext.manufacturing.doctype.blanket_order.blanket_order.make_order",
+					frm: frm,
+					args: {
+						doctype: 'Sales Order'
+					}
 				});
-			}).addClass("btn-primary");
+			}, __('Create'));
+
+			frm.add_custom_button(__("Quotation"), function() {
+				frappe.model.open_mapped_doc({
+					method: "erpnext.manufacturing.doctype.blanket_order.blanket_order.make_order",
+					frm: frm,
+					args: {
+						doctype: 'Quotation'
+					}
+				});
+			}, __('Create'));
 		}
 
 		if (frm.doc.supplier && frm.doc.docstatus === 1) {
-			frm.add_custom_button(__('View Orders'), function() {
-				frappe.set_route('List', 'Purchase Order', {blanket_order: frm.doc.name});
-			});
-			frm.add_custom_button(__("Create Purchase Order"), function(){
+			frm.add_custom_button(__("Purchase Order"), function(){
 				frappe.model.open_mapped_doc({
-					method: "erpnext.manufacturing.doctype.blanket_order.blanket_order.make_purchase_order",
-					frm: frm
+					method: "erpnext.manufacturing.doctype.blanket_order.blanket_order.make_order",
+					frm: frm,
+					args: {
+						doctype: 'Purchase Order'
+					}
 				});
-			}).addClass("btn-primary");
+			}, __('Create'));
 		}
 	},
 
diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py
index 38118bd..d7556ad 100644
--- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py
+++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py
@@ -14,10 +14,18 @@
 class BlanketOrder(Document):
 	def validate(self):
 		self.validate_dates()
+		self.validate_duplicate_items()
 
 	def validate_dates(self):
 		if getdate(self.from_date) > getdate(self.to_date):
-			frappe.throw(_("From date cannot be greater than To date")) 
+			frappe.throw(_("From date cannot be greater than To date"))
+
+	def validate_duplicate_items(self):
+		item_list = []
+		for item in self.items:
+			if item.item_code in item_list:
+				frappe.throw(_("Note: Item {0} added multiple times").format(frappe.bold(item.item_code)))
+			item_list.append(item.item_code)
 
 	def update_ordered_qty(self):
 		ref_doctype = "Sales Order" if self.blanket_order_type == "Selling" else "Purchase Order"
@@ -35,7 +43,14 @@
 			d.db_set("ordered_qty", item_ordered_qty.get(d.item_code, 0))
 
 @frappe.whitelist()
-def make_sales_order(source_name):
+def make_order(source_name):
+	doctype = frappe.flags.args.doctype
+
+	def update_doc(source_doc, target_doc, source_parent):
+		if doctype == 'Quotation':
+			target_doc.quotation_to = 'Customer'
+			target_doc.party_name = source_doc.customer
+
 	def update_item(source, target, source_parent):
 		target_qty = source.get("qty") - source.get("ordered_qty")
 		target.qty = target_qty if not flt(target_qty) < 0 else 0
@@ -49,39 +64,11 @@
 
 	target_doc = get_mapped_doc("Blanket Order", source_name, {
 		"Blanket Order": {
-			"doctype": "Sales Order"
+			"doctype": doctype,
+			"postprocess": update_doc
 		},
 		"Blanket Order Item": {
-			"doctype": "Sales Order Item",
-			"field_map": {
-				"rate": "blanket_order_rate",
-				"parent": "blanket_order"
-			},
-			"postprocess": update_item
-		}
-	})
-	return target_doc
-
-@frappe.whitelist()
-def make_purchase_order(source_name):
-	def update_item(source, target, source_parent):
-		target_qty = source.get("qty") - source.get("ordered_qty")
-		target.qty = target_qty if not flt(target_qty) < 0 else 0
-		item = get_item_defaults(target.item_code, source_parent.company)
-		if item:
-			target.item_name = item.get("item_name")
-			target.description = item.get("description")
-			target.uom = item.get("stock_uom")
-			target.warehouse = item.get("default_warehouse")
-			target.against_blanket_order = 1
-			target.blanket_order = source_name
-
-	target_doc = get_mapped_doc("Blanket Order", source_name, {
-		"Blanket Order": {
-			"doctype": "Purchase Order"
-		},
-		"Blanket Order Item": {
-			"doctype": "Purchase Order Item",
+			"doctype": doctype + " Item",
 			"field_map": {
 				"rate": "blanket_order_rate",
 				"parent": "blanket_order"
diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py b/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py
index ed319a0..d9aa0ca 100644
--- a/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py
+++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order_dashboard.py
@@ -6,7 +6,7 @@
 		'fieldname': 'blanket_order',
 		'transactions': [
 			{
-				'items': ['Purchase Order', 'Sales Order']
+				'items': ['Purchase Order', 'Sales Order', 'Quotation']
 			}
 		]
 	}
diff --git a/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py
index 455ea06..3171def 100644
--- a/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py
+++ b/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py
@@ -7,13 +7,17 @@
 import unittest
 from frappe.utils import add_months, today
 from erpnext import get_company_currency
-from .blanket_order import make_sales_order, make_purchase_order
+from .blanket_order import make_order
 
 class TestBlanketOrder(unittest.TestCase):
+	def setUp(self):
+		frappe.flags.args = frappe._dict()
+
 	def test_sales_order_creation(self):
 		bo = make_blanket_order(blanket_order_type="Selling")
 
-		so = make_sales_order(bo.name)
+		frappe.flags.args.doctype = 'Sales Order'
+		so = make_order(bo.name)
 		so.currency = get_company_currency(so.company)
 		so.delivery_date = today()
 		so.items[0].qty = 10
@@ -29,7 +33,8 @@
 		self.assertEqual(so.items[0].qty, bo.items[0].ordered_qty)
 
 		# test the quantity
-		so1 = make_sales_order(bo.name)
+		frappe.flags.args.doctype = 'Sales Order'
+		so1 = make_order(bo.name)
 		so1.currency = get_company_currency(so1.company)
 		self.assertEqual(so1.items[0].qty, (bo.items[0].qty-bo.items[0].ordered_qty))
 
@@ -37,7 +42,8 @@
 	def test_purchase_order_creation(self):
 		bo = make_blanket_order(blanket_order_type="Purchasing")
 
-		po = make_purchase_order(bo.name)
+		frappe.flags.args.doctype = 'Purchase Order'
+		po = make_order(bo.name)
 		po.currency = get_company_currency(po.company)
 		po.schedule_date = today()
 		po.items[0].qty = 10
@@ -53,7 +59,8 @@
 		self.assertEqual(po.items[0].qty, bo.items[0].ordered_qty)
 
 		# test the quantity
-		po1 = make_sales_order(bo.name)
+		frappe.flags.args.doctype = 'Purchase Order'
+		po1 = make_order(bo.name)
 		po1.currency = get_company_currency(po1.company)
 		self.assertEqual(po1.items[0].qty, (bo.items[0].qty-bo.items[0].ordered_qty))
 
@@ -78,7 +85,7 @@
 		"qty": args.quantity or 1000,
 		"rate": args.rate or 100
 	})
-	
+
 	bo.insert()
 	bo.submit()
 	return bo
\ No newline at end of file
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 8aec8bd..b6e6cb3 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -626,10 +626,11 @@
 erpnext.patches.v12_0.create_accounting_dimensions_in_missing_doctypes
 erpnext.patches.v11_1.set_status_for_material_request_type_manufacture
 erpnext.patches.v12_0.move_plaid_settings_to_doctype
-execute:frappe.reload_doc('desk', 'doctype','dashboard_chart_link')
-execute:frappe.reload_doc('desk', 'doctype','dashboard')
-execute:frappe.reload_doc('desk', 'doctype','dashboard_chart_source')
-execute:frappe.reload_doc('desk', 'doctype','dashboard_chart')
+execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart_link')
+execute:frappe.reload_doc('desk', 'doctype', 'dashboard')
+execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart_source')
+execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart')
+execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart_field')
 erpnext.patches.v12_0.add_default_dashboards
 erpnext.patches.v12_0.remove_bank_remittance_custom_fields
 erpnext.patches.v12_0.generate_leave_ledger_entries
@@ -660,3 +661,4 @@
 erpnext.patches.v12_0.create_irs_1099_field_united_states
 erpnext.patches.v12_0.move_bank_account_swift_number_to_bank
 erpnext.patches.v12_0.rename_bank_reconciliation_fields # 2020-01-22
+erpnext.patches.v12_0.set_received_qty_in_material_request_as_per_stock_uom
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/set_received_qty_in_material_request_as_per_stock_uom.py b/erpnext/patches/v12_0/set_received_qty_in_material_request_as_per_stock_uom.py
new file mode 100644
index 0000000..88c3e2e
--- /dev/null
+++ b/erpnext/patches/v12_0/set_received_qty_in_material_request_as_per_stock_uom.py
@@ -0,0 +1,30 @@
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+	purchase_receipts = frappe.db.sql("""
+		SELECT
+			 parent from `tabPurchase Receipt Item`
+		WHERE
+			material_request is not null
+			AND docstatus=1
+		""",as_dict=1)
+
+	purchase_receipts = set([d.parent for d in purchase_receipts])
+
+	for pr in purchase_receipts:
+		doc = frappe.get_doc("Purchase Receipt", pr)
+		doc.status_updater = [
+			{
+			'source_dt': 'Purchase Receipt Item',
+			'target_dt': 'Material Request Item',
+			'join_field': 'material_request_item',
+			'target_field': 'received_qty',
+			'target_parent_dt': 'Material Request',
+			'target_parent_field': 'per_received',
+			'target_ref_field': 'stock_qty',
+			'source_field': 'stock_qty',
+			'percent_join_field': 'material_request'
+			}
+		]
+		doc.update_qty()
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index 9a5b750..dbe48ec 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -6,7 +6,7 @@
 
 	apply_pricing_rule_on_item: function(item){
 		let effective_item_rate = item.price_list_rate;
-		if (item.parenttype === "Sales Order" && item.blanket_order_rate) {
+		if (in_list(["Sales Order", "Quotation"], item.parenttype) && item.blanket_order_rate) {
 			effective_item_rate = item.blanket_order_rate;
 		}
 		if(item.margin_type == "Percentage"){
diff --git a/erpnext/public/js/education/lms/quiz.js b/erpnext/public/js/education/lms/quiz.js
index 5248129..91cbbf4 100644
--- a/erpnext/public/js/education/lms/quiz.js
+++ b/erpnext/public/js/education/lms/quiz.js
@@ -29,7 +29,7 @@
 			this.questions.push(question)
 			this.wrapper.appendChild(question_wrapper);
 		})
-		if (data.activity.is_complete) {
+		if (data.activity && data.activity.is_complete) {
 			this.disable()
 			let indicator = 'red'
 			let message = 'Your are not allowed to attempt the quiz again.'
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index 4eb3175..4d44eae 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -453,7 +453,8 @@
 				fields: [{
 					fieldtype:'Data',
 					fieldname:"docname",
-					hidden: 0,
+					read_only: 1,
+					hidden: 1,
 				}, {
 					fieldtype:'Link',
 					fieldname:"item_code",
diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py
index b79c91c..7c47b8a 100644
--- a/erpnext/selling/doctype/quotation/quotation.py
+++ b/erpnext/selling/doctype/quotation/quotation.py
@@ -155,6 +155,11 @@
 	def update_item(obj, target, source_parent):
 		target.stock_qty = flt(obj.qty) * flt(obj.conversion_factor)
 
+		if obj.against_blanket_order:
+			target.against_blanket_order = obj.against_blanket_order
+			target.blanket_order = obj.blanket_order
+			target.blanket_order_rate = obj.blanket_order_rate
+
 	doclist = get_mapped_doc("Quotation", source_name, {
 			"Quotation": {
 				"doctype": "Sales Order",
diff --git a/erpnext/selling/doctype/quotation_item/quotation_item.json b/erpnext/selling/doctype/quotation_item/quotation_item.json
index 3ff5555..d50397c 100644
--- a/erpnext/selling/doctype/quotation_item/quotation_item.json
+++ b/erpnext/selling/doctype/quotation_item/quotation_item.json
@@ -55,6 +55,9 @@
   "weight_uom",
   "reference",
   "warehouse",
+  "against_blanket_order",
+  "blanket_order",
+  "blanket_order_rate",
   "column_break_30",
   "prevdoc_doctype",
   "prevdoc_docname",
@@ -573,12 +576,38 @@
    "fieldname": "image_section",
    "fieldtype": "Section Break",
    "label": "Image"
+  },
+  {
+   "depends_on": "eval:doc.against_blanket_order",
+   "fieldname": "blanket_order",
+   "fieldtype": "Link",
+   "label": "Blanket Order",
+   "no_copy": 1,
+   "options": "Blanket Order",
+   "print_hide": 1
+  },
+  {
+   "depends_on": "eval:doc.against_blanket_order",
+   "fieldname": "blanket_order_rate",
+   "fieldtype": "Currency",
+   "label": "Blanket Order Rate",
+   "no_copy": 1,
+   "print_hide": 1,
+   "read_only": 1
+  },
+  {
+   "default": "0",
+   "fieldname": "against_blanket_order",
+   "fieldtype": "Check",
+   "label": "Against Blanket Order",
+   "no_copy": 1,
+   "print_hide": 1
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2020-03-05 14:18:58.783751",
+ "modified": "2020-03-30 18:40:28.782720",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Quotation Item",
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 3b43690..c2b3892 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -50,8 +50,8 @@
 			'target_field': 'received_qty',
 			'target_parent_dt': 'Material Request',
 			'target_parent_field': 'per_received',
-			'target_ref_field': 'qty',
-			'source_field': 'qty',
+			'target_ref_field': 'stock_qty',
+			'source_field': 'stock_qty',
 			'percent_join_field': 'material_request'
 		}]
 		if cint(self.is_return):
@@ -357,7 +357,7 @@
 		if warehouse_with_no_account:
 			frappe.msgprint(_("No accounting entries for the following warehouses") + ": \n" +
 				"\n".join(warehouse_with_no_account))
-		
+
 		return process_gl_map(gl_entries)
 
 	def get_asset_gl_entry(self, gl_entries):
@@ -628,7 +628,7 @@
 
 	if not landed_cost_vouchers:
 		return
-	
+
 	item_account_wise_cost = {}
 
 	for lcv in landed_cost_vouchers:
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 53964f2..9c5a8e1 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -240,26 +240,13 @@
 	item_group_defaults = get_item_group_defaults(item.name, args.company)
 	brand_defaults = get_brand_defaults(item.name, args.company)
 
-	if overwrite_warehouse or not args.warehouse:
-		warehouse = (
-			args.get("set_warehouse") or
-			item_defaults.get("default_warehouse") or
-			item_group_defaults.get("default_warehouse") or
-			brand_defaults.get("default_warehouse") or
-			args.warehouse
-		)
-
-		if not warehouse:
-			defaults = frappe.defaults.get_defaults() or {}
-			warehouse_exists = frappe.db.exists("Warehouse", {
-				'name': defaults.default_warehouse,
-				'company': args.company
-			})
-			if defaults.get("default_warehouse") and warehouse_exists:
-				warehouse = defaults.default_warehouse
-
-	else:
-		warehouse = args.warehouse
+	defaults = frappe._dict({
+		'item_defaults': item_defaults,
+		'item_group_defaults': item_group_defaults,
+		'brand_defaults': brand_defaults
+	})
+	
+	warehouse = get_item_warehouse(item, args, overwrite_warehouse, defaults)
 
 	if args.get('doctype') == "Material Request" and not args.get('material_request_type'):
 		args['material_request_type'] = frappe.db.get_value('Material Request',
@@ -272,7 +259,7 @@
 		expense_account = get_asset_category_account(fieldname = "fixed_asset_account", item = args.item_code, company= args.company)
 
 	#Set the UOM to the Default Sales UOM or Default Purchase UOM if configured in the Item Master
-	if not args.uom:
+	if not args.get('uom'):
 		if args.get('doctype') in sales_doctypes:
 			args.uom = item.sales_uom if item.sales_uom else item.stock_uom
 		elif (args.get('doctype') in ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']) or \
@@ -362,6 +349,37 @@
 
 	return out
 
+def get_item_warehouse(item, args, overwrite_warehouse, defaults={}):
+	if not defaults:
+		defaults = frappe._dict({
+			'item_defaults' : get_item_defaults(item.name, args.company),
+			'item_group_defaults' : get_item_group_defaults(item.name, args.company),
+			'brand_defaults' : get_brand_defaults(item.name, args.company)
+		})
+
+	if overwrite_warehouse or not args.warehouse:
+		warehouse = (
+			args.get("set_warehouse") or
+			defaults.item_defaults.get("default_warehouse") or
+			defaults.item_group_defaults.get("default_warehouse") or
+			defaults.brand_defaults.get("default_warehouse") or
+			args.get('warehouse')
+		)
+
+		if not warehouse:
+			defaults = frappe.defaults.get_defaults() or {}
+			warehouse_exists = frappe.db.exists("Warehouse", {
+				'name': defaults.default_warehouse,
+				'company': args.company
+			})
+			if defaults.get("default_warehouse") and warehouse_exists:
+				warehouse = defaults.default_warehouse
+
+	else:
+		warehouse = args.get('warehouse')
+	
+	return warehouse
+
 def update_barcode_value(out):
 	from erpnext.accounts.doctype.sales_invoice.pos import get_barcode_data
 	barcode_data = get_barcode_data([out])
diff --git a/erpnext/www/lms/content.html b/erpnext/www/lms/content.html
index 5607c08..cdc7141 100644
--- a/erpnext/www/lms/content.html
+++ b/erpnext/www/lms/content.html
@@ -63,7 +63,7 @@
 		</a>
 	</div>
 	<div>
-		<h1>{{ content.name }} <span class="small text-muted">({{ position + 1 }}/{{length}})</span></h1>
+		<h2>{{ content.name }} <span class="small text-muted">({{ position + 1 }}/{{length}})</span></h2>
 	</div>
 {% endmacro %}