mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-04 05:58:27 +00:00
merge, fixed production cleanup patch
This commit is contained in:
5
manufacturing/__init__.py
Normal file
5
manufacturing/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from __future__ import unicode_literals
|
||||
install_docs = [
|
||||
{"doctype":"Role", "role_name":"Manufacturing Manager", "name":"Manufacturing Manager"},
|
||||
{"doctype":"Role", "role_name":"Manufacturing User", "name":"Manufacturing User"},
|
||||
]
|
||||
1
manufacturing/doctype/__init__.py
Normal file
1
manufacturing/doctype/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from __future__ import unicode_literals
|
||||
1
manufacturing/doctype/bom/__init__.py
Normal file
1
manufacturing/doctype/bom/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from __future__ import unicode_literals
|
||||
179
manufacturing/doctype/bom/bom.js
Normal file
179
manufacturing/doctype/bom/bom.js
Normal file
@@ -0,0 +1,179 @@
|
||||
// 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/>.
|
||||
|
||||
// On REFRESH
|
||||
cur_frm.cscript.refresh = function(doc,dt,dn){
|
||||
cur_frm.toggle_enable("item", doc.__islocal);
|
||||
if (!doc.__islocal && doc.docstatus==0) {
|
||||
cur_frm.set_intro("Submit the BOM to use it in production");
|
||||
}
|
||||
}
|
||||
|
||||
cur_frm.cscript.item = function(doc, dt, dn) {
|
||||
if (doc.item) {
|
||||
get_server_fields('get_item_detail',doc.item,'',doc,dt,dn,1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cur_frm.cscript.workstation = function(doc,dt,dn) {
|
||||
var d = locals[dt][dn];
|
||||
if (d.workstation) {
|
||||
var callback = function(r, rt) {
|
||||
calculate_op_cost(doc, dt, dn);
|
||||
calculate_total(doc);
|
||||
}
|
||||
get_server_fields('get_workstation_details', d.workstation,
|
||||
'bom_operations', doc, dt, dn, 1, callback);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cur_frm.cscript.hour_rate = function(doc, dt, dn) {
|
||||
calculate_op_cost(doc, dt, dn);
|
||||
calculate_total(doc);
|
||||
}
|
||||
|
||||
|
||||
cur_frm.cscript.time_in_mins = cur_frm.cscript.hour_rate;
|
||||
|
||||
cur_frm.cscript.item_code = function(doc, cdt, cdn) {
|
||||
get_bom_material_detail(doc, cdt, cdn);
|
||||
}
|
||||
|
||||
cur_frm.cscript.bom_no = function(doc, cdt, cdn) {
|
||||
get_bom_material_detail(doc, cdt, cdn);
|
||||
}
|
||||
|
||||
var get_bom_material_detail= function(doc, cdt, cdn) {
|
||||
var d = locals[cdt][cdn];
|
||||
if (d.item_code) {
|
||||
wn.call({
|
||||
doc: cur_frm.doc,
|
||||
method: "get_bom_material_detail",
|
||||
args: {
|
||||
'item_code': d.item_code,
|
||||
'bom_no': d.bom_no != null ? d.bom_no: '',
|
||||
'qty': d.qty
|
||||
},
|
||||
callback: function(r) {
|
||||
d = locals[cdt][cdn];
|
||||
$.extend(d, r.message);
|
||||
refresh_field("bom_materials");
|
||||
doc = locals[doc.doctype][doc.name];
|
||||
calculate_rm_cost(doc, cdt, cdn);
|
||||
calculate_total(doc);
|
||||
},
|
||||
freeze: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
cur_frm.cscript.qty = function(doc, cdt, cdn) {
|
||||
calculate_rm_cost(doc, cdt, cdn);
|
||||
calculate_total(doc);
|
||||
}
|
||||
|
||||
cur_frm.cscript.rate = function(doc, cdt, cdn) {
|
||||
var d = locals[cdt][cdn];
|
||||
if (d.bom_no) {
|
||||
msgprint("You can not change rate if BOM mentioned agianst any item");
|
||||
get_bom_material_detail(doc, cdt, cdn);
|
||||
} else {
|
||||
calculate_rm_cost(doc, cdt, cdn);
|
||||
calculate_total(doc);
|
||||
}
|
||||
}
|
||||
|
||||
cur_frm.cscript.is_default = function(doc, cdt, cdn) {
|
||||
if (doc.docstatus == 1)
|
||||
$c_obj(make_doclist(cdt, cdn), 'manage_default_bom', '', '');
|
||||
}
|
||||
|
||||
|
||||
cur_frm.cscript.is_active = function(doc, dt, dn) {
|
||||
if (!doc.__islocal)
|
||||
$c_obj(make_doclist(dt, dn), 'manage_active_bom', '', '');
|
||||
}
|
||||
|
||||
var calculate_op_cost = function(doc, dt, dn) {
|
||||
var op = getchildren('BOM Operation', doc.name, 'bom_operations');
|
||||
total_op_cost = 0;
|
||||
for(var i=0;i<op.length;i++) {
|
||||
op_cost = flt(flt(op[i].hour_rate) * flt(op[i].time_in_mins) / 60, 2);
|
||||
set_multiple('BOM Operation',op[i].name, {'operating_cost': op_cost}, 'bom_operations');
|
||||
total_op_cost += op_cost;
|
||||
}
|
||||
doc.operating_cost = total_op_cost;
|
||||
refresh_field('operating_cost');
|
||||
}
|
||||
|
||||
var calculate_rm_cost = function(doc, dt, dn) {
|
||||
var rm = getchildren('BOM Item', doc.name, 'bom_materials');
|
||||
total_rm_cost = 0;
|
||||
for(var i=0;i<rm.length;i++) {
|
||||
amt = flt(rm[i].rate) * flt(rm[i].qty);
|
||||
set_multiple('BOM Item',rm[i].name, {'amount': amt}, 'bom_materials');
|
||||
set_multiple('BOM Item',rm[i].name,
|
||||
{'qty_consumed_per_unit': flt(rm[i].qty)/flt(doc.quantity)}, 'bom_materials');
|
||||
total_rm_cost += amt;
|
||||
}
|
||||
doc.raw_material_cost = total_rm_cost;
|
||||
refresh_field('raw_material_cost');
|
||||
}
|
||||
|
||||
|
||||
// Calculate Total Cost
|
||||
var calculate_total = function(doc) {
|
||||
doc.total_cost = flt(doc.raw_material_cost) + flt(doc.operating_cost);
|
||||
refresh_field('total_cost');
|
||||
}
|
||||
|
||||
|
||||
cur_frm.fields_dict['item'].get_query = function(doc) {
|
||||
return 'SELECT DISTINCT `tabItem`.`name`, `tabItem`.description FROM `tabItem` \
|
||||
WHERE is_manufactured_item = "Yes" and (IFNULL(`tabItem`.`end_of_life`,"") = "" OR \
|
||||
`tabItem`.`end_of_life` = "0000-00-00" OR `tabItem`.`end_of_life` > NOW()) AND \
|
||||
`tabItem`.`%(key)s` like "%s" ORDER BY `tabItem`.`name` LIMIT 50';
|
||||
}
|
||||
|
||||
cur_frm.fields_dict['project_name'].get_query = function(doc, dt, dn) {
|
||||
return 'SELECT `tabProject`.name FROM `tabProject` \
|
||||
WHERE `tabProject`.status not in ("Completed", "Cancelled") \
|
||||
AND `tabProject`.name LIKE "%s" ORDER BY `tabProject`.name ASC LIMIT 50';
|
||||
}
|
||||
|
||||
cur_frm.fields_dict['bom_materials'].grid.get_field('item_code').get_query = function(doc) {
|
||||
return 'SELECT DISTINCT `tabItem`.`name`, `tabItem`.description FROM `tabItem` \
|
||||
WHERE (IFNULL(`tabItem`.`end_of_life`,"") = "" OR `tabItem`.`end_of_life` = "0000-00-00" \
|
||||
OR `tabItem`.`end_of_life` > NOW()) AND `tabItem`.`%(key)s` like "%s" \
|
||||
ORDER BY `tabItem`.`name` LIMIT 50';
|
||||
}
|
||||
|
||||
cur_frm.fields_dict['bom_materials'].grid.get_field('bom_no').get_query = function(doc) {
|
||||
var d = locals[this.doctype][this.docname];
|
||||
return 'SELECT DISTINCT `tabBOM`.`name`, `tabBOM`.`remarks` FROM `tabBOM` \
|
||||
WHERE `tabBOM`.`item` = "' + d.item_code + '" AND `tabBOM`.`is_active` = "Yes" AND \
|
||||
`tabBOM`.docstatus = 1 AND `tabBOM`.`name` like "%s" \
|
||||
ORDER BY `tabBOM`.`name` LIMIT 50';
|
||||
}
|
||||
|
||||
cur_frm.cscript.validate = function(doc, dt, dn) {
|
||||
calculate_op_cost(doc, dt, dn);
|
||||
calculate_rm_cost(doc, dt, dn);
|
||||
calculate_total(doc);
|
||||
}
|
||||
433
manufacturing/doctype/bom/bom.py
Normal file
433
manufacturing/doctype/bom/bom.py
Normal file
@@ -0,0 +1,433 @@
|
||||
# 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
|
||||
from webnotes.utils import cint, cstr, flt, now, nowdate
|
||||
from webnotes.model.doc import Document, addchild
|
||||
from webnotes.model.wrapper import getlist
|
||||
from webnotes.model.code import get_obj
|
||||
from webnotes import msgprint
|
||||
|
||||
sql = webnotes.conn.sql
|
||||
|
||||
|
||||
class DocType:
|
||||
def __init__(self, doc, doclist=[]):
|
||||
self.doc = doc
|
||||
self.doclist = doclist
|
||||
|
||||
def autoname(self):
|
||||
last_name = sql("""select max(name) from `tabBOM`
|
||||
where name like 'BOM/%s/%%'""" % self.doc.item)
|
||||
if last_name:
|
||||
idx = cint(cstr(last_name[0][0]).split('/')[-1]) + 1
|
||||
else:
|
||||
idx = 1
|
||||
self.doc.name = 'BOM/' + self.doc.item + ('/%.3i' % idx)
|
||||
|
||||
|
||||
def get_item_det(self, item_code):
|
||||
item = sql("""select name, is_asset_item, is_purchase_item, docstatus,
|
||||
is_sub_contracted_item, description, stock_uom, default_bom,
|
||||
last_purchase_rate, standard_rate, is_manufactured_item
|
||||
from `tabItem` where item_code = %s""", item_code, as_dict = 1)
|
||||
|
||||
return item
|
||||
|
||||
|
||||
def get_item_detail(self, item_code):
|
||||
""" Get stock uom and description for finished good item"""
|
||||
|
||||
item = self.get_item_det(item_code)
|
||||
ret={
|
||||
'description' : item and item[0]['description'] or '',
|
||||
'uom' : item and item[0]['stock_uom'] or ''
|
||||
}
|
||||
return ret
|
||||
|
||||
|
||||
|
||||
def get_workstation_details(self,workstation):
|
||||
""" Fetch hour rate from workstation master"""
|
||||
|
||||
ws = sql("select hour_rate from `tabWorkstation` where name = %s",
|
||||
workstation , as_dict = 1)
|
||||
return {'hour_rate' : ws and flt(ws[0]['hour_rate']) or ''}
|
||||
|
||||
|
||||
def validate_rm_item(self, item):
|
||||
""" Validate raw material items"""
|
||||
|
||||
if item[0]['name'] == self.doc.item:
|
||||
msgprint("Item_code: %s in materials tab cannot be same as FG Item",
|
||||
item[0]['name'], raise_exception=1)
|
||||
|
||||
if item and item[0]['is_asset_item'] == 'Yes':
|
||||
msgprint("Item: %s is an asset item, please check", item[0]['name'], raise_exception=1)
|
||||
|
||||
if not item or item[0]['docstatus'] == 2:
|
||||
msgprint("Item %s does not exist in system" % item[0]['item_code'], raise_exception = 1)
|
||||
|
||||
|
||||
|
||||
def get_bom_material_detail(self):
|
||||
""" Get raw material details like uom, desc and rate"""
|
||||
|
||||
arg = webnotes.form_dict.get('args')
|
||||
import json
|
||||
arg = json.loads(arg)
|
||||
|
||||
item = self.get_item_det(arg['item_code'])
|
||||
self.validate_rm_item(item)
|
||||
|
||||
arg['bom_no'] = arg['bom_no'] or item and cstr(item[0]['default_bom']) or ''
|
||||
arg.update(item[0])
|
||||
|
||||
rate = self.get_rm_rate(arg)
|
||||
ret_item = {
|
||||
'description' : item and arg['description'] or '',
|
||||
'stock_uom' : item and arg['stock_uom'] or '',
|
||||
'bom_no' : arg['bom_no'],
|
||||
'rate' : rate
|
||||
}
|
||||
return ret_item
|
||||
|
||||
|
||||
def get_rm_rate(self, arg):
|
||||
""" Get raw material rate as per selected method, if bom exists takes bom cost """
|
||||
|
||||
if arg['bom_no']:
|
||||
rate = self.get_bom_unitcost(arg['bom_no'])
|
||||
elif arg and (arg['is_purchase_item'] == 'Yes' or arg['is_sub_contracted_item'] == 'Yes'):
|
||||
if self.doc.rm_cost_as_per == 'Valuation Rate':
|
||||
rate = self.get_valuation_rate(arg)
|
||||
elif self.doc.rm_cost_as_per == 'Last Purchase Rate':
|
||||
rate = arg['last_purchase_rate']
|
||||
elif self.doc.rm_cost_as_per == 'Standard Rate':
|
||||
rate = arg['standard_rate']
|
||||
|
||||
return rate
|
||||
|
||||
def get_bom_unitcost(self, bom_no):
|
||||
bom = sql("""select name, total_cost/quantity as unit_cost from `tabBOM`
|
||||
where is_active = 'Yes' and name = %s""", bom_no, as_dict=1)
|
||||
return bom and bom[0]['unit_cost'] or 0
|
||||
|
||||
def get_valuation_rate(self, arg):
|
||||
""" Get average valuation rate of relevant warehouses
|
||||
as per valuation method (MAR/FIFO)
|
||||
as on costing date
|
||||
"""
|
||||
|
||||
dt = self.doc.costing_date or nowdate()
|
||||
time = self.doc.costing_date == nowdate() and now().split()[1] or '23:59'
|
||||
warehouse = sql("select warehouse from `tabBin` where item_code = %s", arg['item_code'])
|
||||
rate = []
|
||||
for wh in warehouse:
|
||||
r = get_obj('Valuation Control').get_incoming_rate(dt, time,
|
||||
arg['item_code'], wh[0], qty=arg.get('qty', 0))
|
||||
if r:
|
||||
rate.append(r)
|
||||
|
||||
return rate and flt(sum(rate))/len(rate) or 0
|
||||
|
||||
|
||||
|
||||
def manage_default_bom(self):
|
||||
""" Uncheck others if current one is selected as default,
|
||||
update default bom in item master
|
||||
"""
|
||||
|
||||
if self.doc.is_default and self.doc.is_active == 'Yes':
|
||||
sql("update `tabBOM` set is_default = 0 where name != %s and item=%s",
|
||||
(self.doc.name, self.doc.item))
|
||||
|
||||
# update default bom in Item Master
|
||||
sql("update `tabItem` set default_bom = %s where name = %s",
|
||||
(self.doc.name, self.doc.item))
|
||||
else:
|
||||
sql("update `tabItem` set default_bom = '' where name = %s and default_bom = %s",
|
||||
(self.doc.item, self.doc.name))
|
||||
|
||||
|
||||
def manage_active_bom(self):
|
||||
""" Manage active/inactive """
|
||||
if self.doc.is_active == 'Yes':
|
||||
self.validate()
|
||||
else:
|
||||
self.check_active_parent_boms()
|
||||
|
||||
|
||||
|
||||
def check_active_parent_boms(self):
|
||||
""" Check parent BOM before making it inactive """
|
||||
act_pbom = sql("""select distinct t1.parent from `tabBOM Item` t1, `tabBOM` t2
|
||||
where t1.bom_no =%s and t2.name = t1.parent and t2.is_active = 'Yes'
|
||||
and t2.docstatus = 1 and t1.docstatus =1 """, self.doc.name)
|
||||
if act_pbom and act_pbom[0][0]:
|
||||
msgprint("""Sorry cannot inactivate as BOM: %s is child
|
||||
of one or many other active parent BOMs""" % self.doc.name, raise_exception=1)
|
||||
|
||||
|
||||
|
||||
def calculate_cost(self):
|
||||
"""Calculate bom totals"""
|
||||
self.calculate_op_cost()
|
||||
self.calculate_rm_cost()
|
||||
self.doc.total_cost = self.doc.raw_material_cost + self.doc.operating_cost
|
||||
self.doc.modified = now()
|
||||
self.doc.save()
|
||||
|
||||
|
||||
def calculate_op_cost(self):
|
||||
"""Update workstation rate and calculates totals"""
|
||||
total_op_cost = 0
|
||||
for d in getlist(self.doclist, 'bom_operations'):
|
||||
if d.hour_rate and d.time_in_mins:
|
||||
d.operating_cost = flt(d.hour_rate) * flt(d.time_in_mins) / 60.0
|
||||
d.save()
|
||||
total_op_cost += flt(d.operating_cost)
|
||||
self.doc.operating_cost = total_op_cost
|
||||
|
||||
|
||||
|
||||
def calculate_rm_cost(self):
|
||||
"""Fetch RM rate as per today's valuation rate and calculate totals"""
|
||||
total_rm_cost = 0
|
||||
for d in getlist(self.doclist, 'bom_materials'):
|
||||
if d.bom_no:
|
||||
d.rate = self.get_bom_unitcost(d.bom_no)
|
||||
d.amount = flt(d.rate) * flt(d.qty)
|
||||
d.qty_consumed_per_unit = flt(d.qty) / flt(self.doc.quantity)
|
||||
d.save()
|
||||
total_rm_cost += d.amount
|
||||
self.doc.raw_material_cost = total_rm_cost
|
||||
|
||||
|
||||
|
||||
def validate_main_item(self):
|
||||
""" Validate main FG item"""
|
||||
item = self.get_item_det(self.doc.item)
|
||||
if not item:
|
||||
msgprint("Item %s does not exists in the system or expired." %
|
||||
self.doc.item, raise_exception = 1)
|
||||
|
||||
elif item[0]['is_manufactured_item'] != 'Yes' \
|
||||
and item[0]['is_sub_contracted_item'] != 'Yes':
|
||||
msgprint("""As Item: %s is not a manufactured / sub-contracted item, \
|
||||
you can not make BOM for it""" % self.doc.item, raise_exception = 1)
|
||||
|
||||
|
||||
def validate_operations(self):
|
||||
""" Check duplicate operation no"""
|
||||
self.op = []
|
||||
for d in getlist(self.doclist, 'bom_operations'):
|
||||
if cstr(d.operation_no) in self.op:
|
||||
msgprint("Operation no: %s is repeated in Operations Table" %
|
||||
d.operation_no, raise_exception=1)
|
||||
else:
|
||||
# add operation in op list
|
||||
self.op.append(cstr(d.operation_no))
|
||||
|
||||
def validate_materials(self):
|
||||
""" Validate raw material entries """
|
||||
check_list = []
|
||||
for m in getlist(self.doclist, 'bom_materials'):
|
||||
# check if operation no not in op table
|
||||
if cstr(m.operation_no) not in self.op:
|
||||
msgprint("""Operation no: %s against item: %s at row no: %s \
|
||||
is not present at Operations table""" %
|
||||
(m.operation_no, m.item_code, m.idx), raise_exception = 1)
|
||||
|
||||
item = self.get_item_det(m.item_code)
|
||||
if item[0]['is_manufactured_item'] == 'Yes' or \
|
||||
item[0]['is_sub_contracted_item'] == 'Yes':
|
||||
if not m.bom_no:
|
||||
msgprint("Please enter BOM No aginst item: %s at row no: %s" %
|
||||
(m.item_code, m.idx), raise_exception=1)
|
||||
else:
|
||||
self.validate_bom_no(m.item_code, m.bom_no, m.idx)
|
||||
|
||||
elif m.bom_no:
|
||||
msgprint("""As Item %s is not a manufactured / sub-contracted item, \
|
||||
you can enter BOM against it (Row No: %s).""" %
|
||||
(m.item_code, m.idx), raise_exception = 1)
|
||||
|
||||
if flt(m.qty) <= 0:
|
||||
msgprint("Please enter qty against raw material: %s at row no: %s" %
|
||||
(m.item_code, m.idx), raise_exception = 1)
|
||||
|
||||
self.check_if_item_repeated(m.item_code, m.operation_no, check_list)
|
||||
|
||||
|
||||
def validate_bom_no(self, item, bom_no, idx):
|
||||
"""Validate BOM No of sub-contracted items"""
|
||||
bom = sql("""select name from `tabBOM` where name = %s and item = %s
|
||||
and ifnull(is_active, 'No') = 'Yes' and docstatus < 2 """,
|
||||
(bom_no, item), as_dict =1)
|
||||
if not bom:
|
||||
msgprint("""Incorrect BOM No: %s against item: %s at row no: %s.
|
||||
It may be inactive or cancelled or for some other item.""" %
|
||||
(bom_no, item, idx), raise_exception = 1)
|
||||
|
||||
|
||||
|
||||
def check_if_item_repeated(self, item, op, check_list):
|
||||
if [cstr(item), cstr(op)] in check_list:
|
||||
msgprint("Item %s has been entered twice against same operation" %
|
||||
item, raise_exception = 1)
|
||||
else:
|
||||
check_list.append([cstr(item), cstr(op)])
|
||||
|
||||
|
||||
def validate(self):
|
||||
self.validate_main_item()
|
||||
self.validate_operations()
|
||||
self.validate_materials()
|
||||
|
||||
def check_recursion(self):
|
||||
""" Check whether recursion occurs in any bom"""
|
||||
|
||||
check_list = [['parent', 'bom_no', 'parent'], ['bom_no', 'parent', 'child']]
|
||||
for d in check_list:
|
||||
bom_list, count = [self.doc.name], 0
|
||||
while (len(bom_list) > count ):
|
||||
boms = sql(" select %s from `tabBOM Item` where %s = '%s' " %
|
||||
(d[0], d[1], cstr(bom_list[count])))
|
||||
count = count + 1
|
||||
for b in boms:
|
||||
if b[0] == self.doc.name:
|
||||
msgprint("""Recursion Occured => '%s' cannot be '%s' of '%s'.
|
||||
""" % (cstr(b), cstr(d[2]), self.doc.name), raise_exception = 1)
|
||||
if b[0]:
|
||||
bom_list.append(b[0])
|
||||
|
||||
|
||||
def on_update(self):
|
||||
self.check_recursion()
|
||||
self.update_cost_by_traversing()
|
||||
self.update_flat_bom_by_traversing()
|
||||
|
||||
|
||||
|
||||
def add_to_flat_bom_detail(self):
|
||||
"Add items to Flat BOM table"
|
||||
self.doclist = self.doc.clear_table(self.doclist, 'flat_bom_details', 1)
|
||||
for d in self.cur_flat_bom_items:
|
||||
ch = addchild(self.doc, 'flat_bom_details', 'BOM Explosion Item', 1, self.doclist)
|
||||
for i in d.keys():
|
||||
ch.fields[i] = d[i]
|
||||
ch.docstatus = self.doc.docstatus
|
||||
ch.save(1)
|
||||
self.doc.save()
|
||||
|
||||
|
||||
def get_child_flat_bom_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)
|
||||
for d in child_fb_items:
|
||||
self.cur_flat_bom_items.append({
|
||||
'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)
|
||||
|
||||
})
|
||||
|
||||
|
||||
def get_flat_bom_items(self):
|
||||
""" Get all raw materials including items from child bom"""
|
||||
self.cur_flat_bom_items = []
|
||||
for d in getlist(self.doclist, 'bom_materials'):
|
||||
if d.bom_no:
|
||||
self.get_child_flat_bom_items(d.bom_no, d.qty)
|
||||
else:
|
||||
self.cur_flat_bom_items.append({
|
||||
'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)
|
||||
})
|
||||
|
||||
|
||||
def update_flat_bom(self):
|
||||
""" Update Flat BOM, following will be correct data"""
|
||||
self.get_flat_bom_items()
|
||||
self.add_to_flat_bom_detail()
|
||||
|
||||
|
||||
def get_parent_bom_list(self, bom_no):
|
||||
p_bom = sql("select parent from `tabBOM Item` where bom_no = '%s'" % bom_no)
|
||||
return p_bom and [i[0] for i in p_bom] or []
|
||||
|
||||
|
||||
def on_submit(self):
|
||||
self.manage_default_bom()
|
||||
|
||||
def on_cancel(self):
|
||||
# check if used in any other bom
|
||||
par = sql("""select t1.parent from `tabBOM Item` t1, `tabBOM` t2
|
||||
where t1.parent = t2.name and t1.bom_no = %s and t1.docstatus = 1
|
||||
and t2.is_active = 'Yes'""", self.doc.name)
|
||||
if par:
|
||||
msgprint("""BOM can not be cancelled, as it is a child item \
|
||||
in following active BOM %s""" % [d[0] for d in par], raise_exception=1)
|
||||
|
||||
webnotes.conn.set(self.doc, "is_active", "No")
|
||||
webnotes.conn.set(self.doc, "is_default", 0)
|
||||
self.manage_default_bom()
|
||||
self.update_flat_bom_by_traversing()
|
||||
|
||||
def traverse_tree(self):
|
||||
def _get_childs(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
|
||||
while(count < len(bom_list)):
|
||||
for child_bom in _get_childs(bom_list[count]):
|
||||
if child_bom not in bom_list:
|
||||
bom_list.append(child_bom)
|
||||
count += 1
|
||||
|
||||
return bom_list
|
||||
|
||||
def update_cost_by_traversing(self):
|
||||
bom_list = self.traverse_tree()
|
||||
bom_list.reverse()
|
||||
for bom in bom_list:
|
||||
get_obj("BOM", bom, with_children=1).calculate_cost()
|
||||
|
||||
def update_flat_bom_by_traversing(self):
|
||||
bom_list = self.traverse_tree()
|
||||
bom_list.reverse()
|
||||
for bom in bom_list:
|
||||
get_obj("BOM", bom, with_children=1).update_flat_bom()
|
||||
342
manufacturing/doctype/bom/bom.txt
Normal file
342
manufacturing/doctype/bom/bom.txt
Normal file
@@ -0,0 +1,342 @@
|
||||
[
|
||||
{
|
||||
"owner": "Administrator",
|
||||
"docstatus": 0,
|
||||
"creation": "2012-07-03 13:30:03",
|
||||
"modified_by": "Administrator",
|
||||
"modified": "2012-12-10 18:30:00"
|
||||
},
|
||||
{
|
||||
"istable": 0,
|
||||
"in_create": 0,
|
||||
"allow_print": 0,
|
||||
"search_fields": "item",
|
||||
"module": "Manufacturing",
|
||||
"document_type": "Master",
|
||||
"allow_attach": 0,
|
||||
"read_only": 0,
|
||||
"allow_email": 0,
|
||||
"hide_heading": 0,
|
||||
"issingle": 0,
|
||||
"name": "__common__",
|
||||
"default_print_format": "Standard",
|
||||
"allow_rename": 0,
|
||||
"doctype": "DocType",
|
||||
"is_submittable": 1,
|
||||
"hide_toolbar": 0,
|
||||
"allow_copy": 0
|
||||
},
|
||||
{
|
||||
"name": "__common__",
|
||||
"parent": "BOM",
|
||||
"doctype": "DocField",
|
||||
"parenttype": "DocType",
|
||||
"parentfield": "fields"
|
||||
},
|
||||
{
|
||||
"name": "__common__",
|
||||
"parent": "BOM",
|
||||
"read": 1,
|
||||
"doctype": "DocPerm",
|
||||
"parenttype": "DocType",
|
||||
"parentfield": "permissions"
|
||||
},
|
||||
{
|
||||
"name": "BOM",
|
||||
"doctype": "DocType"
|
||||
},
|
||||
{
|
||||
"oldfieldtype": "Column Break",
|
||||
"doctype": "DocField",
|
||||
"width": "50%",
|
||||
"fieldname": "column_break0",
|
||||
"fieldtype": "Column Break",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"description": "Select the item code for which Bill of Material is being created",
|
||||
"oldfieldtype": "Link",
|
||||
"doctype": "DocField",
|
||||
"label": "Item",
|
||||
"oldfieldname": "item",
|
||||
"permlevel": 0,
|
||||
"fieldname": "item",
|
||||
"fieldtype": "Link",
|
||||
"search_index": 1,
|
||||
"reqd": 1,
|
||||
"in_filter": 1,
|
||||
"options": "Item"
|
||||
},
|
||||
{
|
||||
"description": "Total quantity of items for which raw materials required and operations done will be defined",
|
||||
"oldfieldtype": "Currency",
|
||||
"doctype": "DocField",
|
||||
"label": "Quantity",
|
||||
"oldfieldname": "quantity",
|
||||
"fieldname": "quantity",
|
||||
"fieldtype": "Currency",
|
||||
"reqd": 1,
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"width": "50%",
|
||||
"fieldname": "column_break1",
|
||||
"fieldtype": "Column Break",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"no_copy": 1,
|
||||
"oldfieldtype": "Select",
|
||||
"allow_on_submit": 1,
|
||||
"doctype": "DocField",
|
||||
"label": "Is Active",
|
||||
"oldfieldname": "is_active",
|
||||
"permlevel": 0,
|
||||
"fieldname": "is_active",
|
||||
"fieldtype": "Select",
|
||||
"reqd": 1,
|
||||
"hidden": 0,
|
||||
"options": "\nYes\nNo"
|
||||
},
|
||||
{
|
||||
"no_copy": 1,
|
||||
"oldfieldtype": "Check",
|
||||
"allow_on_submit": 1,
|
||||
"doctype": "DocField",
|
||||
"label": "Is Default",
|
||||
"oldfieldname": "is_default",
|
||||
"fieldname": "is_default",
|
||||
"fieldtype": "Check",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"oldfieldtype": "Section Break",
|
||||
"doctype": "DocField",
|
||||
"label": "Operations",
|
||||
"fieldname": "operations",
|
||||
"fieldtype": "Section Break",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"description": "Specify the operations, operating cost and give a unique Operation no to your operations.",
|
||||
"oldfieldtype": "Table",
|
||||
"doctype": "DocField",
|
||||
"label": "BOM Operations",
|
||||
"oldfieldname": "bom_operations",
|
||||
"options": "BOM Operation",
|
||||
"fieldname": "bom_operations",
|
||||
"fieldtype": "Table",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"oldfieldtype": "Section Break",
|
||||
"doctype": "DocField",
|
||||
"label": "Materials",
|
||||
"fieldname": "materials",
|
||||
"fieldtype": "Section Break",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"label": "Consider Raw Material Cost As Per",
|
||||
"options": "Valuation Rate\nLast Purchase Rate\nStandard Rate",
|
||||
"fieldname": "rm_cost_as_per",
|
||||
"fieldtype": "Select",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"description": "Enter the raw materials required to manufacture the BOM item. Specify the operation no as entered in the previous tab which will be performed on the raw materials entered.",
|
||||
"oldfieldtype": "Table",
|
||||
"doctype": "DocField",
|
||||
"label": "BOM Item",
|
||||
"oldfieldname": "bom_materials",
|
||||
"options": "BOM Item",
|
||||
"fieldname": "bom_materials",
|
||||
"fieldtype": "Table",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"oldfieldtype": "Section Break",
|
||||
"doctype": "DocField",
|
||||
"label": "Costing",
|
||||
"fieldname": "costing",
|
||||
"fieldtype": "Section Break",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"label": "Raw Material Cost",
|
||||
"fieldname": "raw_material_cost",
|
||||
"fieldtype": "Float",
|
||||
"permlevel": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"label": "Operating Cost",
|
||||
"fieldname": "operating_cost",
|
||||
"fieldtype": "Float",
|
||||
"permlevel": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "col_break24",
|
||||
"fieldtype": "Column Break",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"label": "Total Cost",
|
||||
"fieldname": "total_cost",
|
||||
"fieldtype": "Float",
|
||||
"permlevel": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"label": "More Info",
|
||||
"fieldname": "more_info_section",
|
||||
"fieldtype": "Section Break",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"description": "Select name of the project if BOM need to be created against any project",
|
||||
"oldfieldtype": "Link",
|
||||
"doctype": "DocField",
|
||||
"label": "Project Name",
|
||||
"oldfieldname": "project_name",
|
||||
"options": "Project",
|
||||
"fieldname": "project_name",
|
||||
"fieldtype": "Link",
|
||||
"permlevel": 0,
|
||||
"in_filter": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"label": "Item UOM",
|
||||
"options": "link:UOM",
|
||||
"fieldname": "uom",
|
||||
"fieldtype": "Select",
|
||||
"permlevel": 1
|
||||
},
|
||||
{
|
||||
"oldfieldtype": "Text",
|
||||
"doctype": "DocField",
|
||||
"label": "Item Description",
|
||||
"oldfieldname": "description",
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Small Text",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "col_break23",
|
||||
"fieldtype": "Column Break",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"oldfieldtype": "Data",
|
||||
"doctype": "DocField",
|
||||
"label": "Maintained By",
|
||||
"oldfieldname": "maintained_by",
|
||||
"fieldname": "maintained_by",
|
||||
"fieldtype": "Data",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"print_hide": 1,
|
||||
"no_copy": 1,
|
||||
"doctype": "DocField",
|
||||
"label": "Amended From",
|
||||
"options": "Sales Invoice",
|
||||
"fieldname": "amended_from",
|
||||
"fieldtype": "Link",
|
||||
"permlevel": 1
|
||||
},
|
||||
{
|
||||
"print_hide": 1,
|
||||
"description": "The date at which current entry is corrected in the system.",
|
||||
"no_copy": 1,
|
||||
"depends_on": "eval:doc.amended_from",
|
||||
"doctype": "DocField",
|
||||
"label": "Amendment Date",
|
||||
"fieldname": "amendment_date",
|
||||
"fieldtype": "Date",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"no_copy": 1,
|
||||
"oldfieldtype": "Text",
|
||||
"doctype": "DocField",
|
||||
"label": "Remarks",
|
||||
"oldfieldname": "remarks",
|
||||
"fieldname": "remarks",
|
||||
"fieldtype": "Small Text",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"print_hide": 0,
|
||||
"doctype": "DocField",
|
||||
"label": "BOM Explosion Items",
|
||||
"options": "Simple",
|
||||
"fieldname": "section_break0",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"print_hide": 1,
|
||||
"permlevel": 1,
|
||||
"no_copy": 1,
|
||||
"oldfieldtype": "Table",
|
||||
"doctype": "DocField",
|
||||
"label": "BOM Explosion Item",
|
||||
"oldfieldname": "flat_bom_details",
|
||||
"default": "No Toolbar",
|
||||
"fieldname": "flat_bom_details",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
"options": "BOM Explosion Item"
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"doctype": "DocPerm",
|
||||
"submit": 1,
|
||||
"write": 1,
|
||||
"role": "System Manager",
|
||||
"cancel": 1,
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"doctype": "DocPerm",
|
||||
"role": "System Manager",
|
||||
"permlevel": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"doctype": "DocPerm",
|
||||
"submit": 1,
|
||||
"write": 1,
|
||||
"role": "Manufacturing Manager",
|
||||
"cancel": 1,
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"doctype": "DocPerm",
|
||||
"role": "Manufacturing Manager",
|
||||
"permlevel": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"doctype": "DocPerm",
|
||||
"submit": 1,
|
||||
"write": 1,
|
||||
"role": "Manufacturing User",
|
||||
"cancel": 1,
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"doctype": "DocPerm",
|
||||
"role": "Manufacturing User",
|
||||
"permlevel": 1
|
||||
}
|
||||
]
|
||||
48
manufacturing/doctype/bom/bom_list.js
Normal file
48
manufacturing/doctype/bom/bom_list.js
Normal file
@@ -0,0 +1,48 @@
|
||||
// render
|
||||
wn.doclistviews['BOM'] = wn.views.ListView.extend({
|
||||
init: function(d) {
|
||||
this._super(d);
|
||||
this.fields = this.fields.concat([
|
||||
'`tabBOM`.item',
|
||||
'`tabBOM`.uom',
|
||||
'IFNULL(`tabBOM`.quantity, 0) as quantity',
|
||||
'`tabBOM`.is_active',
|
||||
'`tabBOM`.costing_date',
|
||||
'`tabBOM`.total_cost',
|
||||
'`tabBOM`.description',
|
||||
]);
|
||||
this.stats = this.stats.concat(['company']);
|
||||
},
|
||||
|
||||
prepare_data: function(data) {
|
||||
this._super(data);
|
||||
data.costing_date = wn.datetime.str_to_user(data.costing_date);
|
||||
data.description = (data.is_active === 'Yes' ? '' : '[Inactive] ') + data.description;
|
||||
},
|
||||
|
||||
columns: [
|
||||
{width: '3%', content: 'check'},
|
||||
{width: '3%', content: 'docstatus'},
|
||||
{width: '15%', content: 'name'},
|
||||
{width: '15%', content: 'item'},
|
||||
{width: '23%', content: 'description+tags'},
|
||||
{
|
||||
width: '12%',
|
||||
content: function(parent, data) {
|
||||
$(parent).html(data.quantity + ' ' + data.uom)
|
||||
},
|
||||
css: {'text-align':'right'},
|
||||
},
|
||||
{
|
||||
width: '20%',
|
||||
content: function(parent, data) {
|
||||
$(parent).html(sys_defaults.currency + " "
|
||||
+ fmt_money(data.total_cost));
|
||||
},
|
||||
css: {'text-align': 'right'},
|
||||
},
|
||||
{width: '12%', content:'costing_date', css: {
|
||||
'text-align': 'right', 'color':'#777'
|
||||
}},
|
||||
]
|
||||
});
|
||||
147
manufacturing/doctype/bom/test_bom.py
Normal file
147
manufacturing/doctype/bom/test_bom.py
Normal file
@@ -0,0 +1,147 @@
|
||||
# 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 unittest
|
||||
import webnotes
|
||||
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
manufacturing/doctype/bom_explosion_item/__init__.py
Normal file
1
manufacturing/doctype/bom_explosion_item/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from __future__ import unicode_literals
|
||||
@@ -0,0 +1,22 @@
|
||||
# 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
|
||||
|
||||
class DocType:
|
||||
def __init__(self, d, dl):
|
||||
self.doc, self.doclist = d, dl
|
||||
107
manufacturing/doctype/bom_explosion_item/bom_explosion_item.txt
Normal file
107
manufacturing/doctype/bom_explosion_item/bom_explosion_item.txt
Normal file
@@ -0,0 +1,107 @@
|
||||
[
|
||||
{
|
||||
"owner": "Administrator",
|
||||
"docstatus": 0,
|
||||
"creation": "2012-07-03 13:30:04",
|
||||
"modified_by": "Administrator",
|
||||
"modified": "2012-12-10 18:30:00"
|
||||
},
|
||||
{
|
||||
"read_only": 0,
|
||||
"istable": 1,
|
||||
"autoname": "FBD/.######",
|
||||
"name": "__common__",
|
||||
"default_print_format": "Standard",
|
||||
"doctype": "DocType",
|
||||
"module": "Manufacturing"
|
||||
},
|
||||
{
|
||||
"name": "__common__",
|
||||
"parent": "BOM Explosion Item",
|
||||
"doctype": "DocField",
|
||||
"parenttype": "DocType",
|
||||
"permlevel": 1,
|
||||
"parentfield": "fields"
|
||||
},
|
||||
{
|
||||
"name": "BOM Explosion Item",
|
||||
"doctype": "DocType"
|
||||
},
|
||||
{
|
||||
"oldfieldtype": "Link",
|
||||
"doctype": "DocField",
|
||||
"label": "Item Code",
|
||||
"oldfieldname": "item_code",
|
||||
"fieldname": "item_code",
|
||||
"fieldtype": "Link",
|
||||
"options": "Item"
|
||||
},
|
||||
{
|
||||
"oldfieldtype": "Text",
|
||||
"doctype": "DocField",
|
||||
"label": "Description",
|
||||
"oldfieldname": "description",
|
||||
"width": "300px",
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Text"
|
||||
},
|
||||
{
|
||||
"oldfieldtype": "Currency",
|
||||
"doctype": "DocField",
|
||||
"label": "Qty",
|
||||
"oldfieldname": "qty",
|
||||
"fieldname": "qty",
|
||||
"fieldtype": "Float"
|
||||
},
|
||||
{
|
||||
"oldfieldtype": "Currency",
|
||||
"doctype": "DocField",
|
||||
"label": "Rate",
|
||||
"oldfieldname": "standard_rate",
|
||||
"fieldname": "rate",
|
||||
"fieldtype": "Float"
|
||||
},
|
||||
{
|
||||
"oldfieldtype": "Currency",
|
||||
"doctype": "DocField",
|
||||
"label": "Amount",
|
||||
"oldfieldname": "amount_as_per_sr",
|
||||
"fieldname": "amount",
|
||||
"fieldtype": "Float"
|
||||
},
|
||||
{
|
||||
"oldfieldtype": "Link",
|
||||
"doctype": "DocField",
|
||||
"label": "Stock UOM",
|
||||
"oldfieldname": "stock_uom",
|
||||
"fieldname": "stock_uom",
|
||||
"fieldtype": "Link",
|
||||
"options": "UOM"
|
||||
},
|
||||
{
|
||||
"oldfieldtype": "Link",
|
||||
"doctype": "DocField",
|
||||
"label": "Parent BOM",
|
||||
"oldfieldname": "parent_bom",
|
||||
"width": "250px",
|
||||
"fieldname": "parent_bom",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"options": "BOM"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"label": "Mat Detail No",
|
||||
"fieldname": "mat_detail_no",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1
|
||||
},
|
||||
{
|
||||
"no_copy": 0,
|
||||
"doctype": "DocField",
|
||||
"label": "Qty Consumed Per Unit",
|
||||
"fieldname": "qty_consumed_per_unit",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0
|
||||
}
|
||||
]
|
||||
1
manufacturing/doctype/bom_item/__init__.py
Normal file
1
manufacturing/doctype/bom_item/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from __future__ import unicode_literals
|
||||
22
manufacturing/doctype/bom_item/bom_item.py
Normal file
22
manufacturing/doctype/bom_item/bom_item.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# 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
|
||||
|
||||
class DocType:
|
||||
def __init__(self, d, dl):
|
||||
self.doc, self.doclist = d, dl
|
||||
194
manufacturing/doctype/bom_item/bom_item.txt
Normal file
194
manufacturing/doctype/bom_item/bom_item.txt
Normal file
@@ -0,0 +1,194 @@
|
||||
# DocType, BOM Item
|
||||
[
|
||||
|
||||
# These values are common in all dictionaries
|
||||
{
|
||||
'creation': '2012-03-27 14:36:02',
|
||||
'docstatus': 0,
|
||||
'modified': '2012-12-10 18:30:00',
|
||||
'modified_by': u'Administrator',
|
||||
'owner': u'Administrator'
|
||||
},
|
||||
|
||||
# These values are common for all DocType
|
||||
{
|
||||
'colour': u'White:FFF',
|
||||
'default_print_format': u'Standard',
|
||||
'doctype': 'DocType',
|
||||
'istable': 1,
|
||||
'module': u'Manufacturing',
|
||||
'name': '__common__',
|
||||
'section_style': u'Simple',
|
||||
'server_code_error': u' ',
|
||||
'show_in_menu': 0,
|
||||
'version': 27
|
||||
},
|
||||
|
||||
# These values are common for all DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'name': '__common__',
|
||||
'parent': u'BOM Item',
|
||||
'parentfield': u'fields',
|
||||
'parenttype': u'DocType'
|
||||
},
|
||||
|
||||
# These values are common for all DocPerm
|
||||
{
|
||||
'doctype': u'DocPerm',
|
||||
'name': '__common__',
|
||||
'parent': u'BOM Item',
|
||||
'parentfield': u'permissions',
|
||||
'parenttype': u'DocType',
|
||||
'read': 1,
|
||||
'role': u'System Manager'
|
||||
},
|
||||
|
||||
# DocType, BOM Item
|
||||
{
|
||||
'doctype': 'DocType',
|
||||
'name': u'BOM Item'
|
||||
},
|
||||
|
||||
# DocPerm
|
||||
{
|
||||
'doctype': u'DocPerm',
|
||||
'permlevel': 0,
|
||||
'write': 1
|
||||
},
|
||||
|
||||
# DocPerm
|
||||
{
|
||||
'doctype': u'DocPerm',
|
||||
'permlevel': 1
|
||||
},
|
||||
|
||||
# DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'fieldname': u'operation_no',
|
||||
'fieldtype': u'Data',
|
||||
'label': u'Operation No',
|
||||
'oldfieldname': u'operation_no',
|
||||
'oldfieldtype': u'Data',
|
||||
'permlevel': 0,
|
||||
'reqd': 1
|
||||
},
|
||||
|
||||
# DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'fieldname': u'item_code',
|
||||
'fieldtype': u'Link',
|
||||
'in_filter': 1,
|
||||
'label': u'Item Code',
|
||||
'oldfieldname': u'item_code',
|
||||
'oldfieldtype': u'Link',
|
||||
'options': u'Item',
|
||||
'permlevel': 0,
|
||||
'reqd': 1,
|
||||
'search_index': 1,
|
||||
'trigger': u'Client'
|
||||
},
|
||||
|
||||
# DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'fieldname': u'bom_no',
|
||||
'fieldtype': u'Link',
|
||||
'in_filter': 1,
|
||||
'label': u'BOM No',
|
||||
'oldfieldname': u'bom_no',
|
||||
'oldfieldtype': u'Link',
|
||||
'options': u'BOM',
|
||||
'permlevel': 0,
|
||||
'reqd': 0,
|
||||
'search_index': 1,
|
||||
'trigger': u'Client',
|
||||
'width': u'150px'
|
||||
},
|
||||
|
||||
# DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'fieldname': u'qty',
|
||||
'fieldtype': u'Float',
|
||||
'label': u'Qty',
|
||||
'oldfieldname': u'qty',
|
||||
'oldfieldtype': u'Currency',
|
||||
'permlevel': 0,
|
||||
'reqd': 1,
|
||||
'trigger': u'Client'
|
||||
},
|
||||
|
||||
# DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'fieldname': u'stock_uom',
|
||||
'fieldtype': u'Data',
|
||||
'label': u'Stock UOM',
|
||||
'oldfieldname': u'stock_uom',
|
||||
'oldfieldtype': u'Data',
|
||||
'permlevel': 1,
|
||||
'reqd': 1
|
||||
},
|
||||
|
||||
# DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'fieldname': u'rate',
|
||||
'fieldtype': u'Float',
|
||||
'label': u'Rate',
|
||||
'permlevel': 0
|
||||
},
|
||||
|
||||
# DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'fieldname': u'amount',
|
||||
'fieldtype': u'Float',
|
||||
'label': u'Amount',
|
||||
'oldfieldname': u'amount_as_per_mar',
|
||||
'oldfieldtype': u'Currency',
|
||||
'permlevel': 1,
|
||||
'width': u'150px'
|
||||
},
|
||||
|
||||
# DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'fieldname': u'scrap',
|
||||
'fieldtype': u'Currency',
|
||||
'label': u'Scrap %',
|
||||
'oldfieldname': u'scrap',
|
||||
'oldfieldtype': u'Currency',
|
||||
'permlevel': 0,
|
||||
'print_hide': 1
|
||||
},
|
||||
|
||||
# DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'fieldname': u'description',
|
||||
'fieldtype': u'Text',
|
||||
'label': u'Item Description',
|
||||
'oldfieldname': u'description',
|
||||
'oldfieldtype': u'Text',
|
||||
'permlevel': 0,
|
||||
'reqd': 0,
|
||||
'width': u'250px'
|
||||
},
|
||||
|
||||
# DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'fieldname': u'qty_consumed_per_unit',
|
||||
'fieldtype': u'Float',
|
||||
'hidden': 1,
|
||||
'label': u'Qty Consumed Per Unit',
|
||||
'oldfieldname': u'qty_consumed_per_unit',
|
||||
'oldfieldtype': u'Float',
|
||||
'permlevel': 1,
|
||||
'print_hide': 1
|
||||
}
|
||||
]
|
||||
1
manufacturing/doctype/bom_operation/__init__.py
Normal file
1
manufacturing/doctype/bom_operation/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from __future__ import unicode_literals
|
||||
22
manufacturing/doctype/bom_operation/bom_operation.py
Normal file
22
manufacturing/doctype/bom_operation/bom_operation.py
Normal file
@@ -0,0 +1,22 @@
|
||||
# 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
|
||||
|
||||
class DocType:
|
||||
def __init__(self, d, dl):
|
||||
self.doc, self.doclist = d, dl
|
||||
135
manufacturing/doctype/bom_operation/bom_operation.txt
Normal file
135
manufacturing/doctype/bom_operation/bom_operation.txt
Normal file
@@ -0,0 +1,135 @@
|
||||
# DocType, BOM Operation
|
||||
[
|
||||
|
||||
# These values are common in all dictionaries
|
||||
{
|
||||
'creation': '2012-03-27 14:36:02',
|
||||
'docstatus': 0,
|
||||
'modified': '2012-12-10 18:30:00',
|
||||
'modified_by': u'Administrator',
|
||||
'owner': u'Administrator'
|
||||
},
|
||||
|
||||
# These values are common for all DocType
|
||||
{
|
||||
'colour': u'White:FFF',
|
||||
'default_print_format': u'Standard',
|
||||
'doctype': 'DocType',
|
||||
'istable': 1,
|
||||
'module': u'Manufacturing',
|
||||
'name': '__common__',
|
||||
'section_style': u'Simple',
|
||||
'server_code_error': u' ',
|
||||
'show_in_menu': 0,
|
||||
'version': 8
|
||||
},
|
||||
|
||||
# These values are common for all DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'name': '__common__',
|
||||
'parent': u'BOM Operation',
|
||||
'parentfield': u'fields',
|
||||
'parenttype': u'DocType',
|
||||
'permlevel': 0
|
||||
},
|
||||
|
||||
# These values are common for all DocPerm
|
||||
{
|
||||
'doctype': u'DocPerm',
|
||||
'name': '__common__',
|
||||
'parent': u'BOM Operation',
|
||||
'parentfield': u'permissions',
|
||||
'parenttype': u'DocType',
|
||||
'read': 1,
|
||||
'role': u'System Manager'
|
||||
},
|
||||
|
||||
# DocType, BOM Operation
|
||||
{
|
||||
'doctype': 'DocType',
|
||||
'name': u'BOM Operation'
|
||||
},
|
||||
|
||||
# DocPerm
|
||||
{
|
||||
'doctype': u'DocPerm',
|
||||
'permlevel': 0,
|
||||
'write': 1
|
||||
},
|
||||
|
||||
# DocPerm
|
||||
{
|
||||
'doctype': u'DocPerm',
|
||||
'permlevel': 1
|
||||
},
|
||||
|
||||
# DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'fieldname': u'operation_no',
|
||||
'fieldtype': u'Data',
|
||||
'label': u'Operation No',
|
||||
'oldfieldname': u'operation_no',
|
||||
'oldfieldtype': u'Data',
|
||||
'reqd': 1
|
||||
},
|
||||
|
||||
# DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'fieldname': u'opn_description',
|
||||
'fieldtype': u'Text',
|
||||
'label': u'Opn Description',
|
||||
'oldfieldname': u'opn_description',
|
||||
'oldfieldtype': u'Text',
|
||||
'reqd': 1
|
||||
},
|
||||
|
||||
# DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'fieldname': u'workstation',
|
||||
'fieldtype': u'Link',
|
||||
'label': u'Workstation',
|
||||
'oldfieldname': u'workstation',
|
||||
'oldfieldtype': u'Link',
|
||||
'options': u'Workstation',
|
||||
'reqd': 0,
|
||||
'trigger': u'Client'
|
||||
},
|
||||
|
||||
# DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'fieldname': u'hour_rate',
|
||||
'fieldtype': u'Currency',
|
||||
'label': u'Hour Rate',
|
||||
'oldfieldname': u'hour_rate',
|
||||
'oldfieldtype': u'Currency',
|
||||
'reqd': 0
|
||||
},
|
||||
|
||||
# DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'fieldname': u'time_in_mins',
|
||||
'fieldtype': u'Currency',
|
||||
'label': u'Operation Time (mins)',
|
||||
'oldfieldname': u'time_in_mins',
|
||||
'oldfieldtype': u'Currency',
|
||||
'reqd': 0
|
||||
},
|
||||
|
||||
# DocField
|
||||
{
|
||||
'allow_on_submit': 0,
|
||||
'doctype': u'DocField',
|
||||
'fieldname': u'operating_cost',
|
||||
'fieldtype': u'Currency',
|
||||
'label': u'Operating Cost',
|
||||
'oldfieldname': u'operating_cost',
|
||||
'oldfieldtype': u'Currency',
|
||||
'reqd': 0
|
||||
}
|
||||
]
|
||||
0
manufacturing/doctype/bom_replace_tool/__init__.py
Normal file
0
manufacturing/doctype/bom_replace_tool/__init__.py
Normal file
25
manufacturing/doctype/bom_replace_tool/bom_replace_tool.js
Normal file
25
manufacturing/doctype/bom_replace_tool/bom_replace_tool.js
Normal file
@@ -0,0 +1,25 @@
|
||||
// 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/>.
|
||||
|
||||
|
||||
cur_frm.set_query("current_bom", function(doc) {
|
||||
return erpnext.queries.bom({name: "!" + doc.new_bom});
|
||||
});
|
||||
|
||||
|
||||
cur_frm.set_query("new_bom", function(doc) {
|
||||
return erpnext.queries.bom({name: "!" + doc.current_bom});
|
||||
});
|
||||
56
manufacturing/doctype/bom_replace_tool/bom_replace_tool.py
Normal file
56
manufacturing/doctype/bom_replace_tool/bom_replace_tool.py
Normal file
@@ -0,0 +1,56 @@
|
||||
# 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
|
||||
from webnotes.utils import cstr, flt
|
||||
from webnotes.model.code import get_obj
|
||||
from webnotes import msgprint
|
||||
|
||||
class DocType:
|
||||
def __init__( self, doc, doclist=[]):
|
||||
self.doc = doc
|
||||
self.doclist = doclist
|
||||
|
||||
def replace_bom(self):
|
||||
self.validate_bom()
|
||||
self.update_new_bom()
|
||||
bom_list = self.get_parent_boms()
|
||||
for bom in bom_list:
|
||||
bom_obj = get_obj("BOM", bom, with_children=1)
|
||||
bom_obj.update_cost_by_traversing()
|
||||
bom_obj.update_flat_bom_by_traversing()
|
||||
|
||||
def validate_bom(self):
|
||||
if cstr(self.doc.current_bom) == cstr(self.doc.new_bom):
|
||||
msgprint("Current BOM and New BOM can not be same", raise_exception=1)
|
||||
|
||||
def update_new_bom(self):
|
||||
current_bom_unitcost = webnotes.conn.sql("""select total_cost/quantity
|
||||
from `tabBOM` where name = %s""", self.doc.current_bom)
|
||||
current_bom_unitcost = current_bom_unitcost and flt(current_bom_unitcost[0][0]) or 0
|
||||
webnotes.conn.sql("""update `tabBOM Item` set bom_no=%s,
|
||||
rate=%s, amount=qty*%s where bom_no = %s and docstatus < 2""",
|
||||
(self.doc.new_bom, current_bom_unitcost, current_bom_unitcost, self.doc.current_bom))
|
||||
|
||||
def get_parent_boms(bom_no):
|
||||
return [d[0] for d in webnotes.conn.sql("""select distinct parent from
|
||||
`tabBOM Item` where ifnull(bom_no, '')=%s and docstatus < 2""", bom_no)]
|
||||
|
||||
def get_parent_boms(self):
|
||||
return [d[0] for d in webnotes.conn.sql("""select distinct parent
|
||||
from `tabBOM Item` where ifnull(bom_no, '') = %s and docstatus < 2""",
|
||||
self.doc.new_bom)]
|
||||
78
manufacturing/doctype/bom_replace_tool/bom_replace_tool.txt
Normal file
78
manufacturing/doctype/bom_replace_tool/bom_replace_tool.txt
Normal file
@@ -0,0 +1,78 @@
|
||||
[
|
||||
{
|
||||
"owner": "Administrator",
|
||||
"docstatus": 0,
|
||||
"creation": "2012-12-06 12:10:10",
|
||||
"modified_by": "Administrator",
|
||||
"modified": "2012-12-06 12:32:22"
|
||||
},
|
||||
{
|
||||
"in_create": 1,
|
||||
"allow_print": 1,
|
||||
"module": "Production",
|
||||
"document_type": "Other",
|
||||
"description": "Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate \"BOM Explosion Item\" table as per new BOM",
|
||||
"read_only": 1,
|
||||
"allow_email": 1,
|
||||
"hide_heading": 1,
|
||||
"issingle": 1,
|
||||
"name": "__common__",
|
||||
"doctype": "DocType",
|
||||
"hide_toolbar": 1,
|
||||
"allow_copy": 1
|
||||
},
|
||||
{
|
||||
"name": "__common__",
|
||||
"parent": "BOM Replace Tool",
|
||||
"doctype": "DocField",
|
||||
"parenttype": "DocType",
|
||||
"permlevel": 0,
|
||||
"parentfield": "fields"
|
||||
},
|
||||
{
|
||||
"parent": "BOM Replace Tool",
|
||||
"read": 1,
|
||||
"name": "__common__",
|
||||
"create": 1,
|
||||
"doctype": "DocPerm",
|
||||
"write": 1,
|
||||
"parenttype": "DocType",
|
||||
"role": "Administrator",
|
||||
"permlevel": 0,
|
||||
"parentfield": "permissions"
|
||||
},
|
||||
{
|
||||
"name": "BOM Replace Tool",
|
||||
"doctype": "DocType"
|
||||
},
|
||||
{
|
||||
"description": "The BOM which will be replaced",
|
||||
"colour": "White:FFF",
|
||||
"doctype": "DocField",
|
||||
"label": "Current BOM",
|
||||
"fieldname": "current_bom",
|
||||
"fieldtype": "Link",
|
||||
"reqd": 1,
|
||||
"options": "BOM"
|
||||
},
|
||||
{
|
||||
"description": "The new BOM after replacement",
|
||||
"colour": "White:FFF",
|
||||
"doctype": "DocField",
|
||||
"label": "New BOM",
|
||||
"fieldname": "new_bom",
|
||||
"fieldtype": "Link",
|
||||
"reqd": 1,
|
||||
"options": "BOM"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"label": "Replace",
|
||||
"fieldname": "replace",
|
||||
"fieldtype": "Button",
|
||||
"options": "replace_bom"
|
||||
},
|
||||
{
|
||||
"doctype": "DocPerm"
|
||||
}
|
||||
]
|
||||
1
manufacturing/doctype/production_order/__init__.py
Normal file
1
manufacturing/doctype/production_order/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from __future__ import unicode_literals
|
||||
100
manufacturing/doctype/production_order/production_order.js
Normal file
100
manufacturing/doctype/production_order/production_order.js
Normal file
@@ -0,0 +1,100 @@
|
||||
// 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/>.
|
||||
|
||||
|
||||
cur_frm.cscript.onload = function(doc, dt, dn) {
|
||||
if (!doc.posting_date) doc.transaction_date = dateutil.obj_to_str(new Date());
|
||||
if (!doc.status) doc.status = 'Draft';
|
||||
cfn_set_fields(doc, dt, dn);
|
||||
}
|
||||
|
||||
// ================================== Refresh ==========================================
|
||||
cur_frm.cscript.refresh = function(doc, dt, dn) {
|
||||
cfn_set_fields(doc, dt, dn);
|
||||
}
|
||||
|
||||
var cfn_set_fields = function(doc, dt, dn) {
|
||||
if (doc.docstatus == 1) {
|
||||
if (doc.status != 'Stopped' && doc.status != 'Completed')
|
||||
cur_frm.add_custom_button('Stop!', cur_frm.cscript['Stop Production Order']);
|
||||
else if (doc.status == 'Stopped')
|
||||
cur_frm.add_custom_button('Unstop', cur_frm.cscript['Unstop Production Order']);
|
||||
|
||||
if (doc.status == 'Submitted' || doc.status == 'Material Transferred' || doc.status == 'In Process'){
|
||||
cur_frm.add_custom_button('Transfer Material', cur_frm.cscript['Transfer Material']);
|
||||
cur_frm.add_custom_button('Backflush', cur_frm.cscript['Backflush']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cur_frm.cscript.production_item = function(doc, dt, dn) {
|
||||
get_server_fields('get_item_detail',doc.production_item,'',doc,dt,dn,1);
|
||||
}
|
||||
|
||||
cur_frm.cscript['Stop Production Order'] = function() {
|
||||
var doc = cur_frm.doc;
|
||||
var check = confirm("Do you really want to stop production order: " + doc.name);
|
||||
if (check) {
|
||||
$c_obj(make_doclist(doc.doctype, doc.name), 'stop_unstop', 'Stopped', function(r, rt) {cur_frm.refresh();});
|
||||
}
|
||||
}
|
||||
|
||||
cur_frm.cscript['Unstop Production Order'] = function() {
|
||||
var doc = cur_frm.doc;
|
||||
var check = confirm("Do really want to unstop production order: " + doc.name);
|
||||
if (check)
|
||||
$c_obj(make_doclist(doc.doctype, doc.name), 'stop_unstop', 'Unstopped', function(r, rt) {cur_frm.refresh();});
|
||||
}
|
||||
|
||||
cur_frm.cscript['Transfer Material'] = function() {
|
||||
var doc = cur_frm.doc;
|
||||
cur_frm.cscript.make_se(doc, process = 'Material Transfer');
|
||||
}
|
||||
|
||||
cur_frm.cscript['Backflush'] = function() {
|
||||
var doc = cur_frm.doc;
|
||||
cur_frm.cscript.make_se(doc, process = 'Backflush');
|
||||
}
|
||||
|
||||
cur_frm.cscript.make_se = function(doc, process) {
|
||||
var se = LocalDB.create('Stock Entry');
|
||||
se = locals['Stock Entry'][se];
|
||||
se.purpose = 'Production Order';
|
||||
se.process = process;
|
||||
se.posting_date = doc.posting_date;
|
||||
se.production_order = doc.name;
|
||||
se.fiscal_year = doc.fiscal_year;
|
||||
se.company = doc.company;
|
||||
|
||||
loaddoc('Stock Entry', se.name);
|
||||
}
|
||||
|
||||
cur_frm.fields_dict['production_item'].get_query = function(doc) {
|
||||
return 'SELECT DISTINCT `tabItem`.`name`, `tabItem`.`description` FROM `tabItem` WHERE (IFNULL(`tabItem`.`end_of_life`,"") = "" OR `tabItem`.`end_of_life` = "0000-00-00" OR `tabItem`.`end_of_life` > NOW()) AND `tabItem`.docstatus != 2 AND `tabItem`.is_pro_applicable = "Yes" AND `tabItem`.%(key)s LIKE "%s" ORDER BY `tabItem`.`name` LIMIT 50';
|
||||
}
|
||||
|
||||
cur_frm.fields_dict['project_name'].get_query = function(doc, dt, dn) {
|
||||
return 'SELECT `tabProject`.name FROM `tabProject` \
|
||||
WHERE `tabProject`.status not in ("Completed", "Cancelled") \
|
||||
AND `tabProject`.name LIKE "%s" ORDER BY `tabProject`.name ASC LIMIT 50';
|
||||
}
|
||||
|
||||
|
||||
cur_frm.set_query("bom_no", function(doc) {
|
||||
if (doc.production_item) {
|
||||
return erpnext.queries.bom({item: cstr(doc.production_item)});
|
||||
} else msgprint(" Please enter Production Item first");
|
||||
});
|
||||
147
manufacturing/doctype/production_order/production_order.py
Normal file
147
manufacturing/doctype/production_order/production_order.py
Normal file
@@ -0,0 +1,147 @@
|
||||
# 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
|
||||
|
||||
from webnotes.utils import cstr, flt, now, nowdate
|
||||
from webnotes.model import db_exists
|
||||
from webnotes.model.doc import make_autoname
|
||||
from webnotes.model.wrapper import copy_doclist
|
||||
from webnotes.model.code import get_obj
|
||||
from webnotes import msgprint
|
||||
|
||||
sql = webnotes.conn.sql
|
||||
|
||||
|
||||
|
||||
class DocType:
|
||||
def __init__(self, doc, doclist=[]):
|
||||
self.doc = doc
|
||||
self.doclist = doclist
|
||||
|
||||
|
||||
def autoname(self):
|
||||
p = self.doc.fiscal_year
|
||||
self.doc.name = make_autoname('PRO/' + self.doc.fiscal_year[2:5]+self.doc.fiscal_year[7:9] + '/.######')
|
||||
|
||||
|
||||
def get_item_detail(self, prod_item):
|
||||
item = sql("""select description, stock_uom, default_bom from `tabItem`
|
||||
where (ifnull(end_of_life,'')='' or end_of_life = '0000-00-00' or end_of_life > now()) and name = %s""", prod_item, as_dict = 1 )
|
||||
ret = {
|
||||
'description' : item and item[0]['description'] or '',
|
||||
'stock_uom' : item and item[0]['stock_uom'] or '',
|
||||
'default_bom' : item and item[0]['default_bom'] or ''
|
||||
}
|
||||
return ret
|
||||
|
||||
|
||||
def validate(self):
|
||||
if self.doc.production_item :
|
||||
item_detail = sql("select name from `tabItem` where name = '%s' and docstatus != 2"
|
||||
% self.doc.production_item, as_dict = 1)
|
||||
if not item_detail:
|
||||
msgprint("Item '%s' does not exist or cancelled in the system."
|
||||
% cstr(self.doc.production_item), raise_exception=1)
|
||||
|
||||
if self.doc.bom_no:
|
||||
bom = sql("""select name from `tabBOM` where name = %s and docstatus = 1
|
||||
and is_active = 'Yes' and item = %s"""
|
||||
, (self.doc.bom_no, self.doc.production_item), as_dict =1)
|
||||
if not bom:
|
||||
msgprint("""Incorrect BOM: %s entered.
|
||||
May be BOM not exists or inactive or not submitted
|
||||
or for some other item.""" % cstr(self.doc.bom_no), raise_exception=1)
|
||||
|
||||
if self.doc.sales_order:
|
||||
if not webnotes.conn.sql("""select name from `tabSales Order`
|
||||
where name=%s and docstatus = 1""", self.doc.sales_order):
|
||||
msgprint("Sales Order: %s is not valid" % self.doc.sales_order, raise_exception=1)
|
||||
|
||||
self.validate_production_order_against_so()
|
||||
|
||||
|
||||
def validate_production_order_against_so(self):
|
||||
# already ordered qty
|
||||
ordered_qty_against_so = webnotes.conn.sql("""select sum(qty) from `tabProduction Order`
|
||||
where production_item = %s and sales_order = %s and docstatus < 2""",
|
||||
(self.doc.production_item, self.doc.sales_order))[0][0]
|
||||
|
||||
|
||||
# get qty from Sales Order Item table
|
||||
so_item_qty = webnotes.conn.sql("""select sum(qty) from `tabSales Order Item`
|
||||
where parent = %s and item_code = %s""",
|
||||
(self.doc.sales_order, self.doc.production_item))[0][0]
|
||||
# get qty from Packing Item table
|
||||
dnpi_qty = webnotes.conn.sql("""select sum(qty) from `tabDelivery Note Packing Item`
|
||||
where parent = %s and parenttype = 'Sales Order' and item_code = %s""",
|
||||
(self.doc.sales_order, self.doc.production_item))[0][0]
|
||||
# total qty in SO
|
||||
so_qty = flt(so_item_qty) + flt(dnpi_qty)
|
||||
|
||||
if ordered_qty_against_so > so_qty:
|
||||
msgprint("""Total production order qty for item: %s against sales order: %s \
|
||||
will be %s, which is greater than sales order qty (%s).
|
||||
Please reduce qty or remove the item.""" %
|
||||
(self.doc.production_item, self.doc.sales_order,
|
||||
ordered_qty_against_so, so_qty), raise_exception=1)
|
||||
|
||||
|
||||
def stop_unstop(self, status):
|
||||
""" Called from client side on Stop/Unstop event"""
|
||||
self.update_status(status)
|
||||
qty = (flt(self.doc.qty)-flt(self.doc.produced_qty)) * ((status == 'Stopped') and -1 or 1)
|
||||
self.update_planned_qty(qty)
|
||||
msgprint("Production Order has been %s" % status)
|
||||
|
||||
|
||||
def update_status(self, status):
|
||||
if status == 'Stopped':
|
||||
webnotes.conn.set(self.doc, 'status', cstr(status))
|
||||
else:
|
||||
if flt(self.doc.qty) == flt(self.doc.produced_qty):
|
||||
webnotes.conn.set(self.doc, 'status', 'Completed')
|
||||
if flt(self.doc.qty) > flt(self.doc.produced_qty):
|
||||
webnotes.conn.set(self.doc, 'status', 'In Process')
|
||||
if flt(self.doc.produced_qty) == 0:
|
||||
webnotes.conn.set(self.doc, 'status', 'Submitted')
|
||||
|
||||
|
||||
def on_submit(self):
|
||||
webnotes.conn.set(self.doc,'status', 'Submitted')
|
||||
self.update_planned_qty(self.doc.qty)
|
||||
|
||||
|
||||
def on_cancel(self):
|
||||
# Check whether any stock entry exists against this Production Order
|
||||
stock_entry = sql("""select name from `tabStock Entry`
|
||||
where production_order = %s and docstatus = 1""", self.doc.name)
|
||||
if stock_entry:
|
||||
msgprint("""Submitted Stock Entry %s exists against this production order.
|
||||
Hence can not be cancelled.""" % stock_entry[0][0], raise_exception=1)
|
||||
|
||||
webnotes.conn.set(self.doc,'status', 'Cancelled')
|
||||
self.update_planned_qty(-self.doc.qty)
|
||||
|
||||
def update_planned_qty(self, qty):
|
||||
"""update planned qty in bin"""
|
||||
args = {
|
||||
"item_code": self.doc.production_item,
|
||||
"posting_date": nowdate(),
|
||||
"planned_qty": flt(qty)
|
||||
}
|
||||
get_obj('Warehouse', self.doc.fg_warehouse).update_bin(args)
|
||||
324
manufacturing/doctype/production_order/production_order.txt
Normal file
324
manufacturing/doctype/production_order/production_order.txt
Normal file
@@ -0,0 +1,324 @@
|
||||
[
|
||||
{
|
||||
"owner": "Administrator",
|
||||
"docstatus": 0,
|
||||
"creation": "2012-07-03 13:30:03",
|
||||
"modified_by": "Administrator",
|
||||
"modified": "2012-12-10 18:30:00"
|
||||
},
|
||||
{
|
||||
"is_submittable": 1,
|
||||
"in_create": 0,
|
||||
"default_print_format": "Standard",
|
||||
"doctype": "DocType",
|
||||
"module": "Manufacturing",
|
||||
"name": "__common__"
|
||||
},
|
||||
{
|
||||
"name": "__common__",
|
||||
"parent": "Production Order",
|
||||
"doctype": "DocField",
|
||||
"parenttype": "DocType",
|
||||
"parentfield": "fields"
|
||||
},
|
||||
{
|
||||
"name": "__common__",
|
||||
"parent": "Production Order",
|
||||
"read": 1,
|
||||
"doctype": "DocPerm",
|
||||
"parenttype": "DocType",
|
||||
"parentfield": "permissions"
|
||||
},
|
||||
{
|
||||
"name": "Production Order",
|
||||
"doctype": "DocType"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"width": "50%",
|
||||
"fieldname": "column_break0",
|
||||
"fieldtype": "Column Break",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"description": "Item for which this Production Order is raised.",
|
||||
"oldfieldtype": "Link",
|
||||
"colour": "White:FFF",
|
||||
"doctype": "DocField",
|
||||
"label": "Production Item",
|
||||
"oldfieldname": "production_item",
|
||||
"permlevel": 0,
|
||||
"trigger": "Client",
|
||||
"fieldname": "production_item",
|
||||
"fieldtype": "Link",
|
||||
"reqd": 1,
|
||||
"in_filter": 1,
|
||||
"options": "Item"
|
||||
},
|
||||
{
|
||||
"description": "Bill of Material which was considered for manufacturing the production item.",
|
||||
"oldfieldtype": "Link",
|
||||
"colour": "White:FFF",
|
||||
"doctype": "DocField",
|
||||
"label": "BOM No",
|
||||
"oldfieldname": "bom_no",
|
||||
"permlevel": 0,
|
||||
"trigger": "Client",
|
||||
"fieldname": "bom_no",
|
||||
"fieldtype": "Link",
|
||||
"reqd": 1,
|
||||
"options": "BOM"
|
||||
},
|
||||
{
|
||||
"description": "Quantity of item for which Production Order is raised.",
|
||||
"oldfieldtype": "Currency",
|
||||
"colour": "White:FFF",
|
||||
"doctype": "DocField",
|
||||
"label": "Qty",
|
||||
"oldfieldname": "qty",
|
||||
"fieldname": "qty",
|
||||
"fieldtype": "Currency",
|
||||
"reqd": 1,
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"description": "The date on which current entry will get or has actually executed.",
|
||||
"oldfieldtype": "Date",
|
||||
"colour": "White:FFF",
|
||||
"doctype": "DocField",
|
||||
"label": "Posting Date",
|
||||
"oldfieldname": "posting_date",
|
||||
"fieldname": "posting_date",
|
||||
"fieldtype": "Date",
|
||||
"reqd": 1,
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"oldfieldtype": "Column Break",
|
||||
"doctype": "DocField",
|
||||
"width": "50%",
|
||||
"fieldname": "column_break1",
|
||||
"fieldtype": "Column Break",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"description": "The warehouse for finished goods where stock of produced items will be updated.",
|
||||
"oldfieldtype": "Link",
|
||||
"colour": "White:FFF",
|
||||
"doctype": "DocField",
|
||||
"label": "FG Warehouse",
|
||||
"oldfieldname": "fg_warehouse",
|
||||
"permlevel": 0,
|
||||
"fieldname": "fg_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"reqd": 1,
|
||||
"in_filter": 1,
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
"description": "The work in progress warehouse where raw materials will be operated upon to create finished goods.",
|
||||
"oldfieldtype": "Link",
|
||||
"colour": "White:FFF",
|
||||
"doctype": "DocField",
|
||||
"label": "WIP Warehouse",
|
||||
"oldfieldname": "wip_warehouse",
|
||||
"permlevel": 0,
|
||||
"fieldname": "wip_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"reqd": 1,
|
||||
"in_filter": 1,
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
"description": "If checked, BOM for sub-assembly items will be considered for getting raw materials. Otherwise, all sub-assembly items will be treated as a raw material.",
|
||||
"default": "1",
|
||||
"oldfieldtype": "Select",
|
||||
"colour": "White:FFF",
|
||||
"doctype": "DocField",
|
||||
"label": "Use Multi-Level BOM",
|
||||
"oldfieldname": "consider_sa_items",
|
||||
"fieldname": "use_multi_level_bom",
|
||||
"fieldtype": "Check",
|
||||
"reqd": 0,
|
||||
"in_filter": 1,
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"description": "Updated after finished goods are transferred to FG Warehouse through Stock Entry",
|
||||
"no_copy": 1,
|
||||
"oldfieldtype": "Currency",
|
||||
"colour": "White:FFF",
|
||||
"doctype": "DocField",
|
||||
"label": "Produced Qty",
|
||||
"oldfieldname": "produced_qty",
|
||||
"fieldname": "produced_qty",
|
||||
"fieldtype": "Currency",
|
||||
"permlevel": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"label": "More Info",
|
||||
"fieldname": "more_info",
|
||||
"fieldtype": "Section Break",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"width": "50%",
|
||||
"fieldname": "column_break2",
|
||||
"fieldtype": "Column Break",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"no_copy": 1,
|
||||
"oldfieldtype": "Select",
|
||||
"doctype": "DocField",
|
||||
"label": "Origin",
|
||||
"oldfieldname": "origin",
|
||||
"options": "Manual\nMRP",
|
||||
"fieldname": "origin",
|
||||
"fieldtype": "Select",
|
||||
"reqd": 1,
|
||||
"permlevel": 0,
|
||||
"in_filter": 1
|
||||
},
|
||||
{
|
||||
"no_copy": 1,
|
||||
"oldfieldtype": "Select",
|
||||
"doctype": "DocField",
|
||||
"label": "Status",
|
||||
"oldfieldname": "status",
|
||||
"permlevel": 1,
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"search_index": 1,
|
||||
"reqd": 1,
|
||||
"options": "\nDraft\nSubmitted\nStopped\nIn Process\nCompleted\nCancelled",
|
||||
"in_filter": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"label": "Sales Order",
|
||||
"options": "Sales Order",
|
||||
"fieldname": "sales_order",
|
||||
"fieldtype": "Link",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"description": "Select name of the project if Production Order need to be created against any project",
|
||||
"oldfieldtype": "Link",
|
||||
"label": "Project Name",
|
||||
"oldfieldname": "project_name",
|
||||
"trigger": "Client",
|
||||
"fieldname": "project_name",
|
||||
"fieldtype": "Link",
|
||||
"doctype": "DocField",
|
||||
"options": "Project",
|
||||
"permlevel": 0,
|
||||
"in_filter": 1
|
||||
},
|
||||
{
|
||||
"oldfieldtype": "Link",
|
||||
"doctype": "DocField",
|
||||
"label": "Company",
|
||||
"oldfieldname": "company",
|
||||
"options": "Company",
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"reqd": 1,
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"oldfieldtype": "Select",
|
||||
"doctype": "DocField",
|
||||
"label": "Fiscal Year",
|
||||
"oldfieldname": "fiscal_year",
|
||||
"options": "link:Fiscal Year",
|
||||
"fieldname": "fiscal_year",
|
||||
"fieldtype": "Select",
|
||||
"reqd": 1,
|
||||
"permlevel": 0,
|
||||
"in_filter": 1
|
||||
},
|
||||
{
|
||||
"no_copy": 1,
|
||||
"oldfieldtype": "Data",
|
||||
"doctype": "DocField",
|
||||
"label": "Amended From",
|
||||
"oldfieldname": "amended_from",
|
||||
"fieldname": "amended_from",
|
||||
"fieldtype": "Data",
|
||||
"permlevel": 1
|
||||
},
|
||||
{
|
||||
"no_copy": 1,
|
||||
"oldfieldtype": "Date",
|
||||
"doctype": "DocField",
|
||||
"label": "Amendment Date",
|
||||
"oldfieldname": "amendment_date",
|
||||
"fieldname": "amendment_date",
|
||||
"fieldtype": "Date",
|
||||
"permlevel": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"width": "50%",
|
||||
"fieldname": "column_break3",
|
||||
"fieldtype": "Column Break",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"oldfieldtype": "Data",
|
||||
"doctype": "DocField",
|
||||
"label": "Stock UOM",
|
||||
"oldfieldname": "stock_uom",
|
||||
"fieldname": "stock_uom",
|
||||
"fieldtype": "Data",
|
||||
"permlevel": 1
|
||||
},
|
||||
{
|
||||
"oldfieldtype": "Text",
|
||||
"doctype": "DocField",
|
||||
"label": "Production Item Description",
|
||||
"oldfieldname": "description",
|
||||
"width": "300px",
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Text",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"amend": 1,
|
||||
"create": 1,
|
||||
"doctype": "DocPerm",
|
||||
"submit": 1,
|
||||
"write": 1,
|
||||
"role": "System Manager",
|
||||
"cancel": 1,
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"doctype": "DocPerm",
|
||||
"role": "All",
|
||||
"permlevel": 1
|
||||
},
|
||||
{
|
||||
"amend": 1,
|
||||
"create": 1,
|
||||
"doctype": "DocPerm",
|
||||
"submit": 1,
|
||||
"write": 1,
|
||||
"role": "Manufacturing Manager",
|
||||
"cancel": 1,
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"amend": 1,
|
||||
"create": 1,
|
||||
"doctype": "DocPerm",
|
||||
"submit": 1,
|
||||
"write": 1,
|
||||
"role": "Manufacturing User",
|
||||
"cancel": 1,
|
||||
"permlevel": 0
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,40 @@
|
||||
// render
|
||||
wn.doclistviews['Production Order'] = wn.views.ListView.extend({
|
||||
init: function(d) {
|
||||
this._super(d);
|
||||
this.fields = this.fields.concat([
|
||||
'`tabProduction Order`.production_item',
|
||||
'`tabProduction Order`.fg_warehouse',
|
||||
'`tabProduction Order`.stock_uom',
|
||||
'IFNULL(`tabProduction Order`.qty, 0) as qty',
|
||||
'`tabProduction Order`.posting_date',
|
||||
'`tabProduction Order`.status',
|
||||
]);
|
||||
this.stats = this.stats.concat(['company']);
|
||||
},
|
||||
|
||||
prepare_data: function(data) {
|
||||
this._super(data);
|
||||
data.posting_date = wn.datetime.str_to_user(data.posting_date);
|
||||
},
|
||||
|
||||
columns: [
|
||||
{width: '3%', content: 'check'},
|
||||
{width: '5%', content: 'avatar'},
|
||||
{width: '3%', content: 'docstatus'},
|
||||
{width: '17%', content: 'name'},
|
||||
{width: '16%', content: 'production_item'},
|
||||
{width: '20%', content: 'fg_warehouse'},
|
||||
{width: '12%', content: 'status+tags'},
|
||||
{
|
||||
width: '12%',
|
||||
content: function(parent, data) {
|
||||
$(parent).html(data.qty + ' ' + data.stock_uom)
|
||||
},
|
||||
css: {'text-align':'right'}
|
||||
},
|
||||
{width: '12%', content:'posting_date', css: {
|
||||
'text-align': 'right', 'color':'#777'
|
||||
}},
|
||||
]
|
||||
});
|
||||
1
manufacturing/doctype/production_plan_item/__init__.py
Normal file
1
manufacturing/doctype/production_plan_item/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from __future__ import unicode_literals
|
||||
@@ -0,0 +1,22 @@
|
||||
# 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
|
||||
|
||||
class DocType:
|
||||
def __init__(self, d, dl):
|
||||
self.doc, self.doclist = d, dl
|
||||
@@ -0,0 +1,109 @@
|
||||
[
|
||||
{
|
||||
"owner": "Administrator",
|
||||
"docstatus": 0,
|
||||
"creation": "2012-07-03 13:30:04",
|
||||
"modified_by": "Administrator",
|
||||
"modified": "2012-12-10 18:30:00"
|
||||
},
|
||||
{
|
||||
"istable": 1,
|
||||
"autoname": "PPID/.#####",
|
||||
"name": "__common__",
|
||||
"default_print_format": "Standard",
|
||||
"doctype": "DocType",
|
||||
"module": "Manufacturing"
|
||||
},
|
||||
{
|
||||
"name": "__common__",
|
||||
"parent": "Production Plan Item",
|
||||
"doctype": "DocField",
|
||||
"parenttype": "DocType",
|
||||
"parentfield": "fields"
|
||||
},
|
||||
{
|
||||
"name": "Production Plan Item",
|
||||
"doctype": "DocType"
|
||||
},
|
||||
{
|
||||
"permlevel": 0,
|
||||
"oldfieldtype": "Link",
|
||||
"colour": "White:FFF",
|
||||
"doctype": "DocField",
|
||||
"label": "Item Code",
|
||||
"oldfieldname": "item_code",
|
||||
"width": "150px",
|
||||
"trigger": "Client",
|
||||
"fieldname": "item_code",
|
||||
"fieldtype": "Link",
|
||||
"reqd": 1,
|
||||
"options": "Item"
|
||||
},
|
||||
{
|
||||
"oldfieldtype": "Link",
|
||||
"doctype": "DocField",
|
||||
"label": "BOM No",
|
||||
"oldfieldname": "bom_no",
|
||||
"width": "100px",
|
||||
"options": "BOM",
|
||||
"fieldname": "bom_no",
|
||||
"fieldtype": "Link",
|
||||
"reqd": 1,
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"default": "0.00",
|
||||
"oldfieldtype": "Currency",
|
||||
"doctype": "DocField",
|
||||
"label": "Planned Qty",
|
||||
"oldfieldname": "planned_qty",
|
||||
"width": "100px",
|
||||
"fieldname": "planned_qty",
|
||||
"fieldtype": "Currency",
|
||||
"reqd": 1,
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"oldfieldtype": "Data",
|
||||
"doctype": "DocField",
|
||||
"label": "Sales Order",
|
||||
"oldfieldname": "source_docname",
|
||||
"options": "Sales Order",
|
||||
"fieldname": "sales_order",
|
||||
"fieldtype": "Link",
|
||||
"permlevel": 1
|
||||
},
|
||||
{
|
||||
"default": "0.00",
|
||||
"oldfieldtype": "Currency",
|
||||
"doctype": "DocField",
|
||||
"label": "SO Pending Qty",
|
||||
"oldfieldname": "prevdoc_reqd_qty",
|
||||
"width": "100px",
|
||||
"fieldname": "so_pending_qty",
|
||||
"fieldtype": "Currency",
|
||||
"reqd": 0,
|
||||
"permlevel": 1
|
||||
},
|
||||
{
|
||||
"oldfieldtype": "Data",
|
||||
"doctype": "DocField",
|
||||
"label": "UOM",
|
||||
"oldfieldname": "stock_uom",
|
||||
"width": "80px",
|
||||
"fieldname": "stock_uom",
|
||||
"fieldtype": "Data",
|
||||
"reqd": 1,
|
||||
"permlevel": 1
|
||||
},
|
||||
{
|
||||
"oldfieldtype": "Text",
|
||||
"doctype": "DocField",
|
||||
"label": "Description",
|
||||
"oldfieldname": "description",
|
||||
"width": "200px",
|
||||
"fieldname": "description",
|
||||
"fieldtype": "Text",
|
||||
"permlevel": 1
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
from __future__ import unicode_literals
|
||||
@@ -0,0 +1,22 @@
|
||||
# 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
|
||||
|
||||
class DocType:
|
||||
def __init__(self, d, dl):
|
||||
self.doc, self.doclist = d, dl
|
||||
@@ -0,0 +1,84 @@
|
||||
[
|
||||
{
|
||||
"owner": "Administrator",
|
||||
"docstatus": 0,
|
||||
"creation": "2012-07-03 13:30:04",
|
||||
"modified_by": "Administrator",
|
||||
"modified": "2012-12-10 18:30:00"
|
||||
},
|
||||
{
|
||||
"istable": 1,
|
||||
"autoname": "PP/.SO/.#####",
|
||||
"name": "__common__",
|
||||
"default_print_format": "Standard",
|
||||
"doctype": "DocType",
|
||||
"module": "Manufacturing"
|
||||
},
|
||||
{
|
||||
"name": "__common__",
|
||||
"parent": "Production Plan Sales Order",
|
||||
"doctype": "DocField",
|
||||
"parenttype": "DocType",
|
||||
"parentfield": "fields"
|
||||
},
|
||||
{
|
||||
"name": "__common__",
|
||||
"parent": "Production Plan Sales Order",
|
||||
"read": 1,
|
||||
"doctype": "DocPerm",
|
||||
"parenttype": "DocType",
|
||||
"role": "System Manager",
|
||||
"parentfield": "permissions"
|
||||
},
|
||||
{
|
||||
"name": "Production Plan Sales Order",
|
||||
"doctype": "DocType"
|
||||
},
|
||||
{
|
||||
"oldfieldtype": "Data",
|
||||
"doctype": "DocField",
|
||||
"label": "Sales Order",
|
||||
"oldfieldname": "prevdoc_docname",
|
||||
"width": "150px",
|
||||
"options": "Sales Order",
|
||||
"fieldname": "sales_order",
|
||||
"fieldtype": "Link",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"oldfieldtype": "Date",
|
||||
"doctype": "DocField",
|
||||
"label": "SO Date",
|
||||
"oldfieldname": "document_date",
|
||||
"width": "120px",
|
||||
"fieldname": "sales_order_date",
|
||||
"fieldtype": "Date",
|
||||
"permlevel": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"label": "Customer",
|
||||
"width": "150px",
|
||||
"options": "Customer",
|
||||
"fieldname": "customer",
|
||||
"fieldtype": "Link",
|
||||
"permlevel": 1
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"label": "Grand Total",
|
||||
"width": "120px",
|
||||
"fieldname": "grand_total",
|
||||
"fieldtype": "Currency",
|
||||
"permlevel": 1
|
||||
},
|
||||
{
|
||||
"write": 1,
|
||||
"doctype": "DocPerm",
|
||||
"permlevel": 0
|
||||
},
|
||||
{
|
||||
"doctype": "DocPerm",
|
||||
"permlevel": 1
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
from __future__ import unicode_literals
|
||||
@@ -0,0 +1,62 @@
|
||||
// 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/>.
|
||||
|
||||
cur_frm.cscript.onload = function(doc, cdt, cdn) {
|
||||
doc.company = sys_defaults.company;
|
||||
refresh_field("company");
|
||||
}
|
||||
|
||||
cur_frm.cscript.sales_order = function(doc,cdt,cdn) {
|
||||
var d = locals[cdt][cdn];
|
||||
if (d.sales_order) {
|
||||
get_server_fields('get_so_details', d.sales_order, 'pp_so_details', doc, cdt, cdn, 1);
|
||||
}
|
||||
}
|
||||
|
||||
cur_frm.cscript.item_code = function(doc,cdt,cdn) {
|
||||
var d = locals[cdt][cdn];
|
||||
if (d.item_code) {
|
||||
get_server_fields('get_item_details', d.item_code, 'pp_details', doc, cdt, cdn, 1);
|
||||
}
|
||||
}
|
||||
|
||||
cur_frm.cscript.download_materials_required = function(doc, cdt, cdn) {
|
||||
$c_obj(make_doclist(cdt, cdn), 'validate_data', '', function(r, rt) {
|
||||
if (!r['exc'])
|
||||
$c_obj_csv(make_doclist(cdt, cdn), 'download_raw_materials', '', '');
|
||||
});
|
||||
}
|
||||
|
||||
cur_frm.fields_dict['pp_details'].grid.get_field('item_code').get_query = function(doc) {
|
||||
return 'SELECT DISTINCT `tabItem`.`name`,`tabItem`.`item_name` \
|
||||
FROM `tabItem` WHERE `tabItem`.is_pro_applicable = "Yes" \
|
||||
AND (IFNULL(`tabItem`.`end_of_life`,"") = "" \
|
||||
OR `tabItem`.`end_of_life`="0000-00-00" OR `tabItem`.`end_of_life` > NOW()) \
|
||||
AND tabItem.%(key)s like "%s" \
|
||||
ORDER BY `tabItem`.`name` LIMIT 50';
|
||||
}
|
||||
|
||||
cur_frm.fields_dict['pp_details'].grid.get_field('bom_no').get_query = function(doc) {
|
||||
var d = locals[this.doctype][this.docname];
|
||||
if (d.item_code) {
|
||||
return erpnext.queries.bom({item: cstr(d.item_code)});
|
||||
} else msgprint(" Please enter Item first");
|
||||
}
|
||||
|
||||
cur_frm.fields_dict.customer.get_query = erpnext.utils.customer_query;
|
||||
|
||||
cur_frm.fields_dict.pp_so_details.grid.get_field("customer").get_query =
|
||||
erpnext.utils.customer_query;
|
||||
@@ -0,0 +1,295 @@
|
||||
# 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
|
||||
from webnotes.utils import cstr, flt, nowdate, get_defaults
|
||||
from webnotes.model.doc import addchild, Document
|
||||
from webnotes.model.wrapper import getlist
|
||||
from webnotes.model.code import get_obj
|
||||
from webnotes import msgprint
|
||||
|
||||
sql = webnotes.conn.sql
|
||||
|
||||
class DocType:
|
||||
def __init__(self, doc, doclist=[]):
|
||||
self.doc = doc
|
||||
self.doclist = doclist
|
||||
self.item_dict = {}
|
||||
|
||||
def get_so_details(self, so):
|
||||
"""Pull other details from so"""
|
||||
so = sql("""select transaction_date, customer, grand_total
|
||||
from `tabSales Order` where name = %s""", so, as_dict = 1)
|
||||
ret = {
|
||||
'sales_order_date': so and so[0]['transaction_date'] or '',
|
||||
'customer' : so[0]['customer'] or '',
|
||||
'grand_total': so[0]['grand_total']
|
||||
}
|
||||
return ret
|
||||
|
||||
def get_item_details(self, item_code):
|
||||
""" Pull other item details from item master"""
|
||||
|
||||
item = sql("""select description, stock_uom, default_bom
|
||||
from `tabItem` where name = %s""", item_code, as_dict =1)
|
||||
ret = {
|
||||
'description' : item and item[0]['description'],
|
||||
'stock_uom' : item and item[0]['stock_uom'],
|
||||
'bom_no' : item and item[0]['default_bom']
|
||||
}
|
||||
return ret
|
||||
|
||||
def clear_so_table(self):
|
||||
self.doclist = self.doc.clear_table(self.doclist, 'pp_so_details')
|
||||
|
||||
def clear_item_table(self):
|
||||
self.doclist = self.doc.clear_table(self.doclist, 'pp_details')
|
||||
|
||||
def validate_company(self):
|
||||
if not self.doc.company:
|
||||
msgprint("Please enter Company", raise_exception=1)
|
||||
|
||||
def get_open_sales_orders(self):
|
||||
""" Pull sales orders which are pending to deliver based on criteria selected"""
|
||||
so_filter = item_filter = ""
|
||||
if self.doc.from_date:
|
||||
so_filter += ' and so.transaction_date >= "' + self.doc.from_date + '"'
|
||||
if self.doc.to_date:
|
||||
so_filter += ' and so.transaction_date <= "' + self.doc.to_date + '"'
|
||||
if self.doc.customer:
|
||||
so_filter += ' and so.customer = "' + self.doc.customer + '"'
|
||||
|
||||
if self.doc.fg_item:
|
||||
item_filter += ' and item.name = "' + self.doc.fg_item + '"'
|
||||
|
||||
open_so = sql("""
|
||||
select distinct so.name, so.transaction_date, so.customer, so.grand_total
|
||||
from `tabSales Order` so, `tabSales Order Item` so_item
|
||||
where so_item.parent = so.name
|
||||
and so.docstatus = 1 and so.status != "Stopped"
|
||||
and so.company = %s
|
||||
and ifnull(so_item.qty, 0) > ifnull(so_item.delivered_qty, 0) %s
|
||||
and (exists (select * from `tabItem` item where item.name=so_item.item_code
|
||||
and (ifnull(item.is_pro_applicable, 'No') = 'Yes'
|
||||
or ifnull(item.is_sub_contracted_item, 'No') = 'Yes') %s)
|
||||
or exists (select * from `tabDelivery Note Packing Item` dnpi
|
||||
where dnpi.parent = so.name and dnpi.parent_item = so_item.item_code
|
||||
and exists (select * from `tabItem` item where item.name=dnpi.item_code
|
||||
and (ifnull(item.is_pro_applicable, 'No') = 'Yes'
|
||||
or ifnull(item.is_sub_contracted_item, 'No') = 'Yes') %s)))
|
||||
""" % ('%s', so_filter, item_filter, item_filter), self.doc.company, as_dict=1)
|
||||
|
||||
self.add_so_in_table(open_so)
|
||||
|
||||
def add_so_in_table(self, open_so):
|
||||
""" Add sales orders in the table"""
|
||||
so_list = [d.sales_order for d in getlist(self.doclist, 'pp_so_details')]
|
||||
for r in open_so:
|
||||
if cstr(r['name']) not in so_list:
|
||||
pp_so = addchild(self.doc, 'pp_so_details',
|
||||
'Production Plan Sales Order', 1, self.doclist)
|
||||
pp_so.sales_order = r['name']
|
||||
pp_so.sales_order_date = cstr(r['transaction_date'])
|
||||
pp_so.customer = cstr(r['customer'])
|
||||
pp_so.grand_total = flt(r['grand_total'])
|
||||
|
||||
def get_items_from_so(self):
|
||||
""" Pull items from Sales Order, only proction item
|
||||
and subcontracted item will be pulled from Packing item
|
||||
and add items in the table
|
||||
"""
|
||||
items = self.get_items()
|
||||
self.add_items(items)
|
||||
|
||||
def get_items(self):
|
||||
so_list = filter(None, [d.sales_order for d in getlist(self.doclist, 'pp_so_details')])
|
||||
if not so_list:
|
||||
msgprint("Please enter sales order in the above table", raise_exception=1)
|
||||
|
||||
items = sql("""select distinct parent, item_code,
|
||||
(qty - ifnull(delivered_qty, 0)) as pending_qty
|
||||
from `tabSales Order Item` so_item
|
||||
where parent in (%s) and docstatus = 1 and ifnull(qty, 0) > ifnull(delivered_qty, 0)
|
||||
and exists (select * from `tabItem` item where item.name=so_item.item_code
|
||||
and (ifnull(item.is_pro_applicable, 'No') = 'Yes'
|
||||
or ifnull(item.is_sub_contracted_item, 'No') = 'Yes'))""" % \
|
||||
(", ".join(["%s"] * len(so_list))), tuple(so_list), as_dict=1)
|
||||
|
||||
dnpi_items = sql("""select distinct dnpi.parent, dnpi.item_code,
|
||||
(((so_item.qty - ifnull(so_item.delivered_qty, 0)) * dnpi.qty) / so_item.qty)
|
||||
as pending_qty
|
||||
from `tabSales Order Item` so_item, `tabDelivery Note Packing Item` dnpi
|
||||
where so_item.parent = dnpi.parent and so_item.docstatus = 1
|
||||
and dnpi.parent_item = so_item.item_code
|
||||
and so_item.parent in (%s) and ifnull(so_item.qty, 0) > ifnull(so_item.delivered_qty, 0)
|
||||
and exists (select * from `tabItem` item where item.name=dnpi.item_code
|
||||
and (ifnull(item.is_pro_applicable, 'No') = 'Yes'
|
||||
or ifnull(item.is_sub_contracted_item, 'No') = 'Yes'))""" % \
|
||||
(", ".join(["%s"] * len(so_list))), tuple(so_list), as_dict=1)
|
||||
|
||||
return items + dnpi_items
|
||||
|
||||
|
||||
def add_items(self, items):
|
||||
self.clear_item_table()
|
||||
|
||||
for p in items:
|
||||
item_details = sql("""select description, stock_uom, default_bom
|
||||
from tabItem where name=%s""", p['item_code'])
|
||||
pi = addchild(self.doc, 'pp_details', 'Production Plan Item', 1, self.doclist)
|
||||
pi.sales_order = p['parent']
|
||||
pi.item_code = p['item_code']
|
||||
pi.description = item_details and item_details[0][0] or ''
|
||||
pi.stock_uom = item_details and item_details[0][1] or ''
|
||||
pi.bom_no = item_details and item_details[0][2] or ''
|
||||
pi.so_pending_qty = flt(p['pending_qty'])
|
||||
pi.planned_qty = flt(p['pending_qty'])
|
||||
|
||||
|
||||
def validate_data(self):
|
||||
for d in getlist(self.doclist, 'pp_details'):
|
||||
self.validate_bom_no(d)
|
||||
if not flt(d.planned_qty):
|
||||
msgprint("Please Enter Planned Qty for item: %s at row no: %s" %
|
||||
(d.item_code, d.idx), raise_exception=1)
|
||||
|
||||
def validate_bom_no(self, d):
|
||||
if not d.bom_no:
|
||||
msgprint("Please enter bom no for item: %s at row no: %s" %
|
||||
(d.item_code, d.idx), raise_exception=1)
|
||||
else:
|
||||
bom = sql("""select name from `tabBOM` where name = %s and item = %s
|
||||
and docstatus = 1 and ifnull(is_active, 'No') = 'Yes'""",
|
||||
(d.bom_no, d.item_code), as_dict = 1)
|
||||
if not bom:
|
||||
msgprint("""Incorrect BOM No: %s entered for item: %s at row no: %s
|
||||
May be BOM is inactive or for other item or does not exists in the system""" %
|
||||
(d.bom_no, d.item_doce, d.idx), raise_exception=1)
|
||||
|
||||
def raise_production_order(self):
|
||||
"""It will raise production order (Draft) for all distinct FG items"""
|
||||
self.validate_company()
|
||||
self.validate_data()
|
||||
|
||||
items = self.get_distinct_items_and_boms()[1]
|
||||
pro = self.create_production_order(items)
|
||||
if pro:
|
||||
msgprint("Following Production Order has been generated:\n" + '\n'.join(pro))
|
||||
else :
|
||||
msgprint("No Production Order generated.")
|
||||
|
||||
|
||||
def get_distinct_items_and_boms(self):
|
||||
""" Club similar BOM and item for processing"""
|
||||
item_dict, bom_dict = {}, {}
|
||||
for d in self.doclist.get({"parentfield": "pp_details"}):
|
||||
bom_dict[d.bom_no] = bom_dict.get(d.bom_no, 0) + flt(d.planned_qty)
|
||||
item_dict[(d.item_code, d.sales_order)] = {
|
||||
"qty" : flt(item_dict.get((d.item_code, d.sales_order), \
|
||||
{}).get("qty")) + flt(d.planned_qty),
|
||||
"bom_no" : d.bom_no,
|
||||
"description" : d.description,
|
||||
"stock_uom" : d.stock_uom,
|
||||
"use_multi_level_bom": self.doc.use_multi_level_bom,
|
||||
"company" : self.doc.company,
|
||||
"posting_date" : nowdate(),
|
||||
"origin" : "MRP",
|
||||
"wip_warehouse" : "",
|
||||
"fg_warehouse" : "",
|
||||
"status" : "Draft",
|
||||
"fiscal_year" : get_defaults()["fiscal_year"]
|
||||
}
|
||||
return bom_dict, item_dict
|
||||
|
||||
def create_production_order(self, items):
|
||||
"""Create production order. Called from Production Planning Tool"""
|
||||
|
||||
pro_list = []
|
||||
for item_so in items:
|
||||
pro_doc = Document('Production Order')
|
||||
pro_doc.production_item = item_so[0]
|
||||
pro_doc.sales_order = item_so[1]
|
||||
for key in items[item_so]:
|
||||
pro_doc.fields[key] = items[item_so][key]
|
||||
|
||||
pro_doc.save(new = 1)
|
||||
|
||||
get_obj("Production Order", pro_doc.name).validate_production_order_against_so()
|
||||
pro_list.append(pro_doc.name)
|
||||
|
||||
return pro_list
|
||||
|
||||
def download_raw_materials(self):
|
||||
""" Create csv data for required raw material to produce finished goods"""
|
||||
bom_dict = self.get_distinct_items_and_boms()[0]
|
||||
self.get_raw_materials(bom_dict)
|
||||
return self.get_csv()
|
||||
|
||||
def get_raw_materials(self, bom_dict):
|
||||
""" Get raw materials considering sub-assembly items """
|
||||
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
|
||||
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
|
||||
""" , (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 item_code, ifnull(sum(qty_consumed_per_unit), 0) * '%s',
|
||||
description, stock_uom
|
||||
from `tabBOM Item`
|
||||
where parent = '%s' and docstatus < 2
|
||||
group by item_code
|
||||
""" % (flt(bom_dict[bom]), bom))
|
||||
|
||||
self.make_items_dict(fl_bom_items)
|
||||
|
||||
def make_items_dict(self, item_list):
|
||||
for i in item_list:
|
||||
self.item_dict[i[0]] = [(flt(self.item_dict.get(i[0], [0])[0]) + flt(i[1])), i[2], i[3]]
|
||||
|
||||
|
||||
def get_csv(self):
|
||||
item_list = [['Item Code', 'Description', 'Stock UOM', 'Required Qty', 'Warehouse',
|
||||
'Quantity Requested for Purchase', 'Ordered Qty', 'Actual Qty']]
|
||||
for d in self.item_dict:
|
||||
item_list.append([d, self.item_dict[d][1], self.item_dict[d][2], self.item_dict[d][0]])
|
||||
item_qty= sql("""select warehouse, indented_qty, ordered_qty, actual_qty
|
||||
from `tabBin` where item_code = %s""", d)
|
||||
i_qty, o_qty, a_qty = 0, 0, 0
|
||||
for w in item_qty:
|
||||
i_qty, o_qty, a_qty = i_qty + flt(w[1]), o_qty + flt(w[2]), a_qty + flt(w[3])
|
||||
item_list.append(['', '', '', '', w[0], flt(w[1]), flt(w[2]), flt(w[3])])
|
||||
if item_qty:
|
||||
item_list.append(['', '', '', '', 'Total', i_qty, o_qty, a_qty])
|
||||
|
||||
return item_list
|
||||
@@ -0,0 +1,219 @@
|
||||
[
|
||||
{
|
||||
"owner": "jai@webnotestech.com",
|
||||
"docstatus": 0,
|
||||
"creation": "2012-07-03 13:30:03",
|
||||
"modified_by": "Administrator",
|
||||
"modified": "2012-12-10 18:30:00"
|
||||
},
|
||||
{
|
||||
"read_only": 1,
|
||||
"issingle": 1,
|
||||
"in_create": 1,
|
||||
"default_print_format": "Standard",
|
||||
"doctype": "DocType",
|
||||
"module": "Manufacturing",
|
||||
"name": "__common__"
|
||||
},
|
||||
{
|
||||
"name": "__common__",
|
||||
"parent": "Production Planning Tool",
|
||||
"doctype": "DocField",
|
||||
"parenttype": "DocType",
|
||||
"permlevel": 0,
|
||||
"parentfield": "fields"
|
||||
},
|
||||
{
|
||||
"name": "__common__",
|
||||
"parent": "Production Planning Tool",
|
||||
"read": 1,
|
||||
"create": 1,
|
||||
"doctype": "DocPerm",
|
||||
"write": 1,
|
||||
"parenttype": "DocType",
|
||||
"permlevel": 0,
|
||||
"parentfield": "permissions"
|
||||
},
|
||||
{
|
||||
"name": "Production Planning Tool",
|
||||
"doctype": "DocType"
|
||||
},
|
||||
{
|
||||
"description": "Select Sales Orders from which you want to create Production Orders.",
|
||||
"colour": "White:FFF",
|
||||
"doctype": "DocField",
|
||||
"label": "Select Sales Orders",
|
||||
"fieldname": "select_sales_orders",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"width": "50%",
|
||||
"fieldname": "column_break0",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"label": "Filter based on item",
|
||||
"fieldname": "fg_item",
|
||||
"fieldtype": "Link",
|
||||
"options": "Item"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"label": "Filter based on customer",
|
||||
"fieldname": "customer",
|
||||
"fieldtype": "Link",
|
||||
"options": "Customer"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"label": "Company",
|
||||
"reqd": 1,
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"options": "Company"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"width": "50%",
|
||||
"fieldname": "column_break1",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"label": "From Date",
|
||||
"fieldname": "from_date",
|
||||
"fieldtype": "Date"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"label": "To Date",
|
||||
"fieldname": "to_date",
|
||||
"fieldtype": "Date"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "section_break1",
|
||||
"fieldtype": "Section Break",
|
||||
"options": "Simple"
|
||||
},
|
||||
{
|
||||
"description": "Pull sales orders (pending to deliver) based on the above criteria",
|
||||
"colour": "White:FFF",
|
||||
"doctype": "DocField",
|
||||
"label": "Get Sales Orders",
|
||||
"fieldname": "get_sales_orders",
|
||||
"fieldtype": "Button",
|
||||
"options": "get_open_sales_orders"
|
||||
},
|
||||
{
|
||||
"colour": "White:FFF",
|
||||
"doctype": "DocField",
|
||||
"label": "Production Plan Sales Orders",
|
||||
"fieldname": "pp_so_details",
|
||||
"fieldtype": "Table",
|
||||
"options": "Production Plan Sales Order"
|
||||
},
|
||||
{
|
||||
"colour": "White:FFF",
|
||||
"doctype": "DocField",
|
||||
"label": "Clear Table",
|
||||
"trigger": "Client",
|
||||
"fieldname": "clear_so_table",
|
||||
"fieldtype": "Button",
|
||||
"options": "clear_so_table"
|
||||
},
|
||||
{
|
||||
"description": "Enter items and planned qty for which you want to raise production orders or download raw materials for analysis.",
|
||||
"colour": "White:FFF",
|
||||
"doctype": "DocField",
|
||||
"label": "Create Production Orders",
|
||||
"fieldname": "create_production_orders",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"description": "Pull items from Sales Order mentioned in the above table.",
|
||||
"colour": "White:FFF",
|
||||
"doctype": "DocField",
|
||||
"label": "Get Items",
|
||||
"fieldname": "get_items_from_so",
|
||||
"fieldtype": "Button",
|
||||
"options": "get_items_from_so"
|
||||
},
|
||||
{
|
||||
"colour": "White:FFF",
|
||||
"doctype": "DocField",
|
||||
"label": "Production Plan Items",
|
||||
"fieldname": "pp_details",
|
||||
"fieldtype": "Table",
|
||||
"options": "Production Plan Item"
|
||||
},
|
||||
{
|
||||
"colour": "White:FFF",
|
||||
"doctype": "DocField",
|
||||
"label": "Clear Table",
|
||||
"trigger": "Client",
|
||||
"fieldname": "clear_item_table",
|
||||
"fieldtype": "Button",
|
||||
"options": "clear_item_table"
|
||||
},
|
||||
{
|
||||
"description": "If checked, BOM for sub-assembly items will be considered for raw materials. Otherwise, all sub-assembly items will be treated as a raw material.",
|
||||
"default": "1",
|
||||
"colour": "White:FFF",
|
||||
"doctype": "DocField",
|
||||
"label": "Use Multi-Level BOM",
|
||||
"reqd": 0,
|
||||
"fieldname": "use_multi_level_bom",
|
||||
"fieldtype": "Check"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"fieldname": "section_break3",
|
||||
"fieldtype": "Section Break",
|
||||
"options": "Simple"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"width": "50%",
|
||||
"fieldname": "column_break5",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"description": "Separate production order will be created for each finished good item.",
|
||||
"colour": "White:FFF",
|
||||
"doctype": "DocField",
|
||||
"label": "Raise Production Order",
|
||||
"fieldname": "raise_production_order",
|
||||
"fieldtype": "Button",
|
||||
"options": "raise_production_order"
|
||||
},
|
||||
{
|
||||
"doctype": "DocField",
|
||||
"width": "50%",
|
||||
"fieldname": "column_break6",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"description": "Download a report containing all raw materials with their latest inventory status",
|
||||
"colour": "White:FFF",
|
||||
"doctype": "DocField",
|
||||
"label": "Download Materials Required",
|
||||
"trigger": "Client",
|
||||
"fieldname": "download_materials_required",
|
||||
"fieldtype": "Button"
|
||||
},
|
||||
{
|
||||
"role": "System Manager",
|
||||
"doctype": "DocPerm"
|
||||
},
|
||||
{
|
||||
"role": "Manufacturing User",
|
||||
"doctype": "DocPerm"
|
||||
},
|
||||
{
|
||||
"role": "Manufacturing Manager",
|
||||
"doctype": "DocPerm"
|
||||
}
|
||||
]
|
||||
1
manufacturing/doctype/workstation/__init__.py
Normal file
1
manufacturing/doctype/workstation/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from __future__ import unicode_literals
|
||||
26
manufacturing/doctype/workstation/workstation.js
Normal file
26
manufacturing/doctype/workstation/workstation.js
Normal file
@@ -0,0 +1,26 @@
|
||||
// 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/>.
|
||||
|
||||
|
||||
|
||||
//--------- ONLOAD -------------
|
||||
cur_frm.cscript.onload = function(doc, cdt, cdn) {
|
||||
|
||||
}
|
||||
|
||||
cur_frm.cscript.refresh = function(doc, cdt, cdn) {
|
||||
|
||||
}
|
||||
41
manufacturing/doctype/workstation/workstation.py
Normal file
41
manufacturing/doctype/workstation/workstation.py
Normal file
@@ -0,0 +1,41 @@
|
||||
# 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
|
||||
|
||||
from webnotes.utils import flt
|
||||
from webnotes.model import db_exists
|
||||
from webnotes.model.wrapper import copy_doclist
|
||||
|
||||
sql = webnotes.conn.sql
|
||||
|
||||
|
||||
|
||||
class DocType:
|
||||
def __init__(self, doc, doclist=[]):
|
||||
self.doc = doc
|
||||
self.doclist = doclist
|
||||
|
||||
def update_bom_operation(self):
|
||||
bom_list = sql(" select DISTINCT parent from `tabBOM Operation` where workstation = '%s'" % self.doc.name)
|
||||
for bom_no in bom_list:
|
||||
sql("update `tabBOM Operation` set hour_rate = '%s' where parent = '%s' and workstation = '%s'"%( self.doc.hour_rate, bom_no[0], self.doc.name))
|
||||
|
||||
def on_update(self):
|
||||
webnotes.conn.set(self.doc, 'overhead', flt(self.doc.hour_rate_electricity) + flt(self.doc.hour_rate_consumable) + flt(self.doc.hour_rate_rent))
|
||||
webnotes.conn.set(self.doc, 'hour_rate', flt(self.doc.hour_rate_labour) + flt(self.doc.overhead))
|
||||
self.update_bom_operation()
|
||||
269
manufacturing/doctype/workstation/workstation.txt
Normal file
269
manufacturing/doctype/workstation/workstation.txt
Normal file
@@ -0,0 +1,269 @@
|
||||
# DocType, Workstation
|
||||
[
|
||||
|
||||
# These values are common in all dictionaries
|
||||
{
|
||||
'creation': '2012-03-27 14:36:05',
|
||||
'docstatus': 0,
|
||||
'modified': '2012-12-10 18:30:00',
|
||||
'modified_by': u'Administrator',
|
||||
'owner': u'Administrator'
|
||||
},
|
||||
|
||||
# These values are common for all DocType
|
||||
{
|
||||
'_last_update': u'1322125389',
|
||||
'allow_email': 0,
|
||||
'allow_trash': 1,
|
||||
'autoname': u'field:workstation_name',
|
||||
'colour': u'White:FFF',
|
||||
'default_print_format': u'Standard',
|
||||
'doctype': 'DocType',
|
||||
'document_type': u'Master',
|
||||
'module': u'Manufacturing',
|
||||
'name': '__common__',
|
||||
'section_style': u'Simple',
|
||||
'server_code_error': u' ',
|
||||
'show_in_menu': 0,
|
||||
'version': 14
|
||||
},
|
||||
|
||||
# These values are common for all DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'name': '__common__',
|
||||
'parent': u'Workstation',
|
||||
'parentfield': u'fields',
|
||||
'parenttype': u'DocType'
|
||||
},
|
||||
|
||||
# These values are common for all DocPerm
|
||||
{
|
||||
'doctype': u'DocPerm',
|
||||
'name': '__common__',
|
||||
'parent': u'Workstation',
|
||||
'parentfield': u'permissions',
|
||||
'parenttype': u'DocType',
|
||||
'read': 1
|
||||
},
|
||||
|
||||
# DocType, Workstation
|
||||
{
|
||||
'doctype': 'DocType',
|
||||
'name': u'Workstation'
|
||||
},
|
||||
|
||||
# DocPerm
|
||||
{
|
||||
'cancel': 1,
|
||||
'create': 1,
|
||||
'doctype': u'DocPerm',
|
||||
'permlevel': 0,
|
||||
'role': u'System Manager',
|
||||
'write': 1
|
||||
},
|
||||
|
||||
# DocPerm
|
||||
{
|
||||
'doctype': u'DocPerm',
|
||||
'permlevel': 1,
|
||||
'role': u'System Manager'
|
||||
},
|
||||
|
||||
# DocPerm
|
||||
{
|
||||
'cancel': 1,
|
||||
'create': 1,
|
||||
'doctype': u'DocPerm',
|
||||
'permlevel': 0,
|
||||
'role': u'Manufacturing User',
|
||||
'write': 1
|
||||
},
|
||||
|
||||
# DocPerm
|
||||
{
|
||||
'cancel': 1,
|
||||
'create': 1,
|
||||
'doctype': u'DocPerm',
|
||||
'permlevel': 0,
|
||||
'role': u'Manufacturing User',
|
||||
'write': 1
|
||||
},
|
||||
|
||||
# DocPerm
|
||||
{
|
||||
'doctype': u'DocPerm',
|
||||
'permlevel': 1,
|
||||
'role': u'Manufacturing Manager'
|
||||
},
|
||||
|
||||
# DocPerm
|
||||
{
|
||||
'doctype': u'DocPerm',
|
||||
'permlevel': 1,
|
||||
'role': u'Manufacturing User'
|
||||
},
|
||||
|
||||
# DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'fieldname': u'trash_reason',
|
||||
'fieldtype': u'Small Text',
|
||||
'label': u'Trash Reason',
|
||||
'oldfieldname': u'trash_reason',
|
||||
'oldfieldtype': u'Small Text',
|
||||
'permlevel': 1
|
||||
},
|
||||
|
||||
# DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'fieldname': u'workstation_name',
|
||||
'fieldtype': u'Data',
|
||||
'label': u'Workstation Name',
|
||||
'oldfieldname': u'workstation_name',
|
||||
'oldfieldtype': u'Data',
|
||||
'permlevel': 0,
|
||||
'reqd': 1
|
||||
},
|
||||
|
||||
# DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'fieldname': u'warehouse',
|
||||
'fieldtype': u'Link',
|
||||
'label': u'Warehouse',
|
||||
'oldfieldname': u'warehouse',
|
||||
'oldfieldtype': u'Link',
|
||||
'options': u'Warehouse',
|
||||
'permlevel': 0,
|
||||
'reqd': 1
|
||||
},
|
||||
|
||||
# DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'fieldname': u'description',
|
||||
'fieldtype': u'Text',
|
||||
'label': u'Description',
|
||||
'oldfieldname': u'description',
|
||||
'oldfieldtype': u'Text',
|
||||
'permlevel': 0,
|
||||
'width': u'300px'
|
||||
},
|
||||
|
||||
# DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'fieldname': u'capacity',
|
||||
'fieldtype': u'Data',
|
||||
'hidden': 1,
|
||||
'label': u'Capacity',
|
||||
'oldfieldname': u'capacity',
|
||||
'oldfieldtype': u'Data',
|
||||
'permlevel': 0,
|
||||
'reqd': 0
|
||||
},
|
||||
|
||||
# DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'fieldname': u'capacity_units',
|
||||
'fieldtype': u'Select',
|
||||
'hidden': 1,
|
||||
'label': u'Capacity Units',
|
||||
'oldfieldname': u'capacity_units',
|
||||
'oldfieldtype': u'Select',
|
||||
'options': u'\nUnits/Shifts\nUnits/Hour',
|
||||
'permlevel': 0,
|
||||
'reqd': 0
|
||||
},
|
||||
|
||||
# DocField
|
||||
{
|
||||
'colour': u'White:FFF',
|
||||
'doctype': u'DocField',
|
||||
'fieldname': u'hour_rate_labour',
|
||||
'fieldtype': u'Currency',
|
||||
'label': u'Hour Rate Labour',
|
||||
'oldfieldname': u'hour_rate_labour',
|
||||
'oldfieldtype': u'Currency',
|
||||
'permlevel': 0,
|
||||
'reqd': 0
|
||||
},
|
||||
|
||||
# DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'fieldname': u'over_heads',
|
||||
'fieldtype': u'Section Break',
|
||||
'label': u'Over Heads',
|
||||
'oldfieldtype': u'Section Break',
|
||||
'permlevel': 0
|
||||
},
|
||||
|
||||
# DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'fieldname': u'hour_rate_electricity',
|
||||
'fieldtype': u'Currency',
|
||||
'label': u'Hour Rate Electricity',
|
||||
'oldfieldname': u'hour_rate_electricity',
|
||||
'oldfieldtype': u'Currency',
|
||||
'permlevel': 0
|
||||
},
|
||||
|
||||
# DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'fieldname': u'hour_rate_consumable',
|
||||
'fieldtype': u'Currency',
|
||||
'label': u'Hour Rate Consumable',
|
||||
'oldfieldname': u'hour_rate_consumable',
|
||||
'oldfieldtype': u'Currency',
|
||||
'permlevel': 0
|
||||
},
|
||||
|
||||
# DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'fieldname': u'hour_rate_rent',
|
||||
'fieldtype': u'Currency',
|
||||
'label': u'Hour Rate Rent',
|
||||
'oldfieldname': u'hour_rate_rent',
|
||||
'oldfieldtype': u'Currency',
|
||||
'permlevel': 0
|
||||
},
|
||||
|
||||
# DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'fieldname': u'overhead',
|
||||
'fieldtype': u'Currency',
|
||||
'label': u'Overhead',
|
||||
'oldfieldname': u'overhead',
|
||||
'oldfieldtype': u'Currency',
|
||||
'permlevel': 1
|
||||
},
|
||||
|
||||
# DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'fieldname': u'hour_rate_section_break',
|
||||
'fieldtype': u'Section Break',
|
||||
'label': u'Hour Rate',
|
||||
'oldfieldtype': u'Section Break',
|
||||
'permlevel': 0
|
||||
},
|
||||
|
||||
# DocField
|
||||
{
|
||||
'doctype': u'DocField',
|
||||
'fieldname': u'hour_rate',
|
||||
'fieldtype': u'Currency',
|
||||
'label': u'Hour Rate',
|
||||
'oldfieldname': u'hour_rate',
|
||||
'oldfieldtype': u'Currency',
|
||||
'permlevel': 1
|
||||
}
|
||||
]
|
||||
1
manufacturing/page/__init__.py
Normal file
1
manufacturing/page/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from __future__ import unicode_literals
|
||||
1
manufacturing/page/manufacturing_home/__init__.py
Normal file
1
manufacturing/page/manufacturing_home/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from __future__ import unicode_literals
|
||||
@@ -0,0 +1,48 @@
|
||||
<div class="layout-wrapper layout-wrapper-background">
|
||||
<div class="appframe-area"></div>
|
||||
<div class="layout-main-section">
|
||||
<div style="width: 48%; float: left;">
|
||||
<h4><a href="#!List/Production Order">Production Order</a></h4>
|
||||
<p class="help">Orders for manufacturing</p>
|
||||
<br>
|
||||
<h4><a href="#!Form/Production Planning Tool/Production Planning Tool">Production Plan</a></h4>
|
||||
<p class="help">Generate Purchase Requisition (MRP) and Production Orders</p>
|
||||
</div>
|
||||
<div style="width: 48%; float: right;">
|
||||
<h4><a href="#!List/BOM">Bill of Materials</a></h4>
|
||||
<p class="help">Bill of Materials (BOM) Master</p>
|
||||
<br>
|
||||
<h4><a href="#!List/Item">Item</a></h4>
|
||||
<p class="help">Item Master</p>
|
||||
</div>
|
||||
<div style="clear: both"></div>
|
||||
<hr>
|
||||
<h3>Reports</h3>
|
||||
<div class="reports-list"></div>
|
||||
</div>
|
||||
<div class="layout-side-section">
|
||||
<div class="psidebar">
|
||||
<div class="section">
|
||||
<div class="section-head">Tools</div>
|
||||
<div class="section-body">
|
||||
<div class="section-item">
|
||||
<a class="section-link"
|
||||
title = "BOM Replace Tool"
|
||||
href="#!Form/BOM Replace Tool">BOM Replace Tool</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section">
|
||||
<div class="section-head">Setup</div>
|
||||
<div class="section-body">
|
||||
<div class="section-item">
|
||||
<a class="section-link"
|
||||
title = "Workstation master"
|
||||
href="#!List/Workstation">Workstations</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="clear: both;"></div>
|
||||
</div>
|
||||
20
manufacturing/page/manufacturing_home/manufacturing_home.js
Normal file
20
manufacturing/page/manufacturing_home/manufacturing_home.js
Normal file
@@ -0,0 +1,20 @@
|
||||
// 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/>.
|
||||
|
||||
pscript['onload_manufacturing-home'] = function(wrapper) {
|
||||
wrapper.appframe = new wn.ui.AppFrame($(wrapper).find('.appframe-area'), 'Manufacturing');
|
||||
erpnext.module_page.setup_page('Manufacturing', wrapper);
|
||||
}
|
||||
28
manufacturing/page/manufacturing_home/manufacturing_home.txt
Normal file
28
manufacturing/page/manufacturing_home/manufacturing_home.txt
Normal file
@@ -0,0 +1,28 @@
|
||||
# Page, production-home
|
||||
[
|
||||
|
||||
# These values are common in all dictionaries
|
||||
{
|
||||
'creation': '2012-02-21 13:24:34',
|
||||
'docstatus': 0,
|
||||
'modified': '2012-12-10 18:30:00',
|
||||
'modified_by': u'Administrator',
|
||||
'owner': u'Administrator'
|
||||
},
|
||||
|
||||
# These values are common for all Page
|
||||
{
|
||||
'doctype': 'Page',
|
||||
'module': u'Manufacturing',
|
||||
'name': '__common__',
|
||||
'page_name': u'manufacturing-home',
|
||||
'standard': u'Yes',
|
||||
'title': u'Manufacturing Home'
|
||||
},
|
||||
|
||||
# Page, production-home
|
||||
{
|
||||
'doctype': 'Page',
|
||||
'name': u'manufacturing-home'
|
||||
}
|
||||
]
|
||||
1
manufacturing/search_criteria/__init__.py
Normal file
1
manufacturing/search_criteria/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from __future__ import unicode_literals
|
||||
@@ -0,0 +1 @@
|
||||
from __future__ import unicode_literals
|
||||
@@ -0,0 +1,37 @@
|
||||
# Search Criteria, consumption_against_production
|
||||
[
|
||||
|
||||
# These values are common in all dictionaries
|
||||
{
|
||||
'creation': '2012-04-03 12:49:50',
|
||||
'docstatus': 0,
|
||||
'modified': '2012-12-10 18:30:00',
|
||||
'modified_by': u'Administrator',
|
||||
'owner': u'jai@webnotestech.com'
|
||||
},
|
||||
|
||||
# These values are common for all Search Criteria
|
||||
{
|
||||
'add_col': u'`tabProduction Order`.consider_sa_items',
|
||||
'add_cond': u'`tabProduction Order`.name = `tabStock Entry`.production_order',
|
||||
'add_tab': u'`tabProduction Order`',
|
||||
'columns': u'Stock Entry\x01ID,Stock Entry\x01Production Order,Stock Entry\x01Process,Stock Entry\x01Posting Date,Stock Entry\x01Company,Stock Entry Detail\x01Source Warehouse,Stock Entry Detail\x01Target Warehouse,Stock Entry Detail\x01FG Item,Stock Entry Detail\x01Item Code,Stock Entry Detail\x01Description,Stock Entry Detail\x01Reqd Qty,Stock Entry Detail\x01Transfer Quantity',
|
||||
'criteria_name': u'Consumption Against Production',
|
||||
'doc_type': u'Stock Entry Detail',
|
||||
'doctype': 'Search Criteria',
|
||||
'filters': u"{'Stock Entry\x01Submitted':1,'Stock Entry\x01Purpose':'Production Order','Stock Entry\x01Process':''}",
|
||||
'module': u'Manufacturing',
|
||||
'name': '__common__',
|
||||
'page_len': 50,
|
||||
'parent_doc_type': u'Stock Entry',
|
||||
'sort_by': u'`tabStock Entry`.`name`',
|
||||
'sort_order': u'DESC',
|
||||
'standard': u'Yes'
|
||||
},
|
||||
|
||||
# Search Criteria, consumption_against_production
|
||||
{
|
||||
'doctype': 'Search Criteria',
|
||||
'name': u'consumption_against_production'
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
from __future__ import unicode_literals
|
||||
@@ -0,0 +1,32 @@
|
||||
// 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/>.
|
||||
|
||||
report.customize_filters = function() {
|
||||
|
||||
//to hide all filters
|
||||
this.hide_all_filters();
|
||||
|
||||
// to unhide required filters
|
||||
this.filter_fields_dict['Stock Entry'+FILTER_SEP +'ID'].df.filter_hide = 0;
|
||||
this.filter_fields_dict['Stock Entry'+FILTER_SEP +'Production Order'].df.filter_hide = 0;
|
||||
|
||||
this.filter_fields_dict['Stock Entry'+FILTER_SEP +'From Posting Date'].df.filter_hide = 0;
|
||||
this.filter_fields_dict['Stock Entry'+FILTER_SEP +'To Posting Date'].df.filter_hide = 0;
|
||||
|
||||
this.filter_fields_dict['Stock Entry Detail'+FILTER_SEP +'Target Warehouse'].df.filter_hide = 0;
|
||||
|
||||
this.filter_fields_dict['Stock Entry Detail'+FILTER_SEP +'Item Code'].df.filter_hide = 0;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
# Search Criteria, itemwise_production_report
|
||||
[
|
||||
|
||||
# These values are common in all dictionaries
|
||||
{
|
||||
'creation': '2012-04-03 12:49:51',
|
||||
'docstatus': 0,
|
||||
'modified': '2012-12-10 18:30:00',
|
||||
'modified_by': u'Administrator',
|
||||
'owner': u'jai@webnotestech.com'
|
||||
},
|
||||
|
||||
# These values are common for all Search Criteria
|
||||
{
|
||||
'columns': u'Stock Entry\x01ID,Stock Entry\x01Posting Date,Stock Entry\x01Production Order,Stock Entry\x01Process,Stock Entry\x01Company,Stock Entry Detail\x01Target Warehouse,Stock Entry Detail\x01Item Code,Stock Entry Detail\x01Description,Stock Entry Detail\x01Stock UOM,Stock Entry Detail\x01Transfer Quantity',
|
||||
'criteria_name': u'Itemwise Production Report',
|
||||
'doc_type': u'Stock Entry Detail',
|
||||
'doctype': 'Search Criteria',
|
||||
'filters': u"{'Stock Entry\x01Saved':1,'Stock Entry\x01Submitted':1,'Stock Entry\x01Purpose':'Production Order','Stock Entry\x01Process':'Backflush','Stock Entry Detail\x01FG Item':1}",
|
||||
'module': u'Manufacturing',
|
||||
'name': '__common__',
|
||||
'page_len': 50,
|
||||
'parent_doc_type': u'Stock Entry',
|
||||
'sort_by': u'`tabStock Entry`.`name`',
|
||||
'sort_order': u'DESC',
|
||||
'standard': u'Yes'
|
||||
},
|
||||
|
||||
# Search Criteria, itemwise_production_report
|
||||
{
|
||||
'doctype': 'Search Criteria',
|
||||
'name': u'itemwise_production_report'
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1 @@
|
||||
from __future__ import unicode_literals
|
||||
@@ -0,0 +1,34 @@
|
||||
# Search Criteria, production_orders_in_process
|
||||
[
|
||||
|
||||
# These values are common in all dictionaries
|
||||
{
|
||||
'creation': '2012-04-03 12:49:52',
|
||||
'docstatus': 0,
|
||||
'modified': '2012-12-10 18:30:00',
|
||||
'modified_by': u'Administrator',
|
||||
'owner': u'jai@webnotestech.com'
|
||||
},
|
||||
|
||||
# These values are common for all Search Criteria
|
||||
{
|
||||
'add_cond': u'`tabProduction Order`.qty > `tabProduction Order`.produced_qty',
|
||||
'columns': u'Production Order\x01ID,Production Order\x01Origin,Production Order\x01Status,Production Order\x01Posting Date,Production Order\x01Production Item,Production Order\x01BOM No,Production Order\x01Description,Production Order\x01Stock UOM,Production Order\x01Qty,Production Order\x01Produced Qty,Production Order\x01FG Warehouse,Production Order\x01WIP Warehouse,Production Order\x01Consider SA Items,Production Order\x01Fiscal Year,Production Order\x01Company',
|
||||
'criteria_name': u'Production Orders In Process',
|
||||
'doc_type': u'Production Order',
|
||||
'doctype': 'Search Criteria',
|
||||
'filters': u"{'Production Order\x01Submitted':1,'Production Order\x01Origin':'','Production Order\x01Status':'','Production Order\x01Consider SA Items':'','Production Order\x01Fiscal Year':''}",
|
||||
'module': u'Manufacturing',
|
||||
'name': '__common__',
|
||||
'page_len': 50,
|
||||
'sort_by': u'`tabProduction Order`.`name`',
|
||||
'sort_order': u'DESC',
|
||||
'standard': u'Yes'
|
||||
},
|
||||
|
||||
# Search Criteria, production_orders_in_process
|
||||
{
|
||||
'doctype': 'Search Criteria',
|
||||
'name': u'production_orders_in_process'
|
||||
}
|
||||
]
|
||||
Reference in New Issue
Block a user