[material request] Added feature to pull items from BOM
diff --git a/manufacturing/doctype/bom/bom.py b/manufacturing/doctype/bom/bom.py
index 20c1141..5954475 100644
--- a/manufacturing/doctype/bom/bom.py
+++ b/manufacturing/doctype/bom/bom.py
@@ -402,4 +402,53 @@
 			if act_pbom and act_pbom[0][0]:
 				action = self.doc.docstatus < 2 and _("deactivate") or _("cancel")
 				msgprint(_("Cannot ") + action + _(": It is linked to other active BOM(s)"),
-					raise_exception=1)
\ No newline at end of file
+					raise_exception=1)
+
+def get_bom_items_as_dict(bom, qty=1, fetch_exploded=1):
+	item_dict = {}
+		
+	query = """select 
+				bom_item.item_code, 
+				ifnull(sum(bom_item.qty_consumed_per_unit),0) * %(qty)s as qty, 
+				item.description, 
+				item.stock_uom,
+				item.default_warehouse
+			from 
+				`tab%(table)s` bom_item, `tabItem` item 
+			where 
+				bom_item.docstatus < 2 
+				and bom_item.parent = "%(bom)s"
+				and item.name = bom_item.item_code 
+				%(conditions)s
+				group by item_code, stock_uom"""
+	
+	if fetch_exploded:
+		items = webnotes.conn.sql(query % {
+			"qty": qty,
+			"table": "BOM Explosion Item",
+			"bom": bom,
+			"conditions": """and ifnull(item.is_pro_applicable, 'No') = 'No'
+					and ifnull(item.is_sub_contracted_item, 'No') = 'No' """
+		}, as_dict=True)
+	else:
+		items = webnotes.conn.sql(query % {
+			"qty": qty,
+			"table": "BOM Item",
+			"bom": bom,
+			"conditions": ""
+		}, as_dict=True)
+
+	# make unique
+	for item in items:
+		if item_dict.has_key(item.item_code):
+			item_dict[item.item_code]["qty"] += flt(item.qty)
+		else:
+			item_dict[item.item_code] = item
+		
+	return item_dict
+
+@webnotes.whitelist()
+def get_bom_items(bom, qty=1, fetch_exploded=1):
+	items = get_bom_items_as_dict(bom, qty, fetch_exploded).values()
+	items.sort(lambda a, b: a.item_code > b.item_code and 1 or -1)
+	return items
\ No newline at end of file
diff --git a/manufacturing/doctype/bom/test_bom.py b/manufacturing/doctype/bom/test_bom.py
index d0b394a..da98faf 100644
--- a/manufacturing/doctype/bom/test_bom.py
+++ b/manufacturing/doctype/bom/test_bom.py
@@ -10,6 +10,35 @@
 	[
 		{
 			"doctype": "BOM", 
+			"item": "_Test Item Home Desktop 100", 
+			"quantity": 1.0,
+			"is_active": 1,
+			"is_default": 1,
+			"docstatus": 1
+		}, 
+		{
+			"doctype": "BOM Item", 
+			"item_code": "_Test Serialized Item With Series", 
+			"parentfield": "bom_materials", 
+			"qty": 1.0, 
+			"rate": 5000.0, 
+			"amount": 5000.0, 
+			"stock_uom": "_Test UOM"
+		}, 
+		{
+			"doctype": "BOM Item", 
+			"item_code": "_Test Item 2", 
+			"parentfield": "bom_materials", 
+			"qty": 2.0, 
+			"rate": 1000.0,
+			"amount": 2000.0,
+			"stock_uom": "_Test UOM"
+		}
+	],
+
+	[
+		{
+			"doctype": "BOM", 
 			"item": "_Test FG Item", 
 			"quantity": 1.0,
 			"is_active": 1,
@@ -29,10 +58,33 @@
 			"doctype": "BOM Item", 
 			"item_code": "_Test Item Home Desktop 100", 
 			"parentfield": "bom_materials", 
+			"bom_no": "BOM/_Test Item Home Desktop 100/001",
 			"qty": 2.0, 
 			"rate": 1000.0,
 			"amount": 2000.0,
 			"stock_uom": "_Test UOM"
 		}
-	]
-]
\ No newline at end of file
+	],
+]
+
+class TestBOM(unittest.TestCase):
+	def test_get_items(self):
+		from manufacturing.doctype.bom.bom import get_bom_items_as_dict
+		items_dict = get_bom_items_as_dict(bom="BOM/_Test FG Item/001", qty=1, fetch_exploded=0)
+		self.assertTrue(test_records[1][1]["item_code"] in items_dict)
+		self.assertTrue(test_records[1][2]["item_code"] in items_dict)
+		self.assertEquals(len(items_dict.values()), 2)
+		
+	def test_get_items_exploded(self):
+		from manufacturing.doctype.bom.bom import get_bom_items_as_dict
+		items_dict = get_bom_items_as_dict(bom="BOM/_Test FG Item/001", qty=1, fetch_exploded=1)
+		self.assertTrue(test_records[1][1]["item_code"] in items_dict)
+		self.assertFalse(test_records[1][2]["item_code"] in items_dict)
+		self.assertTrue(test_records[0][1]["item_code"] in items_dict)
+		self.assertTrue(test_records[0][2]["item_code"] in items_dict)
+		self.assertEquals(len(items_dict.values()), 3)
+		
+	def test_get_items_list(self):
+		from manufacturing.doctype.bom.bom import get_bom_items
+		self.assertEquals(len(get_bom_items(bom="BOM/_Test FG Item/001", qty=1, fetch_exploded=1)), 3)
+
diff --git a/stock/doctype/item/test_item.py b/stock/doctype/item/test_item.py
index 12bb4e0..ad88c8c 100644
--- a/stock/doctype/item/test_item.py
+++ b/stock/doctype/item/test_item.py
@@ -31,7 +31,7 @@
 		"is_sales_item": "Yes",
 		"is_service_item": "No",
 		"inspection_required": "No",
-		"is_pro_applicable": "No",
+		"is_pro_applicable": "Yes",
 		"is_sub_contracted_item": "No",
 		"stock_uom": "_Test UOM",
 		"default_income_account": "Sales - _TC",
@@ -47,6 +47,26 @@
 	],
 	[{
 		"doctype": "Item",
+		"item_code": "_Test Item 2",
+		"item_name": "_Test Item 2",
+		"description": "_Test Item 2",
+		"item_group": "_Test Item Group",
+		"is_stock_item": "Yes",
+		"is_asset_item": "No",
+		"has_batch_no": "No",
+		"has_serial_no": "No",
+		"is_purchase_item": "Yes",
+		"is_sales_item": "Yes",
+		"is_service_item": "No",
+		"inspection_required": "No",
+		"is_pro_applicable": "Yes",
+		"is_sub_contracted_item": "No",
+		"stock_uom": "_Test UOM",
+		"default_income_account": "Sales - _TC",
+		"default_warehouse": "_Test Warehouse - _TC",
+	}],	
+	[{
+		"doctype": "Item",
 		"item_code": "_Test Item Home Desktop 100",
 		"item_name": "_Test Item Home Desktop 100",
 		"description": "_Test Item Home Desktop 100",
@@ -61,8 +81,9 @@
 		"is_sales_item": "Yes",
 		"is_service_item": "No",
 		"inspection_required": "No",
-		"is_pro_applicable": "No",
+		"is_pro_applicable": "Yes",
 		"is_sub_contracted_item": "No",
+		"is_manufactured_item": "Yes",
 		"stock_uom": "_Test UOM"
 	},
 	{
@@ -182,7 +203,7 @@
 		"is_sales_item": "Yes",
 		"is_service_item": "No",
 		"inspection_required": "No",
-		"is_pro_applicable": "No",
+		"is_pro_applicable": "Yes",
 		"is_sub_contracted_item": "No",
 		"stock_uom": "_Test UOM"
 	}],
diff --git a/stock/doctype/material_request/material_request.js b/stock/doctype/material_request/material_request.js
index 6931181..99e8afb 100644
--- a/stock/doctype/material_request/material_request.js
+++ b/stock/doctype/material_request/material_request.js
@@ -21,7 +21,11 @@
 				+ wn._("Fulfilled"), cint(doc.per_ordered));
 		}
 		
-		if(doc.docstatus == 1 && doc.status != 'Stopped'){
+		if(doc.docstatus==0) {
+			cur_frm.add_custom_button(wn._("Get Items from BOM"), cur_frm.cscript.get_items_from_bom, "icon-sitemap");
+		}
+		
+		if(doc.docstatus == 1 && doc.status != 'Stopped') {
 			if(doc.material_request_type === "Purchase")
 				cur_frm.add_custom_button("Make Supplier Quotation", 
 					this.make_supplier_quotation);
@@ -63,6 +67,53 @@
 		
 	},
 	
+	schedule_date: function(doc, cdt, cdn) {
+		var val = locals[cdt][cdn].schedule_date;
+		if(val) {
+			$.each(wn.model.get("Material Request Item", { parent: cur_frm.doc.name }), function(i, d) {
+				if(!d.schedule_date) {
+					d.schedule_date = val;
+				}
+			});
+			refresh_field("indent_details");
+		}
+	},
+	
+	get_items_from_bom: function() {
+		var d = new wn.ui.Dialog({
+			title: wn._("Get Items from BOM"),
+			fields: [
+				{"fieldname":"bom", "fieldtype":"Link", "label":wn._("BOM"), 
+					options:"BOM"},
+				{"fieldname":"fetch_exploded", "fieldtype":"Check", 
+					"label":wn._("Fetch exploded BOM (including sub-assemblies)"), "default":1},
+				{fieldname:"fetch", "label":wn._("Get Items from BOM"), "fieldtype":"Button"}
+			]
+		});
+		d.get_input("fetch").on("click", function() {
+			var values = d.get_values();
+			if(!values) return;
+			
+			wn.call({
+				method:"manufacturing.doctype.bom.bom.get_bom_items",
+				args: values,
+				callback: function(r) {
+					$.each(r.message, function(i, item) {
+						var d = wn.model.add_child(cur_frm.doc, "Material Request Item", "indent_details");
+						d.item_code = item.item_code;
+						d.description = item.description;
+						d.warehouse = item.default_warehouse;
+						d.uom = item.stock_uom;
+						d.qty = item.qty;
+					});
+					d.hide();
+					refresh_field("indent_details");
+				}
+			});
+		});
+		d.show();
+	},
+	
 	tc_name: function() {
 		this.get_terms();
 	},
diff --git a/stock/doctype/material_request_item/material_request_item.txt b/stock/doctype/material_request_item/material_request_item.txt
index dae97e0..2ef4acd 100644
--- a/stock/doctype/material_request_item/material_request_item.txt
+++ b/stock/doctype/material_request_item/material_request_item.txt
@@ -2,7 +2,7 @@
  {
   "creation": "2013-02-22 01:28:02", 
   "docstatus": 0, 
-  "modified": "2013-08-07 14:45:11", 
+  "modified": "2013-10-11 14:21:32", 
   "modified_by": "Administrator", 
   "owner": "Administrator"
  }, 
@@ -138,7 +138,7 @@
   "oldfieldname": "item_name", 
   "oldfieldtype": "Data", 
   "print_width": "100px", 
-  "reqd": 1, 
+  "reqd": 0, 
   "search_index": 1, 
   "width": "100px"
  }, 
diff --git a/stock/doctype/stock_entry/stock_entry.py b/stock/doctype/stock_entry/stock_entry.py
index 63cfd2b..78a0bd2 100644
--- a/stock/doctype/stock_entry/stock_entry.py
+++ b/stock/doctype/stock_entry/stock_entry.py
@@ -472,68 +472,16 @@
 		self.get_stock_and_rate()
 	
 	def get_bom_raw_materials(self, qty):
-		""" 
-			get all items from flat bom except 
-			child items of sub-contracted and sub assembly items 
-			and sub assembly items itself.
-		"""
+		from manufacturing.doctype.bom.bom import get_bom_items_as_dict
+		
 		# item dict = { item_code: {qty, description, stock_uom} }
-		item_dict = {}
+		item_dict = get_bom_items_as_dict(self.doc.bom_no, qty=qty, fetch_exploded = self.doc.use_multi_level_bom)
 		
-		def _make_items_dict(items_list):
-			"""makes dict of unique items with it's qty"""
-			for item in items_list:
-				if item_dict.has_key(item.item_code):
-					item_dict[item.item_code]["qty"] += flt(item.qty)
-				else:
-					item_dict[item.item_code] = {
-						"qty": flt(item.qty), 
-						"description": item.description, 
-						"stock_uom": item.stock_uom,
-						"from_warehouse": item.default_warehouse
-					}
-		
-		if self.doc.use_multi_level_bom:
-			# get all raw materials with sub assembly childs					
-			fl_bom_sa_child_item = webnotes.conn.sql("""select 
-					fb.item_code, 
-					ifnull(sum(fb.qty_consumed_per_unit),0)*%s as qty, 
-					fb.description, 
-					fb.stock_uom,
-					it.default_warehouse
-				from 
-					`tabBOM Explosion Item` fb,`tabItem` it 
-				where 
-					it.name = fb.item_code 
-					and ifnull(it.is_pro_applicable, 'No') = 'No'
-					and ifnull(it.is_sub_contracted_item, 'No') = 'No' 
-					and fb.docstatus < 2 
-					and fb.parent=%s group by item_code, stock_uom""", 
-				(qty, self.doc.bom_no), as_dict=1)
-			
-			if fl_bom_sa_child_item:
-				_make_items_dict(fl_bom_sa_child_item)
-		else:
-			# get only BOM items
-			fl_bom_sa_items = webnotes.conn.sql("""select 
-					`tabItem`.item_code,
-					ifnull(sum(`tabBOM Item`.qty_consumed_per_unit), 0) *%s as qty,
-					`tabItem`.description, 
-					`tabItem`.stock_uom,
-					`tabItem`.default_warehouse
-				from 
-					`tabBOM Item`, `tabItem`
-				where 
-					`tabBOM Item`.parent = %s and 
-					`tabBOM Item`.item_code = tabItem.name and
-					`tabBOM Item`.docstatus < 2 
-				group by item_code""", (qty, self.doc.bom_no), as_dict=1)
-			
-			if fl_bom_sa_items:
-				_make_items_dict(fl_bom_sa_items)
+		for item in item_dict.values():
+			item.from_warehouse = item.default_warehouse
 			
 		return item_dict
-	
+			
 	def get_pending_raw_materials(self, pro_obj):
 		"""
 			issue (item quantity) that is pending to issue or desire to transfer,
diff --git a/stock/doctype/stock_entry/test_stock_entry.py b/stock/doctype/stock_entry/test_stock_entry.py
index f01a83b..4b36e58 100644
--- a/stock/doctype/stock_entry/test_stock_entry.py
+++ b/stock/doctype/stock_entry/test_stock_entry.py
@@ -1,9 +1,6 @@
 # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd.
 # License: GNU General Public License v3. See license.txt
 
-# ERPNext - web based ERP (http://erpnext.com)
-# For license information, please see license.txt
-
 from __future__ import unicode_literals
 import webnotes, unittest
 from webnotes.utils import flt