Merge branch 'master' of github.com:webnotes/erpnext

This commit is contained in:
Rushabh Mehta 2012-12-21 15:01:08 +05:30
commit 380d7c6a53
5 changed files with 123 additions and 47 deletions

View File

@ -1,6 +1,13 @@
erpnext.updates = [ erpnext.updates = [
["21st December 2012", [
"Manufacturing: For Material Transfer against Production Order, \
fetch quantity pending to be transferred for each item."
]],
["20th December 2012", [ ["20th December 2012", [
"Website: Create Product Search Page, Product Group Page on the website." "Website: Create Product Search Page, Product Group Page on the website.",
"Manufacturing: \
<a href=\"https://github.com/webnotes/erpnext/wiki/Changes-in-Manufacturing-Module\" \
target=\"_blank\">Refactored code, improved usability.</a>"
]], ]],
["14th December 2012", [ ["14th December 2012", [
"Website Module: Major Refactor - removed framework code from website." "Website Module: Major Refactor - removed framework code from website."

View File

@ -46,7 +46,7 @@ var cfn_set_fields = function(doc, dt, dn) {
cur_frm.add_custom_button('Unstop', cur_frm.cscript['Unstop Production Order']); cur_frm.add_custom_button('Unstop', cur_frm.cscript['Unstop Production Order']);
if (doc.status == 'Submitted' || doc.status == 'Material Transferred' || doc.status == 'In Process'){ if (doc.status == 'Submitted' || doc.status == 'Material Transferred' || doc.status == 'In Process'){
cur_frm.add_custom_button('Issue Raw Materials', cur_frm.cscript['Issue Raw Materials']); cur_frm.add_custom_button('Transfer Raw Materials', cur_frm.cscript['Transfer Raw Materials']);
cur_frm.add_custom_button('Update Finished Goods', cur_frm.cscript['Update Finished Goods']); cur_frm.add_custom_button('Update Finished Goods', cur_frm.cscript['Update Finished Goods']);
} }
} }
@ -74,7 +74,7 @@ cur_frm.cscript['Unstop Production Order'] = function() {
$c_obj(make_doclist(doc.doctype, doc.name), 'stop_unstop', 'Unstopped', function(r, rt) {cur_frm.refresh();}); $c_obj(make_doclist(doc.doctype, doc.name), 'stop_unstop', 'Unstopped', function(r, rt) {cur_frm.refresh();});
} }
cur_frm.cscript['Issue Raw Materials'] = function() { cur_frm.cscript['Transfer Raw Materials'] = function() {
var doc = cur_frm.doc; var doc = cur_frm.doc;
cur_frm.cscript.make_se(doc, 'Material Transfer'); cur_frm.cscript.make_se(doc, 'Material Transfer');
} }

View File

@ -20,6 +20,7 @@ wn.provide('erpnext.utils');
erpnext.utils.Controller = Class.extend({ erpnext.utils.Controller = Class.extend({
init: function(opts) { init: function(opts) {
$.extend(this, opts); $.extend(this, opts);
this.setup && this.setup();
}, },
onload_post_render: function() { onload_post_render: function() {
@ -40,13 +41,6 @@ erpnext.utils.Controller = Class.extend({
refresh: function() { refresh: function() {
erpnext.hide_naming_series(); erpnext.hide_naming_series();
if(this.frm.doc.__islocal && this.frm.fields_dict.naming_series
&& !this.frm.doc.naming_series) {
var series_opts = $.map((this.frm.fields_dict.naming_series.df.options ||
"").split("\n"), function(v) { return v ? v : null; });
if (series_opts.length==1) this.frm.set_value("naming_series", series_opts[0]);
}
} }
}); });

View File

@ -29,10 +29,26 @@ erpnext.stock.StockEntry = erpnext.utils.Controller.extend({
refresh: function() { refresh: function() {
this._super(); this._super();
this.toggle_related_fields(this.frm.doc); this.toggle_related_fields(this.frm.doc);
this.toggle_enable_bom();
if (this.frm.doc.docstatus==1) this.frm.add_custom_button("Show Stock Ledger", if (this.frm.doc.docstatus==1) this.frm.add_custom_button("Show Stock Ledger",
this.show_stock_ledger) this.show_stock_ledger)
}, },
on_submit: function() {
this.clean_up();
},
after_cancel: function() {
this.clean_up();
},
clean_up: function() {
// Clear Production Order record from locals, because it is updated via Stock Entry
if(this.frm.doc.production_order && this.frm.doc.purpose == "Manufacture/Repack") {
wn.model.clear_doclist("Production Order", this.frm.doc.production_order);
}
},
get_items: function() { get_items: function() {
this.frm.call({ this.frm.call({
doc: this.frm.doc, doc: this.frm.doc,
@ -49,6 +65,19 @@ erpnext.stock.StockEntry = erpnext.utils.Controller.extend({
refresh_field('mtn_details'); refresh_field('mtn_details');
}, },
production_order: function() {
this.toggle_enable_bom();
this.frm.call({
method: "get_production_order_details",
args: {production_order: this.frm.doc.production_order}
});
},
toggle_enable_bom: function() {
this.frm.toggle_enable("bom_no", !this.frm.doc.production_order);
},
}); });
cur_frm.cscript = new erpnext.stock.StockEntry({frm: cur_frm}); cur_frm.cscript = new erpnext.stock.StockEntry({frm: cur_frm});

View File

@ -32,7 +32,6 @@ class DocType(TransactionBase):
def __init__(self, doc, doclist=[]): def __init__(self, doc, doclist=[]):
self.doc = doc self.doc = doc
self.doclist = doclist self.doclist = doclist
self.item_dict = {}
self.fname = 'mtn_details' self.fname = 'mtn_details'
def validate(self): def validate(self):
@ -229,7 +228,6 @@ class DocType(TransactionBase):
self.doc.purpose) self.doc.purpose)
if self.doc.purpose == 'Purchase Return': if self.doc.purpose == 'Purchase Return':
#delete_doc("Serial No", serial_no)
serial_doc = Document("Serial No", serial_no) serial_doc = Document("Serial No", serial_no)
serial_doc.status = is_submit and 'Purchase Returned' or 'In Store' serial_doc.status = is_submit and 'Purchase Returned' or 'In Store'
serial_doc.docstatus = is_submit and 2 or 0 serial_doc.docstatus = is_submit and 2 or 0
@ -336,11 +334,7 @@ class DocType(TransactionBase):
pro_obj = get_obj('Production Order', self.doc.production_order) pro_obj = get_obj('Production Order', self.doc.production_order)
if pro_obj: if pro_obj:
self.validate_production_order(pro_obj) self.validate_production_order(pro_obj)
self.doc.bom_no = pro_obj.doc.bom_no self.doc.bom_no = pro_obj.doc.bom_no
self.doc.fg_completed_qty = (self.doc.purpose == "Manufacture/Repack") \
and flt(self.doc.fg_completed_qty) \
or flt(pro_obj.doc.qty) - flt(pro_obj.doc.produced_qty)
else: else:
# invalid production order # invalid production order
self.doc.production_order = None self.doc.production_order = None
@ -348,11 +342,14 @@ class DocType(TransactionBase):
if self.doc.bom_no: if self.doc.bom_no:
if self.doc.purpose in ["Material Issue", "Material Transfer", "Manufacture/Repack", if self.doc.purpose in ["Material Issue", "Material Transfer", "Manufacture/Repack",
"Subcontract"]: "Subcontract"]:
self.get_raw_materials() if self.doc.production_order and self.doc.purpose == "Material Transfer":
item_dict = self.get_pending_raw_materials(pro_obj)
else:
item_dict = self.get_bom_raw_materials(self.doc.fg_completed_qty)
# add raw materials to Stock Entry Detail table # add raw materials to Stock Entry Detail table
self.add_to_stock_entry_detail(self.doc.from_warehouse, self.doc.to_warehouse, self.add_to_stock_entry_detail(self.doc.from_warehouse, self.doc.to_warehouse,
self.item_dict) item_dict)
# add finished good item to Stock Entry Detail table -- along with bom_no # add finished good item to Stock Entry Detail table -- along with bom_no
if self.doc.production_order and self.doc.purpose == "Manufacture/Repack": if self.doc.production_order and self.doc.purpose == "Manufacture/Repack":
@ -371,16 +368,28 @@ class DocType(TransactionBase):
self.get_stock_and_rate() self.get_stock_and_rate()
def get_raw_materials(self): def get_bom_raw_materials(self, qty):
""" """
get all items from flat bom except get all items from flat bom except
child items of sub-contracted and sub assembly items child items of sub-contracted and sub assembly items
and sub assembly items itself. and sub assembly items itself.
""" """
# item dict = { item_code: [qty, description, stock_uom] }
item_dict = {}
def _make_items_dict(items_list):
"""makes dict of unique items with it's qty"""
for item in items_list:
if item_dict.has_key(item.item_code):
item_dict[item.item_code][0] += flt(item.qty)
else:
item_dict[item.item_code] = [flt(item.qty), item.description, item.stock_uom]
if self.doc.use_multi_level_bom: if self.doc.use_multi_level_bom:
# get all raw materials with sub assembly childs # get all raw materials with sub assembly childs
fl_bom_sa_child_item = sql("""select fl_bom_sa_child_item = sql("""select
item_code,ifnull(sum(qty_consumed_per_unit),0)*%s as qty,description,stock_uom 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, from ( select distinct fb.name, fb.description, fb.item_code,
fb.qty_consumed_per_unit, fb.stock_uom fb.qty_consumed_per_unit, fb.stock_uom
from `tabBOM Explosion Item` fb,`tabItem` it from `tabBOM Explosion Item` fb,`tabItem` it
@ -388,45 +397,73 @@ class DocType(TransactionBase):
and ifnull(it.is_sub_contracted_item, 'No') = 'No' and fb.docstatus<2 and ifnull(it.is_sub_contracted_item, 'No') = 'No' and fb.docstatus<2
and fb.parent=%s and fb.parent=%s
) a ) a
group by item_code, stock_uom""" , (self.doc.fg_completed_qty, self.doc.bom_no)) group by item_code, stock_uom""" , (qty, self.doc.bom_no), as_dict=1)
self.make_items_dict(fl_bom_sa_child_item)
if fl_bom_sa_child_item:
_make_items_dict(fl_bom_sa_child_item)
else: else:
# Get all raw materials considering multi level BOM, # Get all raw materials considering multi level BOM,
# if multi level bom consider childs of Sub-Assembly items # if multi level bom consider childs of Sub-Assembly items
fl_bom_sa_items = sql("""select item_code, ifnull(sum(qty_consumed_per_unit), 0) * '%s', fl_bom_sa_items = sql("""select item_code,
ifnull(sum(qty_consumed_per_unit), 0) * '%s' as qty,
description, stock_uom from `tabBOM Item` description, stock_uom from `tabBOM Item`
where parent = '%s' and docstatus < 2 where parent = '%s' and docstatus < 2
group by item_code""" % (self.doc.fg_completed_qty, self.doc.bom_no)) group by item_code""" % (qty, self.doc.bom_no), as_dict=1)
self.make_items_dict(fl_bom_sa_items) if fl_bom_sa_items:
_make_items_dict(fl_bom_sa_items)
# Update only qty remaining to be issued for production return item_dict
if self.doc.purpose == 'Material Transfer' and self.doc.production_order:
self.update_only_remaining_qty()
def make_items_dict(self, items_list): def get_pending_raw_materials(self, pro_obj):
"""makes dict of unique items with it's qty""" """
for i in items_list: issue (item quantity) that is pending to issue or desire to transfer,
if self.item_dict.has_key(i[0]): whichever is less
self.item_dict[i[0]][0] = flt(self.item_dict[i[0]][0]) + flt(i[1]) """
item_qty = self.get_bom_raw_materials(1)
issued_item_qty = self.get_issued_qty()
max_qty = flt(pro_obj.doc.qty)
only_pending_fetched = []
for item in item_qty:
pending_to_issue = (max_qty * item_qty[item][0]) - issued_item_qty.get(item, 0)
desire_to_transfer = flt(self.doc.fg_completed_qty) * item_qty[item][0]
if desire_to_transfer <= pending_to_issue:
item_qty[item][0] = desire_to_transfer
else: else:
self.item_dict[i[0]] = [flt(i[1]), cstr(i[2]), cstr(i[3])] item_qty[item][0] = pending_to_issue
if pending_to_issue:
only_pending_fetched.append(item)
def update_only_remaining_qty(self): # delete items with 0 qty
""" Only pending raw material to be issued to shop floor """ for item in item_qty:
already_issued_item = {} if not item_qty[item][0]:
del item_qty[item]
# show some message
if not len(item_qty):
webnotes.msgprint(_("""All items have already been transferred \
for this Production Order."""))
elif only_pending_fetched:
webnotes.msgprint(_("""Only quantities pending to be transferred \
were fetched for the following items:\n""" + "\n".join(only_pending_fetched)))
return item_qty
def get_issued_qty(self):
issued_item_qty = {}
result = sql("""select t1.item_code, sum(t1.qty) result = sql("""select t1.item_code, sum(t1.qty)
from `tabStock Entry Detail` t1, `tabStock Entry` t2 from `tabStock Entry Detail` t1, `tabStock Entry` t2
where t1.parent = t2.name and t2.production_order = %s and t2.docstatus = 1 where t1.parent = t2.name and t2.production_order = %s and t2.docstatus = 1
and t2.purpose = 'Material Transfer' and t2.purpose = 'Material Transfer'
group by t1.item_code""", self.doc.production_order) group by t1.item_code""", self.doc.production_order)
for t in result: for t in result:
already_issued_item[t[0]] = flt(t[1]) issued_item_qty[t[0]] = flt(t[1])
for d in self.item_dict.keys(): return issued_item_qty
self.item_dict[d][0] -= already_issued_item.get(d, 0)
if self.item_dict[d][0] <= 0:
del self.item_dict[d]
def add_to_stock_entry_detail(self, source_wh, target_wh, item_dict, bom_no=None): def add_to_stock_entry_detail(self, source_wh, target_wh, item_dict, bom_no=None):
for d in item_dict: for d in item_dict:
@ -440,7 +477,9 @@ class DocType(TransactionBase):
se_child.qty = flt(item_dict[d][0]) se_child.qty = flt(item_dict[d][0])
se_child.transfer_qty = flt(item_dict[d][0]) se_child.transfer_qty = flt(item_dict[d][0])
se_child.conversion_factor = 1.00 se_child.conversion_factor = 1.00
if bom_no: se_child.bom_no = bom_no
# to be assigned for finished item
se_child.bom_no = bom_no
def add_to_values(self, d, wh, qty, is_cancelled): def add_to_values(self, d, wh, qty, is_cancelled):
self.values.append({ self.values.append({
@ -500,3 +539,10 @@ class DocType(TransactionBase):
'supplier_name' : res and res[0][0] or '', 'supplier_name' : res and res[0][0] or '',
'supplier_address' : addr and addr[0] or ''} 'supplier_address' : addr and addr[0] or ''}
return ret return ret
@webnotes.whitelist()
def get_production_order_details(production_order):
result = webnotes.conn.sql("""select bom_no,
ifnull(qty, 0) - ifnull(produced_qty, 0) as fg_completed_qty
from `tabProduction Order` where name = %s""", production_order, as_dict=1)
return result and result[0] or {}