[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