bom exploded items grouped by items
This commit is contained in:
parent
df28eefa4b
commit
ad600cce22
@ -270,18 +270,23 @@ class DocType:
|
||||
if b[0]:
|
||||
bom_list.append(b[0])
|
||||
|
||||
def update_cost_and_exploded_items(self):
|
||||
bom_list = self.traverse_tree()
|
||||
def update_cost_and_exploded_items(self, bom_list=[]):
|
||||
bom_list = self.traverse_tree(bom_list)
|
||||
for bom in bom_list:
|
||||
bom_obj = get_obj("BOM", bom, with_children=1)
|
||||
bom_obj.on_update()
|
||||
|
||||
def traverse_tree(self):
|
||||
return bom_list
|
||||
|
||||
def traverse_tree(self, bom_list=[]):
|
||||
def _get_children(bom_no):
|
||||
return [cstr(d[0]) for d in webnotes.conn.sql("""select bom_no from `tabBOM Item`
|
||||
where parent = %s and ifnull(bom_no, '') != ''""", bom_no)]
|
||||
|
||||
bom_list, count = [self.doc.name], 0
|
||||
count = 0
|
||||
if self.doc.name not in bom_list:
|
||||
bom_list.append(self.doc.name)
|
||||
|
||||
while(count < len(bom_list)):
|
||||
for child_bom in _get_children(bom_list[count]):
|
||||
if child_bom not in bom_list:
|
||||
@ -325,52 +330,50 @@ class DocType:
|
||||
|
||||
def get_exploded_items(self):
|
||||
""" Get all raw materials including items from child bom"""
|
||||
self.cur_exploded_items = []
|
||||
self.cur_exploded_items = {}
|
||||
for d in getlist(self.doclist, 'bom_materials'):
|
||||
if d.bom_no:
|
||||
self.get_child_exploded_items(d.bom_no, d.qty)
|
||||
else:
|
||||
self.cur_exploded_items.append({
|
||||
self.add_to_cur_exploded_items(webnotes._dict({
|
||||
'item_code' : d.item_code,
|
||||
'description' : d.description,
|
||||
'stock_uom' : d.stock_uom,
|
||||
'qty' : flt(d.qty),
|
||||
'rate' : flt(d.rate),
|
||||
'amount' : flt(d.amount),
|
||||
'parent_bom' : d.parent,
|
||||
'mat_detail_no' : d.name,
|
||||
'qty_consumed_per_unit' : flt(d.qty_consumed_per_unit)
|
||||
})
|
||||
'rate' : flt(d.rate),
|
||||
}))
|
||||
|
||||
def add_to_cur_exploded_items(self, args):
|
||||
if self.cur_exploded_items.get(args.item_code):
|
||||
self.cur_exploded_items[args.item_code]["qty"] += args.qty
|
||||
else:
|
||||
self.cur_exploded_items[args.item_code] = args
|
||||
|
||||
def get_child_exploded_items(self, bom_no, qty):
|
||||
""" Add all items from Flat BOM of child BOM"""
|
||||
|
||||
child_fb_items = sql("""select item_code, description, stock_uom, qty, rate,
|
||||
amount, parent_bom, mat_detail_no, qty_consumed_per_unit
|
||||
from `tabBOM Explosion Item` where parent = '%s' and docstatus = 1""" %
|
||||
bom_no, as_dict = 1)
|
||||
qty_consumed_per_unit from `tabBOM Explosion Item`
|
||||
where parent = %s and docstatus = 1""", bom_no, as_dict = 1)
|
||||
|
||||
for d in child_fb_items:
|
||||
self.cur_exploded_items.append({
|
||||
self.add_to_cur_exploded_items(webnotes._dict({
|
||||
'item_code' : d['item_code'],
|
||||
'description' : d['description'],
|
||||
'stock_uom' : d['stock_uom'],
|
||||
'qty' : flt(d['qty_consumed_per_unit'])*qty,
|
||||
'rate' : flt(d['rate']),
|
||||
'amount' : flt(d['amount']),
|
||||
'parent_bom' : d['parent_bom'],
|
||||
'mat_detail_no' : d['mat_detail_no'],
|
||||
'qty_consumed_per_unit' : flt(d['qty_consumed_per_unit'])*qty/flt(self.doc.quantity)
|
||||
|
||||
})
|
||||
'rate' : flt(d['rate']),
|
||||
}))
|
||||
|
||||
def add_exploded_items(self):
|
||||
"Add items to Flat BOM table"
|
||||
self.doclist = self.doc.clear_table(self.doclist, 'flat_bom_details', 1)
|
||||
for d in self.cur_exploded_items:
|
||||
ch = addchild(self.doc, 'flat_bom_details', 'BOM Explosion Item',
|
||||
self.doclist)
|
||||
for i in d.keys():
|
||||
ch.fields[i] = d[i]
|
||||
ch = addchild(self.doc, 'flat_bom_details', 'BOM Explosion Item', self.doclist)
|
||||
for i in self.cur_exploded_items[d].keys():
|
||||
ch.fields[i] = self.cur_exploded_items[d][i]
|
||||
ch.amount = flt(ch.qty) * flt(ch.rate)
|
||||
ch.qty_consumed_per_unit = flt(ch.qty) / flt(self.doc.quantity)
|
||||
ch.docstatus = self.doc.docstatus
|
||||
ch.save(1)
|
||||
|
||||
|
@ -48,134 +48,4 @@ test_records = [
|
||||
"stock_uom": "No."
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
|
||||
# import webnotes.model
|
||||
# from webnotes.utils import nowdate, flt
|
||||
# from accounts.utils import get_fiscal_year
|
||||
# from webnotes.model.doclist import DocList
|
||||
# import copy
|
||||
#
|
||||
# company = webnotes.conn.get_default("company")
|
||||
#
|
||||
#
|
||||
# def load_data():
|
||||
#
|
||||
# # create default warehouse
|
||||
# if not webnotes.conn.exists("Warehouse", "Default Warehouse"):
|
||||
# webnotes.insert({"doctype": "Warehouse",
|
||||
# "warehouse_name": "Default Warehouse",
|
||||
# "warehouse_type": "Stores"})
|
||||
#
|
||||
# # create UOM: Nos.
|
||||
# if not webnotes.conn.exists("UOM", "Nos"):
|
||||
# webnotes.insert({"doctype": "UOM", "uom_name": "Nos"})
|
||||
#
|
||||
# from webnotes.tests import insert_test_data
|
||||
# # create item groups and items
|
||||
# insert_test_data("Item Group",
|
||||
# sort_fn=lambda ig: (ig[0].get('parent_item_group'), ig[0].get('name')))
|
||||
# insert_test_data("Item")
|
||||
#
|
||||
# base_bom_fg = [
|
||||
# {"doctype": "BOM", "item": "Android Jack D", "quantity": 1,
|
||||
# "is_active": "Yes", "is_default": 1, "uom": "Nos"},
|
||||
# {"doctype": "BOM Operation", "operation_no": 1, "parentfield": "bom_operations",
|
||||
# "opn_description": "Development", "hour_rate": 10, "time_in_mins": 90},
|
||||
# {"doctype": "BOM Item", "item_code": "Home Desktop 300", "operation_no": 1,
|
||||
# "qty": 2, "rate": 20, "stock_uom": "Nos", "parentfield": "bom_materials"},
|
||||
# {"doctype": "BOM Item", "item_code": "Home Desktop 100", "operation_no": 1,
|
||||
# "qty": 1, "rate": 300, "stock_uom": "Nos", "parentfield": "bom_materials"},
|
||||
# {"doctype": "BOM Item", "item_code": "Nebula 7", "operation_no": 1,
|
||||
# "qty": 5, "stock_uom": "Nos", "parentfield": "bom_materials"},
|
||||
# ]
|
||||
#
|
||||
# base_bom_child = [
|
||||
# {"doctype": "BOM", "item": "Nebula 7", "quantity": 5,
|
||||
# "is_active": "Yes", "is_default": 1, "uom": "Nos"},
|
||||
# {"doctype": "BOM Operation", "operation_no": 1, "parentfield": "bom_operations",
|
||||
# "opn_description": "Development"},
|
||||
# {"doctype": "BOM Item", "item_code": "Android Jack S", "operation_no": 1,
|
||||
# "qty": 10, "stock_uom": "Nos", "parentfield": "bom_materials"}
|
||||
# ]
|
||||
#
|
||||
# base_bom_grandchild = [
|
||||
# {"doctype": "BOM", "item": "Android Jack S", "quantity": 1,
|
||||
# "is_active": "Yes", "is_default": 1, "uom": "Nos"},
|
||||
# {"doctype": "BOM Operation", "operation_no": 1, "parentfield": "bom_operations",
|
||||
# "opn_description": "Development"},
|
||||
# {"doctype": "BOM Item", "item_code": "Home Desktop 300", "operation_no": 1,
|
||||
# "qty": 3, "rate": 10, "stock_uom": "Nos", "parentfield": "bom_materials"}
|
||||
# ]
|
||||
#
|
||||
#
|
||||
# class TestPurchaseReceipt(unittest.TestCase):
|
||||
# def setUp(self):
|
||||
# webnotes.conn.begin()
|
||||
# load_data()
|
||||
#
|
||||
# def test_bom_validation(self):
|
||||
# # show throw error bacause bom no missing for sub-assembly item
|
||||
# bom_fg = copy.deepcopy(base_bom_fg)
|
||||
# self.assertRaises(webnotes.ValidationError, webnotes.insert, DocList(bom_fg))
|
||||
#
|
||||
# # main item is not a manufacturing item
|
||||
# bom_fg = copy.deepcopy(base_bom_fg)
|
||||
# bom_fg[0]["item"] = "Home Desktop 200"
|
||||
# bom_fg.pop(4)
|
||||
# self.assertRaises(webnotes.ValidationError, webnotes.insert, DocList(bom_fg))
|
||||
#
|
||||
# # operation no mentioed in material table not matching with operation table
|
||||
# bom_fg = copy.deepcopy(base_bom_fg)
|
||||
# bom_fg.pop(4)
|
||||
# bom_fg[2]["operation_no"] = 2
|
||||
# self.assertRaises(webnotes.ValidationError, webnotes.insert, DocList(bom_fg))
|
||||
#
|
||||
#
|
||||
# def test_bom(self):
|
||||
# gc_wrapper = webnotes.insert(DocList(base_bom_grandchild))
|
||||
# gc_wrapper.submit()
|
||||
#
|
||||
# bom_child = copy.deepcopy(base_bom_child)
|
||||
# bom_child[2]["bom_no"] = gc_wrapper.doc.name
|
||||
# child_wrapper = webnotes.insert(DocList(bom_child))
|
||||
# child_wrapper.submit()
|
||||
#
|
||||
# bom_fg = copy.deepcopy(base_bom_fg)
|
||||
# bom_fg[4]["bom_no"] = child_wrapper.doc.name
|
||||
# fg_wrapper = webnotes.insert(DocList(bom_fg))
|
||||
# fg_wrapper.load_from_db()
|
||||
#
|
||||
# self.check_bom_cost(fg_wrapper)
|
||||
#
|
||||
# self.check_flat_bom(fg_wrapper, child_wrapper, gc_wrapper)
|
||||
#
|
||||
# def check_bom_cost(self, fg_wrapper):
|
||||
# expected_values = {
|
||||
# "operating_cost": 15,
|
||||
# "raw_material_cost": 640,
|
||||
# "total_cost": 655
|
||||
# }
|
||||
#
|
||||
# for key in expected_values:
|
||||
# self.assertEqual(flt(expected_values[key]), flt(fg_wrapper.doc.fields.get(key)))
|
||||
#
|
||||
# def check_flat_bom(self, fg_wrapper, child_wrapper, gc_wrapper):
|
||||
# expected_flat_bom_items = {
|
||||
# ("Home Desktop 300", fg_wrapper.doc.name): (2, 20),
|
||||
# ("Home Desktop 100", fg_wrapper.doc.name): (1, 300),
|
||||
# ("Home Desktop 300", gc_wrapper.doc.name): (30, 10)
|
||||
# }
|
||||
#
|
||||
# self.assertEqual(len(fg_wrapper.doclist.get({"parentfield": "flat_bom_details"})), 3)
|
||||
#
|
||||
# for key, val in expected_flat_bom_items.items():
|
||||
# flat_bom = fg_wrapper.doclist.get({"parentfield": "flat_bom_details",
|
||||
# "item_code": key[0], "parent_bom": key[1]})[0]
|
||||
# self.assertEqual(val, (flat_bom.qty, flat_bom.rate))
|
||||
#
|
||||
#
|
||||
# def tearDown(self):
|
||||
# webnotes.conn.rollback()
|
||||
]
|
@ -1,8 +1,8 @@
|
||||
[
|
||||
{
|
||||
"creation": "2013-02-22 01:27:48",
|
||||
"creation": "2013-03-07 11:42:57",
|
||||
"docstatus": 0,
|
||||
"modified": "2013-03-07 07:03:18",
|
||||
"modified": "2013-06-04 13:13:28",
|
||||
"modified_by": "Administrator",
|
||||
"owner": "Administrator"
|
||||
},
|
||||
@ -80,25 +80,6 @@
|
||||
"oldfieldtype": "Link",
|
||||
"options": "UOM"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "parent_bom",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"label": "Parent BOM",
|
||||
"oldfieldname": "parent_bom",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "BOM",
|
||||
"print_width": "250px",
|
||||
"width": "250px"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "mat_detail_no",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "Mat Detail No"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "qty_consumed_per_unit",
|
||||
|
@ -29,9 +29,10 @@ class DocType:
|
||||
self.validate_bom()
|
||||
self.update_new_bom()
|
||||
bom_list = self.get_parent_boms()
|
||||
updated_bom = []
|
||||
for bom in bom_list:
|
||||
bom_obj = get_obj("BOM", bom, with_children=1)
|
||||
bom_obj.update_cost_and_exploded_items()
|
||||
updated_bom = bom_obj.update_cost_and_exploded_items(updated_bom)
|
||||
|
||||
webnotes.msgprint(_("BOM replaced"))
|
||||
|
||||
|
@ -241,40 +241,30 @@ class DocType:
|
||||
def get_raw_materials(self, bom_dict):
|
||||
""" Get raw materials considering sub-assembly items
|
||||
{
|
||||
"item_code": [qty_required, description, stock_uom]
|
||||
"item_code": [qty_required, description, stock_uom, min_order_qty]
|
||||
}
|
||||
"""
|
||||
for bom in bom_dict:
|
||||
if self.doc.use_multi_level_bom:
|
||||
# get all raw materials with sub assembly childs
|
||||
fl_bom_items = sql("""
|
||||
select
|
||||
item_code,ifnull(sum(qty_consumed_per_unit),0)*%s as qty,
|
||||
description, stock_uom, min_order_qty
|
||||
from
|
||||
(
|
||||
select distinct fb.name, fb.description, fb.item_code,
|
||||
fb.qty_consumed_per_unit, fb.stock_uom, it.min_order_qty
|
||||
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
|
||||
) a
|
||||
group by item_code,stock_uom
|
||||
""" , (flt(bom_dict[bom]), bom))
|
||||
fl_bom_items = sql("""select fb.item_code,
|
||||
ifnull(sum(fb.qty_consumed_per_unit), 0)*%s as qty,
|
||||
fb.description, fb.stock_uom, it.min_order_qty
|
||||
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""", (flt(bom_dict[bom]), bom))
|
||||
else:
|
||||
# Get all raw materials considering SA items as raw materials,
|
||||
# so no childs of SA items
|
||||
fl_bom_items = sql("""
|
||||
select bom_item.item_code,
|
||||
fl_bom_items = sql("""select bom_item.item_code,
|
||||
ifnull(sum(bom_item.qty_consumed_per_unit), 0) * %s,
|
||||
bom_item.description, bom_item.stock_uom, item.min_order_qty
|
||||
from `tabBOM Item` bom_item, tabItem item
|
||||
where bom_item.parent = %s and bom_item.docstatus < 2
|
||||
and bom_item.item_code = item.name
|
||||
group by item_code
|
||||
""", (flt(bom_dict[bom]), bom))
|
||||
and bom_item.item_code = item.name
|
||||
group by item_code""", (flt(bom_dict[bom]), bom))
|
||||
self.make_items_dict(fl_bom_items)
|
||||
|
||||
def make_items_dict(self, item_list):
|
||||
|
0
patches/june_2013/__init__.py
Normal file
0
patches/june_2013/__init__.py
Normal file
28
patches/june_2013/p01_update_bom_exploded_items.py
Normal file
28
patches/june_2013/p01_update_bom_exploded_items.py
Normal file
@ -0,0 +1,28 @@
|
||||
# ERPNext - web based ERP (http://erpnext.com)
|
||||
# Copyright (C) 2012 Web Notes Technologies Pvt Ltd
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import webnotes
|
||||
|
||||
def execute():
|
||||
updated_bom = []
|
||||
for bom in webnotes.conn.sql("select name from tabBOM where docstatus < 2"):
|
||||
webnotes.errprint(bom[0])
|
||||
if bom[0] not in updated_bom:
|
||||
bom_obj = webnotes.get_obj("BOM", bom[0], with_children=1)
|
||||
updated_bom = bom_obj.update_cost_and_exploded_items(updated_bom)
|
||||
|
||||
webnotes.errprint(updated_bom)
|
@ -252,4 +252,5 @@ patch_list = [
|
||||
"patches.may_2013.p03_update_support_ticket",
|
||||
"patches.may_2013.p04_reorder_level",
|
||||
"patches.may_2013.p05_update_cancelled_gl_entries",
|
||||
"patches.june_2013.p01_update_bom_exploded_items",
|
||||
]
|
@ -506,17 +506,13 @@ class DocType(StockController):
|
||||
|
||||
if self.doc.use_multi_level_bom:
|
||||
# get all raw materials with sub assembly childs
|
||||
fl_bom_sa_child_item = sql("""select
|
||||
item_code,ifnull(sum(qty_consumed_per_unit),0)*%s as qty,
|
||||
description,stock_uom
|
||||
from ( select distinct fb.name, fb.description, fb.item_code,
|
||||
fb.qty_consumed_per_unit, fb.stock_uom
|
||||
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
|
||||
) a
|
||||
group by item_code, stock_uom""" , (qty, self.doc.bom_no), as_dict=1)
|
||||
fl_bom_sa_child_item = sql("""select fb.item_code,
|
||||
ifnull(sum(fb.qty_consumed_per_unit),0)*%s as qty, fb.description, fb.stock_uom
|
||||
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)
|
||||
@ -524,10 +520,10 @@ class DocType(StockController):
|
||||
# Get all raw materials considering multi level BOM,
|
||||
# if multi level bom consider childs of Sub-Assembly items
|
||||
fl_bom_sa_items = sql("""select item_code,
|
||||
ifnull(sum(qty_consumed_per_unit), 0) * '%s' as qty,
|
||||
ifnull(sum(qty_consumed_per_unit), 0) *%s as qty,
|
||||
description, stock_uom from `tabBOM Item`
|
||||
where parent = '%s' and docstatus < 2
|
||||
group by item_code""" % (qty, self.doc.bom_no), as_dict=1)
|
||||
where parent = %s and 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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user