stock entry cleanup
diff --git a/buying/doctype/purchase_order/purchase_order.py b/buying/doctype/purchase_order/purchase_order.py
index e52499c..7182306 100644
--- a/buying/doctype/purchase_order/purchase_order.py
+++ b/buying/doctype/purchase_order/purchase_order.py
@@ -299,7 +299,11 @@
def add_bom(self, d):
#----- fetching default bom from Bill of Materials instead of Item Master --
- bom_det = sql("select t1.item, t2.item_code, t2.qty_consumed_per_unit, t2.moving_avg_rate, t2.value_as_per_mar, t2.stock_uom, t2.name, t2.parent from `tabBOM` t1, `tabBOM Item` t2 where t2.parent = t1.name and t1.item = '%s' and ifnull(t1.is_default,0) = 1 and t1.docstatus = 1" % d.item_code)
+ bom_det = sql("""select t1.item, t2.item_code, t2.qty_consumed_per_unit,
+ t2.moving_avg_rate, t2.value_as_per_mar, t2.stock_uom, t2.name, t2.parent
+ from `tabBOM` t1, `tabBOM Item` t2
+ where t2.parent = t1.name and t1.item = %s
+ and ifnull(t1.is_default,0) = 1 and t1.docstatus = 1""", (d.item_code,))
if not bom_det:
msgprint("No default BOM exists for item: %s" % d.item_code)
diff --git a/manufacturing/doctype/production_order/production_order.js b/manufacturing/doctype/production_order/production_order.js
index d36c548..dd452b6 100644
--- a/manufacturing/doctype/production_order/production_order.js
+++ b/manufacturing/doctype/production_order/production_order.js
@@ -93,21 +93,19 @@
cur_frm.cscript['Issue Raw Materials'] = function() {
var doc = cur_frm.doc;
- cur_frm.cscript.make_se(doc, process = 'Material Transfer');
+ cur_frm.cscript.make_se(doc, 'Material Transfer');
}
cur_frm.cscript['Update Finished Goods'] = function() {
var doc = cur_frm.doc;
- cur_frm.cscript.make_se(doc, process = 'Backflush');
+ cur_frm.cscript.make_se(doc, 'Manufacture/Repack');
}
-cur_frm.cscript.make_se = function(doc, process) {
+cur_frm.cscript.make_se = function(doc, purpose) {
var se = wn.model.get_new_doc("Stock Entry");
- se.purpose = 'Production Order';
- se.process = process;
+ se.purpose = purpose;
se.production_order = doc.name;
se.company = doc.company;
-
loaddoc('Stock Entry', se.name);
}
diff --git a/patches/december_2012/production_cleanup.py b/patches/december_2012/production_cleanup.py
index 99042dd..46eb9e5 100644
--- a/patches/december_2012/production_cleanup.py
+++ b/patches/december_2012/production_cleanup.py
@@ -3,8 +3,8 @@
def execute():
delete_doctypes()
rename_module()
- rebuilt_exploded_bom()
cleanup_bom()
+ rebuild_exploded_bom()
def delete_doctypes():
from webnotes.model import delete_doc
@@ -36,12 +36,14 @@
# set end of life to null if "0000-00-00"
webnotes.conn.sql("""update `tabItem` set end_of_life=null where end_of_life='0000-00-00'""")
-def rebuilt_exploded_bom():
+def rebuild_exploded_bom():
from webnotes.model.code import get_obj
for bom in webnotes.conn.sql("""select name from `tabBOM` where docstatus < 2"""):
get_obj("BOM", bom[0], with_children=1).on_update()
def cleanup_bom():
- webnotes.conn.sql("""UPDATE `tabBOM` SET is_active = if(is_active in ('Yes', 1), 1, 0),
- with_operations = 1""")
+ webnotes.conn.sql("""UPDATE `tabBOM` SET is_active = 1 where ifnull(is_active, 'No') = 'Yes'""")
+ webnotes.conn.sql("""UPDATE `tabBOM` SET is_active = 0 where ifnull(is_active, 'No') = 'No'""")
+ webnotes.reload_doc("manufacturing", "doctype", "bom")
+ webnotes.conn.sql("""update `tabBOM` set with_operations = 1""")
\ No newline at end of file
diff --git a/patches/december_2012/stock_entry_cleanup.py b/patches/december_2012/stock_entry_cleanup.py
index be93a9c..9710140 100644
--- a/patches/december_2012/stock_entry_cleanup.py
+++ b/patches/december_2012/stock_entry_cleanup.py
@@ -12,35 +12,35 @@
"fieldname": "is_excisable_goods",
"fieldtype": "Select",
"options": "\nYes\nNo",
- "insert_after": "company"
+ "insert_after": "Company"
},
{
"label": "Excisable Goods",
"fieldname": "excisable_goods",
"fieldtype": "Select",
"options": "\nReturnable\nNon-Returnable)",
- "insert_after": "amended_from"
+ "insert_after": "Amended From"
},
{
"label": "Under Rule",
"fieldname": "under_rule",
"fieldtype": "Select",
"options": "\nOrdinary\n57 AC (5) a\n57 F (2) Non-Exc.",
- "insert_after": "remarks"
+ "insert_after": "Remarks"
},
{
"label": "Transporter",
"fieldname": "transporter",
"fieldtype": "Data",
"options": "",
- "insert_after": "project_name"
+ "insert_after": "Project Name"
},
{
"label": "Transfer Date",
"fieldname": "transfer_date",
"fieldtype": "Date",
"options": "",
- "insert_after": "select_print_heading"
+ "insert_after": "Select Print Heading"
},
]
@@ -62,9 +62,13 @@
def deprecate_process():
webnotes.conn.sql("""update `tabStock Entry`
- set `purpose`="Production Order - Material Transfer"
+ set `purpose`="Material Transfer"
where process="Material Transfer" and purpose="Production Order" """)
webnotes.conn.sql("""update `tabStock Entry`
- set `purpose`="Production Order - Update Finished Goods"
- where process="Backflush" and purpose="Production Order" """)
\ No newline at end of file
+ set `purpose`="Manufacture/Repack"
+ where (process="Backflush" and purpose="Production Order") or purpose="Other" """)
+
+ webnotes.conn.sql("""update `tabStock Entry`
+ set `purpose`="Subcontract"
+ where process="Subcontracting" """)
\ No newline at end of file
diff --git a/patches/patch_list.py b/patches/patch_list.py
index 9f73443..77deb00 100644
--- a/patches/patch_list.py
+++ b/patches/patch_list.py
@@ -542,4 +542,8 @@
'patch_module': 'patches.december_2012',
'patch_file': 'clear_web_cache',
},
+ {
+ 'patch_module': 'patches.december_2012',
+ 'patch_file': 'stock_entry_cleanup',
+ },
]
\ No newline at end of file
diff --git a/stock/doctype/stock_entry/stock_entry.js b/stock/doctype/stock_entry/stock_entry.js
index db418f0..a38d113 100644
--- a/stock/doctype/stock_entry/stock_entry.js
+++ b/stock/doctype/stock_entry/stock_entry.js
@@ -19,71 +19,19 @@
cur_frm.cscript.toggle_related_fields(doc);
}
-
-
cur_frm.cscript.toggle_related_fields = function(doc) {
-
-
-
-
-
-
-
- if(doc.purpose.startswith("Production Order") || doc.purpose == "Other") {
-
-
-
-
- }
-
-
- if (doc.purpose == 'Production Order' || doc.purpose == 'Other') {
- unhide_field('get_items');
- hide_field(['from_warehouse', 'to_warehouse','purchase_receipt_no',
- 'delivery_note_no', 'sales_invoice_no','warehouse_html']);
- if (doc.purpose=='Production Order') unhide_field(['production_order', 'process']);
- else {
- doc.production_order = doc.process = '';
- hide_field(['production_order', 'process']);
- }
-
- doc.from_warehouse = '';
- doc.to_warehouse = '';
- refresh_field(['from_warehosue', 'to_warehouse']);
- if (doc.process == 'Backflush' || doc.purpose == 'Other') {
- unhide_field('fg_completed_qty');
- }
- else{
- hide_field('fg_completed_qty');
- doc.fg_completed_qty = 0;
- }
- } else {
- unhide_field(['from_warehouse', 'to_warehouse']);
- hide_field(['production_order', 'process', 'get_items', 'fg_completed_qty',
- 'purchase_receipt_no','delivery_note_no', 'sales_invoice_no']);
- doc.production_order = '';
- doc.process = '';
- doc.fg_completed_qty = 0;
- }
-
-
if(doc.purpose == 'Purchase Return') {
doc.customer = doc.customer_name = doc.customer_address =
- doc.delivery_note_no = doc.sales_invoice_no = '';
- unhide_field(['supplier','supplier_name','supplier_address','purchase_receipt_no']);
- $(cur_frm.fields_dict.contact_section.row.wrapper).toggle(true);
- }
- else if(doc.purpose == 'Sales Return'){
- doc.supplier=doc.supplier_name = doc.supplier_address=doc.purchase_receipt_no='';
- unhide_field(['customer', 'customer_name', 'customer_address',
- 'delivery_note_no', 'sales_invoice_no']);
- $(cur_frm.fields_dict.contact_section.row.wrapper).toggle(true);
- } else{
+ doc.delivery_note_no = doc.sales_invoice_no = null;
+ doc.bom_no = doc.production_order = doc.fg_completed_qty = null;
+ } else if(doc.purpose == 'Sales Return') {
+ doc.supplier=doc.supplier_name = doc.supplier_address = doc.purchase_receipt_no=null;
+ doc.bom_no = doc.production_order = doc.fg_completed_qty = null;
+ } else {
doc.customer = doc.customer_name = doc.customer_address =
doc.delivery_note_no = doc.sales_invoice_no = doc.supplier =
- doc.supplier_name = doc.supplier_address = doc.purchase_receipt_no = '';
+ doc.supplier_name = doc.supplier_address = doc.purchase_receipt_no = null;
}
- refresh_many(lst);
}
cur_frm.cscript.delivery_note_no = function(doc,cdt,cdn){
@@ -95,7 +43,7 @@
}
cur_frm.cscript.customer = function(doc,cdt,cdn){
- if(doc.customer) get_server_fields('get_cust_addr','','',doc,cdt,cdn,1);
+ if(doc.customer) get_server_fields('get_cust_addr','','',doc,cdt,cdn,1);
}
cur_frm.cscript.purchase_receipt_no = function(doc,cdt,cdn){
@@ -103,48 +51,46 @@
}
cur_frm.cscript.supplier = function(doc,cdt,cdn){
- if(doc.supplier) get_server_fields('get_supp_addr','','',doc,cdt,cdn,1);
+ if(doc.supplier) get_server_fields('get_supp_addr','','',doc,cdt,cdn,1);
}
cur_frm.fields_dict['production_order'].get_query = function(doc) {
- return 'SELECT DISTINCT `tabProduction Order`.`name` FROM `tabProduction Order` WHERE `tabProduction Order`.`docstatus` = 1 AND `tabProduction Order`.`qty` > ifnull(`tabProduction Order`.`produced_qty`,0) AND `tabProduction Order`.`name` like "%s" ORDER BY `tabProduction Order`.`name` DESC LIMIT 50';
+ return 'select name from `tabProduction Order` \
+ where docstatus = 1 and qty > ifnull(produced_qty,0) AND %(key)s like "%s%%" \
+ order by name desc limit 50';
}
cur_frm.cscript.purpose = function(doc, cdt, cdn) {
cur_frm.cscript.toggle_related_fields(doc, cdt, cdn);
}
-cur_frm.cscript.process = function(doc, cdt, cdn) {
- cur_frm.cscript.toggle_related_fields(doc, cdt, cdn);
-}
-
// item code - only if quantity present in source warehosue
-//
var fld = cur_frm.fields_dict['mtn_details'].grid.get_field('item_code');
-fld.query_description = "If Source Warehouse is selected, only items present in the warehouse with actual qty > 0 will be selected"
+fld.query_description = "If Source Warehouse is selected, items with existing stock \
+ for that warehouse will be selected";
+
fld.get_query = function(doc, cdt, cdn) {
var d = locals[cdt][cdn];
-
if(d.s_warehouse) {
return 'SELECT tabItem.name, tabItem.description, tabBin.actual_qty '
- +'FROM tabItem, tabBin '
- +'WHERE tabItem.name = tabBin.item_code '
- +'AND ifnull(`tabBin`.`actual_qty`,0) > 0 '
- +'AND tabBin.warehouse="'+ d.s_warehouse +'" '
- +'AND tabItem.docstatus < 2 '
- +'AND (ifnull(`tabItem`.`end_of_life`,"") = "" OR `tabItem`.`end_of_life` > NOW() OR `tabItem`.`end_of_life`="0000-00-00") '
- +'AND tabItem.%(key)s LIKE "%s" '
- +'ORDER BY tabItem.name ASC '
- +'LIMIT 50'
+ + 'FROM tabItem, tabBin '
+ + 'WHERE tabItem.name = tabBin.item_code '
+ + 'AND ifnull(`tabBin`.`actual_qty`,0) > 0 '
+ + 'AND tabBin.warehouse="'+ d.s_warehouse +'" '
+ + 'AND tabItem.docstatus < 2 '
+ + 'AND (ifnull(`tabItem`.`end_of_life`,"") = "" OR `tabItem`.`end_of_life` > NOW() OR `tabItem`.`end_of_life`="0000-00-00") '
+ + 'AND tabItem.%(key)s LIKE "%s" '
+ + 'ORDER BY tabItem.name ASC '
+ + 'LIMIT 50'
} else {
return 'SELECT tabItem.name, tabItem.description '
- +'FROM tabItem '
- +'WHERE tabItem.docstatus < 2 '
- +'AND (ifnull(`tabItem`.`end_of_life`,"") = "" OR `tabItem`.`end_of_life` > NOW() OR `tabItem`.`end_of_life`="0000-00-00") '
- +'AND tabItem.%(key)s LIKE "%s" '
- +'ORDER BY tabItem.name ASC '
- +'LIMIT 50'
+ + 'FROM tabItem '
+ + 'WHERE tabItem.docstatus < 2 '
+ + 'AND (ifnull(`tabItem`.`end_of_life`,"") = "" OR `tabItem`.`end_of_life` > NOW() OR `tabItem`.`end_of_life`="0000-00-00") '
+ + 'AND tabItem.%(key)s LIKE "%s" '
+ + 'ORDER BY tabItem.name ASC '
+ + 'LIMIT 50'
}
}
@@ -190,7 +136,6 @@
'warehouse' : cstr(d.s_warehouse) || cstr(d.t_warehouse),
'transfer_qty' : d.transfer_qty,
'serial_no' : d.serial_no,
- 'fg_item' : d.fg_item,
'bom_no' : d.bom_no
};
get_server_fields('get_item_details',JSON.stringify(args),'mtn_details',doc,cdt,cdn,1);
@@ -203,7 +148,6 @@
'warehouse' : cstr(d.s_warehouse) || cstr(d.t_warehouse),
'transfer_qty' : d.transfer_qty,
'serial_no' : d.serial_no,
- 'fg_item' : d.fg_item,
'bom_no' : d.bom_no
}
get_server_fields('get_warehouse_details', JSON.stringify(args),
@@ -212,16 +156,10 @@
cur_frm.cscript.t_warehouse = cur_frm.cscript.s_warehouse;
-cur_frm.cscript.transfer_qty = function(doc,cdt,cdn) {
- var d = locals[cdt][cdn];
- if (doc.from_warehouse && (flt(d.transfer_qty) > flt(d.actual_qty))) {
- alert("Transfer Quantity is more than Available Qty");
- }
-}
-
cur_frm.cscript.qty = function(doc, cdt, cdn) {
var d = locals[cdt][cdn];
- set_multiple('Stock Entry Detail', d.name, {'transfer_qty': flt(d.qty) * flt(d.conversion_factor)}, 'mtn_details');
+ set_multiple('Stock Entry Detail', d.name,
+ {'transfer_qty': flt(d.qty) * flt(d.conversion_factor)}, 'mtn_details');
refresh_field('mtn_details');
}
diff --git a/stock/doctype/stock_entry/stock_entry.py b/stock/doctype/stock_entry/stock_entry.py
index cf64643..e64e1b3 100644
--- a/stock/doctype/stock_entry/stock_entry.py
+++ b/stock/doctype/stock_entry/stock_entry.py
@@ -47,6 +47,18 @@
self.validate_incoming_rate()
self.validate_bom()
self.validate_finished_goods()
+
+ def on_submit(self):
+ self.update_serial_no(1)
+ self.update_stock_ledger(0)
+ # update Production Order
+ self.update_production_order(1)
+
+ def on_cancel(self):
+ self.update_serial_no(0)
+ self.update_stock_ledger(1)
+ # update Production Order
+ self.update_production_order(0)
def validate_serial_nos(self):
sl_obj = get_obj("Stock Ledger")
@@ -56,10 +68,8 @@
def validate_warehouse(self, pro_obj):
"""perform various (sometimes conditional) validations on warehouse"""
- source_mandatory = ["Material Issue", "Material Transfer",
- "Production Order - Material Transfer", "Purchase Return"]
- target_mandatory = ["Material Receipt", "Material Transfer",
- "Production Order - Material Transfer", "Sales Return"]
+ source_mandatory = ["Material Issue", "Material Transfer", "Purchase Return"]
+ target_mandatory = ["Material Receipt", "Material Transfer", "Sales Return"]
fg_qty = 0
for d in getlist(self.doclist, 'mtn_details'):
@@ -89,11 +99,15 @@
if self.doc.purpose not in source_mandatory:
d.s_warehouse = None
- if self.doc.purpose == "Production Order - Update Finished Goods":
- if d.item_code == pro_obj.doc.item:
+ if self.doc.purpose == "Manufacture/Repack":
+ if d.bom_no:
d.s_warehouse = None
-
- if cstr(d.t_warehouse) != pro_obj.doc.fg_warehouse:
+
+ if not d.t_warehouse:
+ msgprint(_("Row # ") + "%s: " % cint(d.idx)
+ + _("Target Warehouse") + _(" is mandatory"), raise_exception=1)
+
+ elif pro_obj and cstr(d.t_warehouse) != pro_obj.doc.fg_warehouse:
msgprint(_("Row # ") + "%s: " % cint(d.idx)
+ _("Target Warehouse") + _(" should be same as that in ")
+ _("Production Order"), raise_exception=1)
@@ -104,18 +118,14 @@
msgprint(_("Row # ") + "%s: " % cint(d.idx)
+ _("Source Warehouse") + _(" is mandatory"), raise_exception=1)
- # if self.doc.fg_completed_qty and flt(self.doc.fg_completed_qty) != flt(fg_qty):
- # msgprint("The Total of FG Qty %s in Stock Entry Detail do not match with FG Completed Qty %s" % (flt(fg_qty), flt(self.doc.fg_completed_qty)))
- # raise Exception
-
def validate_production_order(self, pro_obj=None):
if not pro_obj:
- pro_obj = get_obj('Production Order', self.doc.production_order)
+ if self.doc.production_order:
+ pro_obj = get_obj('Production Order', self.doc.production_order)
+ else:
+ return
- if self.doc.purpose == "Production Order - Material Transfer":
- self.doc.fg_completed_qty = 0
-
- elif self.doc.purpose == "Production Order - Update Finished Goods":
+ if self.doc.purpose == "Manufacture/Repack":
if not flt(self.doc.fg_completed_qty):
msgprint(_("Manufacturing Quantity") + _(" is mandatory"), raise_exception=1)
@@ -128,7 +138,7 @@
+ _("Hence, maximum allowed Manufacturing Quantity")
+ " = %s." % flt(pro_obj.doc.qty) - flt(pro_obj.doc.produced_qty),
raise_exception=1)
- else:
+ elif self.doc.purpose != "Material Transfer":
self.doc.production_order = None
def get_stock_and_rate(self):
@@ -184,16 +194,88 @@
+ _(" or the BOM is cancelled or inactive"), raise_exception=1)
def validate_finished_goods(self):
+ """validation: finished good quantity should be same as manufacturing quantity"""
for d in getlist(self.doclist, 'mtn_details'):
if d.bom_no and flt(d.transfer_qty) != flt(self.doc.fg_completed_qty):
-
-
+ msgprint(_("Row #") + " %s: " % d.idx
+ + _("Quantity as per Stock UOM should be equal to Manufacturing Quantity"),
+ raise_exception=1)
+
+ def update_serial_no(self, is_submit):
+ """Create / Update Serial No"""
+ sl_obj = get_obj('Stock Ledger')
+ if is_submit:
+ sl_obj.validate_serial_no_warehouse(self, 'mtn_details')
+
+ for d in getlist(self.doclist, 'mtn_details'):
+ if d.serial_no:
+ serial_nos = sl_obj.get_sr_no_list(d.serial_no)
+ for x in serial_nos:
+ serial_no = x.strip()
+ if d.s_warehouse:
+ sl_obj.update_serial_delivery_details(self, d, serial_no, is_submit)
+ if d.t_warehouse:
+ sl_obj.update_serial_purchase_details(self, d, serial_no, is_submit,
+ self.doc.purpose)
+
+ if self.doc.purpose == 'Purchase Return':
+ #delete_doc("Serial No", serial_no)
+ serial_doc = Document("Serial No", serial_no)
+ serial_doc.status = is_submit and 'Purchase Returned' or 'In Store'
+ serial_doc.docstatus = is_submit and 2 or 0
+ serial_doc.save()
+
+ def update_stock_ledger(self, is_cancelled=0):
+ self.values = []
+ for d in getlist(self.doclist, 'mtn_details'):
+ if cstr(d.s_warehouse):
+ self.add_to_values(d, cstr(d.s_warehouse), -flt(d.transfer_qty), is_cancelled)
+ if cstr(d.t_warehouse):
+ self.add_to_values(d, cstr(d.t_warehouse), flt(d.transfer_qty), is_cancelled)
+ get_obj('Stock Ledger', 'Stock Ledger').update_stock(self.values,
+ self.doc.amended_from and 'Yes' or 'No')
+ def update_production_order(self, is_submit):
+ if self.doc.production_order:
+ # first perform some validations
+ # (they are here coz this fn is also called during on_cancel)
+ pro_obj = get_obj("Production Order", self.doc.production_order)
+ if flt(pro_obj.doc.docstatus) != 1:
+ msgprint("""You cannot do any transaction against
+ Production Order : %s, as it's not submitted"""
+ % (pro_obj.doc.name), raise_exception=1)
+
+ if pro_obj.doc.status == 'Stopped':
+ msgprint("""You cannot do any transaction against Production Order : %s,
+ as it's status is 'Stopped'"""% (pro_obj.doc.name), raise_exception=1)
+
+ if getdate(pro_obj.doc.posting_date) > getdate(self.doc.posting_date):
+ msgprint("""Posting Date of Stock Entry cannot be before Posting Date of
+ Production Order: %s"""% cstr(self.doc.production_order), raise_exception=1)
+
+ # update bin
+ if self.doc.purpose == "Manufacture/Repack":
+ pro_obj.doc.produced_qty = flt(pro_obj.doc.produced_qty) + \
+ (is_submit and 1 or -1 ) * flt(self.doc.fg_completed_qty)
+ args = {
+ "item_code": pro_obj.doc.production_item,
+ "posting_date": self.doc.posting_date,
+ "planned_qty": (is_submit and -1 or 1 ) * flt(self.doc.fg_completed_qty)
+ }
+ get_obj('Warehouse', pro_obj.doc.fg_warehouse).update_bin(args)
+
+ # update production order status
+ pro_obj.doc.status = (flt(pro_obj.doc.qty)==flt(pro_obj.doc.produced_qty)) \
+ and 'Completed' or 'In Process'
+ pro_obj.doc.save()
+
def get_item_details(self, arg):
import json
arg, actual_qty, in_rate = json.loads(arg), 0, 0
- item = sql("select stock_uom, description, item_name from `tabItem` where name = %s and (ifnull(end_of_life,'')='' or end_of_life ='0000-00-00' or end_of_life > now())", (arg.get('item_code')), as_dict = 1)
+ item = sql("""select stock_uom, description, item_name from `tabItem`
+ where name = %s and (ifnull(end_of_life,'')='' or end_of_life ='0000-00-00'
+ or end_of_life > now())""", (arg.get('item_code')), as_dict = 1)
if not item:
msgprint("Item is not active", raise_exception=1)
@@ -213,12 +295,13 @@
ret.update(stock_and_rate)
return ret
-
def get_uom_details(self, arg = ''):
arg, ret = eval(arg), {}
- uom = sql("select conversion_factor from `tabUOM Conversion Detail` where parent = %s and uom = %s", (arg['item_code'],arg['uom']), as_dict = 1)
+ uom = sql("""select conversion_factor from `tabUOM Conversion Detail`
+ where parent = %s and uom = %s""", (arg['item_code'], arg['uom']), as_dict = 1)
if not uom:
- msgprint("There is no Conversion Factor for UOM '%s' in Item '%s'" % (arg['uom'], arg['item_code']))
+ msgprint("There is no Conversion Factor for UOM '%s' in Item '%s'" % (arg['uom'],
+ arg['item_code']))
ret = {'uom' : ''}
else:
ret = {
@@ -233,13 +316,87 @@
ret = {
"actual_qty" : self.get_as_on_stock(arg.get('item_code'), arg.get('warehouse'),
self.doc.posting_date, self.doc.posting_time),
- "incoming_rate" : self.get_incoming_rate(arg.get('item_code'),
+ "incoming_rate" : self.get_incoming_rate(arg.get('item_code'),
arg.get('warehouse'), self.doc.posting_date, self.doc.posting_time,
- arg.get('transfer_qty'), arg.get('serial_no'), arg.get('fg_item'),
- arg.get('bom_no')) or 0
+ arg.get('transfer_qty'), arg.get('serial_no'), arg.get('bom_no')) or 0
}
return ret
+
+ def get_items(self):
+ self.doclist = self.doc.clear_table(self.doclist, 'mtn_details', 1)
+
+ if self.doc.production_order:
+ # common validations
+ pro_obj = get_obj('Production Order', self.doc.production_order)
+ if pro_obj:
+ self.validate_production_order(pro_obj)
+
+ 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:
+ # invalid production order
+ self.doc.production_order = None
+
+ if self.doc.bom_no:
+ if self.doc.purpose in ["Material Issue", "Material Transfer", "Manufacture/Repack",
+ "Subcontract"]:
+ self.get_raw_materials()
+
+ # add raw materials to Stock Entry Detail table
+ self.add_to_stock_entry_detail(self.doc.from_warehouse, self.doc.to_warehouse,
+ self.item_dict)
+
+ # add finished good item to Stock Entry Detail table
+ if self.doc.production_order:
+ self.add_to_stock_entry_detail(None, pro_obj.doc.fg_warehouse, {
+ cstr(pro_obj.doc.production_item):
+ [self.doc.fg_completed_qty, pro_obj.doc.description, pro_obj.doc.stock_uom]
+ })
+ elif self.doc.purpose in ["Material Receipt", "Manufacture/Repack",
+ "Subcontract"]:
+ item = webnotes.conn.sql("""select item, description, uom from `tabBOM`
+ where name=%s""", (self.doc.bom_no,), as_dict=1)
+ self.add_to_stock_entry_detail(None, None, {
+ item[0]["item"] :
+ [self.doc.fg_completed_qty, item[0]["description"], item[0]["uom"]]
+ })
+
+ self.get_stock_and_rate()
+
+ def get_raw_materials(self):
+ """
+ get all items from flat bom except
+ child items of sub-contracted and sub assembly items
+ and sub assembly items itself.
+ """
+ if self.doc.use_multi_level_bom:
+ # get all raw materials with sub assembly childs
+ fl_bom_sa_child_item = sql("""select
+ item_code,ifnull(sum(qty_consumed_per_unit),0)*%s as qty,description,stock_uom
+ from ( select distinct fb.name, fb.description, fb.item_code,
+ fb.qty_consumed_per_unit, fb.stock_uom
+ from `tabBOM Explosion Item` fb,`tabItem` it
+ where it.name = fb.item_code and ifnull(it.is_pro_applicable, 'No') = 'No'
+ and ifnull(it.is_sub_contracted_item, 'No') = 'No' and fb.docstatus<2
+ and fb.parent=%s
+ ) a
+ group by item_code, stock_uom""" , (self.doc.fg_completed_qty, self.doc.bom_no))
+ self.make_items_dict(fl_bom_sa_child_item)
+ else:
+ # Get all raw materials considering multi level BOM,
+ # if multi level bom consider childs of Sub-Assembly items
+ fl_bom_sa_items = sql("""select item_code, ifnull(sum(qty_consumed_per_unit), 0) * '%s',
+ description, stock_uom from `tabBOM Item`
+ where parent = '%s' and docstatus < 2
+ group by item_code""" % (self.doc.fg_completed_qty, self.doc.bom_no))
+ self.make_items_dict(fl_bom_sa_items)
+
+ # Update only qty remaining to be issued for production
+ if self.doc.purpose == 'Material Transfer' and self.doc.production_order:
+ self.update_only_remaining_qty()
def make_items_dict(self, items_list):
"""makes dict of unique items with it's qty"""
@@ -248,17 +405,15 @@
self.item_dict[i[0]][0] = flt(self.item_dict[i[0]][0]) + flt(i[1])
else:
self.item_dict[i[0]] = [flt(i[1]), cstr(i[2]), cstr(i[3])]
-
-
def update_only_remaining_qty(self):
""" Only pending raw material to be issued to shop floor """
already_issued_item = {}
result = sql("""select t1.item_code, sum(t1.qty)
from `tabStock Entry Detail` t1, `tabStock Entry` t2
- where t1.parent = t2.name and t2.production_order = %s
- and t2.purpose = 'Production Order - Material Transfer'
- and t2.docstatus = 1 group by t1.item_code""", self.doc.production_order)
+ where t1.parent = t2.name and t2.production_order = %s and t2.docstatus = 1
+ and t2.purpose = 'Material Transfer'
+ group by t1.item_code""", self.doc.production_order)
for t in result:
already_issued_item[t[0]] = flt(t[1])
@@ -267,231 +422,38 @@
if self.item_dict[d][0] <= 0:
del self.item_dict[d]
-
-
- def get_raw_materials(self, bom_no, fg_qty, use_multi_level_bom):
- """
- get all items from flat bom except
- child items of sub-contracted and sub assembly items
- and sub assembly items itself.
- """
- if use_multi_level_bom:
- # get all raw materials with sub assembly childs
- fl_bom_sa_child_item = sql("""
- select
- item_code,ifnull(sum(qty_consumed_per_unit),0)*%s as qty,description,stock_uom
- from
- (
- select distinct fb.name, fb.description, fb.item_code, fb.qty_consumed_per_unit, fb.stock_uom
- from `tabBOM Explosion Item` fb,`tabItem` it
- where it.name = fb.item_code and ifnull(it.is_pro_applicable, 'No') = 'No'
- and ifnull(it.is_sub_contracted_item, 'No') = 'No' and fb.docstatus<2 and fb.parent=%s
- ) a
- group by item_code,stock_uom
- """ , (fg_qty, bom_no))
- self.make_items_dict(fl_bom_sa_child_item)
- else:
- # Get all raw materials considering multi level BOM,
- # if multi level bom consider childs of Sub-Assembly items
- fl_bom_sa_items = sql("""
- select item_code, ifnull(sum(qty_consumed_per_unit), 0) * '%s', description, stock_uom
- from `tabBOM Item`
- where parent = '%s' and docstatus < 2
- group by item_code
- """ % (fg_qty, bom_no))
-
- self.make_items_dict(fl_bom_sa_items)
-
- # Update only qty remaining to be issued for production
- if self.doc.purpose == 'Production Order - Material Transfer':
- self.update_only_remaining_qty()
-
-
-
def add_to_stock_entry_detail(self, source_wh, target_wh, item_dict, fg_item = 0, bom_no = ''):
for d in item_dict:
se_child = addchild(self.doc, 'mtn_details', 'Stock Entry Detail', 0, self.doclist)
se_child.s_warehouse = source_wh
se_child.t_warehouse = target_wh
- se_child.fg_item = fg_item
se_child.item_code = cstr(d)
se_child.description = item_dict[d][1]
se_child.uom = item_dict[d][2]
se_child.stock_uom = item_dict[d][2]
- se_child.reqd_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.conversion_factor = 1.00
if fg_item: se_child.bom_no = bom_no
- def get_items(self):
- bom_no = self.doc.bom_no
- fg_qty = self.doc.fg_completed_qty
-
- if self.doc.purpose.startswith('Production Order'):
- if not self.doc.production_order:
- webnotes.msgprint(_("Please specify Production Order"), raise_exception=1)
-
- # common validations
- pro_obj = get_obj('Production Order', self.doc.production_order)
- if pro_obj:
- self.validate_production_order(pro_obj)
-
- bom_no = pro_obj.doc.bom_no
- fg_qty = (self.doc.purpose == 'Production Order - Update Finished Goods') \
- and flt(self.doc.fg_completed_qty) or flt(pro_obj.doc.qty)
-
- self.get_raw_materials(bom_no, fg_qty, self.doc.use_multi_level_bom)
- self.doclist = self.doc.clear_table(self.doclist, 'mtn_details', 1)
-
- # add raw materials to Stock Entry Detail table
- self.add_to_stock_entry_detail(self.doc.from_warehouse, self.doc.to_warehouse,
- self.item_dict)
-
- # add finished good item to Stock Entry Detail table
- if self.doc.production_order:
- self.add_to_stock_entry_detail(None, pro_obj.doc.fg_warehouse, {
- cstr(pro_obj.doc.production_item):
- [self.doc.fg_completed_qty, pro_obj.doc.description, pro_obj.doc.stock_uom]
- })
- elif self.doc.bom_no:
- item = webnotes.conn.sql("""select item, description, uom from `tabBOM`
- where name=%s""", (self.doc.bom_no,), as_dict=1)
- self.add_to_stock_entry_detail(None, None, {
- item[0]["item"] :
- [self.doc.fg_completed_qty, item[0]["description"], item[0]["uom"]]
- })
-
-
-
-
- fg_item_dict = {}
- if self.doc.purpose == 'Production Order - Update Finished Goods':
- sw = ''
- tw = cstr(pro_obj.doc.fg_warehouse)
- fg_item_dict = {
- cstr(pro_obj.doc.production_item) : [self.doc.fg_completed_qty,
- pro_obj.doc.description, pro_obj.doc.stock_uom]
- }
- elif self.doc.purpose == 'Other' and self.doc.bom_no:
- sw, tw = '', ''
- item = sql("select item, description, uom from `tabBOM` where name = %s", self.doc.bom_no, as_dict=1)
- fg_item_dict = {
- item[0]['item'] : [self.doc.fg_completed_qty,
- item[0]['description'], item[0]['uom']]
- }
-
- if fg_item_dict:
- self.add_to_stock_entry_detail(sw, tw, fg_item_dict, fg_item = 1, bom_no = bom_no)
-
- self.get_stock_and_rate()
-
- def validate_qty_as_per_stock_uom(self):
- for d in getlist(self.doclist, 'mtn_details'):
- if flt(d.transfer_qty) <= 0:
- msgprint("Row No #%s: Qty as per Stock UOM can not be less than \
- or equal to zero" % cint(d.idx), raise_exception=1)
-
-
def add_to_values(self, d, wh, qty, is_cancelled):
self.values.append({
- 'item_code' : d.item_code,
- 'warehouse' : wh,
- 'posting_date' : self.doc.posting_date,
- 'posting_time' : self.doc.posting_time,
- 'voucher_type' : 'Stock Entry',
- 'voucher_no' : self.doc.name,
- 'voucher_detail_no' : d.name,
- 'actual_qty' : qty,
- 'incoming_rate' : flt(d.incoming_rate) or 0,
- 'stock_uom' : d.stock_uom,
- 'company' : self.doc.company,
- 'is_cancelled' : (is_cancelled ==1) and 'Yes' or 'No',
- 'batch_no' : d.batch_no,
- 'serial_no' : d.serial_no
+ 'item_code': d.item_code,
+ 'warehouse': wh,
+ 'posting_date': self.doc.posting_date,
+ 'posting_time': self.doc.posting_time,
+ 'voucher_type': 'Stock Entry',
+ 'voucher_no': self.doc.name,
+ 'voucher_detail_no': d.name,
+ 'actual_qty': qty,
+ 'incoming_rate': flt(d.incoming_rate) or 0,
+ 'stock_uom': d.stock_uom,
+ 'company': self.doc.company,
+ 'is_cancelled': (is_cancelled ==1) and 'Yes' or 'No',
+ 'batch_no': d.batch_no,
+ 'serial_no': d.serial_no
})
-
- def update_stock_ledger(self, is_cancelled=0):
- self.values = []
- for d in getlist(self.doclist, 'mtn_details'):
- if cstr(d.s_warehouse):
- self.add_to_values(d, cstr(d.s_warehouse), -flt(d.transfer_qty), is_cancelled)
- if cstr(d.t_warehouse):
- self.add_to_values(d, cstr(d.t_warehouse), flt(d.transfer_qty), is_cancelled)
- get_obj('Stock Ledger', 'Stock Ledger').update_stock(self.values, self.doc.amended_from and 'Yes' or 'No')
-
- def update_production_order(self, is_submit):
- if self.doc.production_order:
- pro_obj = get_obj("Production Order", self.doc.production_order)
- if flt(pro_obj.doc.docstatus) != 1:
- msgprint("""You cannot do any transaction against
- Production Order : %s, as it's not submitted"""
- % (pro_obj.doc.name), raise_exception=1)
-
- if pro_obj.doc.status == 'Stopped':
- msgprint("""You cannot do any transaction against Production Order : %s,
- as it's status is 'Stopped'"""% (pro_obj.doc.name), raise_exception=1)
-
- if getdate(pro_obj.doc.posting_date) > getdate(self.doc.posting_date):
- msgprint("""Posting Date of Stock Entry cannot be before Posting Date of
- Production Order: %s"""% cstr(self.doc.production_order), raise_exception=1)
-
- if self.doc.purpose == "Production Order - Update Finished Goods":
- pro_obj.doc.produced_qty = flt(pro_obj.doc.produced_qty) + \
- (is_submit and 1 or -1 ) * flt(self.doc.fg_completed_qty)
- args = {
- "item_code": pro_obj.doc.production_item,
- "posting_date": self.doc.posting_date,
- "planned_qty": (is_submit and -1 or 1 ) * flt(self.doc.fg_completed_qty)
- }
- get_obj('Warehouse', pro_obj.doc.fg_warehouse).update_bin(args)
-
- pro_obj.doc.status = (flt(pro_obj.doc.qty)==flt(pro_obj.doc.produced_qty)) \
- and 'Completed' or 'In Process'
- pro_obj.doc.save()
-
-
- # Create / Update Serial No
- # ----------------------------------
- def update_serial_no(self, is_submit):
- sl_obj = get_obj('Stock Ledger')
- if is_submit:
- sl_obj.validate_serial_no_warehouse(self, 'mtn_details')
-
- for d in getlist(self.doclist, 'mtn_details'):
- if d.serial_no:
- serial_nos = sl_obj.get_sr_no_list(d.serial_no)
- for x in serial_nos:
- serial_no = x.strip()
- if d.s_warehouse:
- sl_obj.update_serial_delivery_details(self, d, serial_no, is_submit)
- if d.t_warehouse:
- sl_obj.update_serial_purchase_details(self, d, serial_no, is_submit, self.doc.purpose)
-
- if self.doc.purpose == 'Purchase Return':
- #delete_doc("Serial No", serial_no)
- serial_doc = Document("Serial No", serial_no)
- serial_doc.status = is_submit and 'Purchase Returned' or 'In Store'
- serial_doc.docstatus = is_submit and 2 or 0
- serial_doc.save()
-
-
- def on_submit(self):
- self.validate_qty_as_per_stock_uom()
- self.update_serial_no(1)
- self.update_stock_ledger(0)
- # update Production Order
- self.update_production_order(1)
-
-
- def on_cancel(self):
- self.update_serial_no(0)
- self.update_stock_ledger(1)
- # update Production Order
- self.update_production_order(0)
-
-
def get_cust_values(self):
"""fetches customer details"""
if self.doc.delivery_note_no:
@@ -525,7 +487,8 @@
return result and result[0] or {}
def get_supp_addr(self):
- res = sql("select supplier_name,address from `tabSupplier` where name = '%s'"%self.doc.supplier)
+ res = sql("""select supplier_name from `tabSupplier`
+ where name=%s""", self.doc.supplier)
addr = self.get_address_text(supplier = self.doc.supplier)
ret = {
'supplier_name' : res and res[0][0] or '',
diff --git a/stock/doctype/stock_entry/stock_entry.txt b/stock/doctype/stock_entry/stock_entry.txt
index 4426f10..89e2b69 100644
--- a/stock/doctype/stock_entry/stock_entry.txt
+++ b/stock/doctype/stock_entry/stock_entry.txt
@@ -2,9 +2,9 @@
{
"owner": "Administrator",
"docstatus": 0,
- "creation": "2012-12-17 11:42:09",
+ "creation": "2012-12-18 13:47:41",
"modified_by": "Administrator",
- "modified": "2012-12-17 18:19:44"
+ "modified": "2012-12-18 17:17:16"
},
{
"is_submittable": 1,
@@ -74,8 +74,8 @@
"report_hide": 0
},
{
- "permlevel": 0,
"print_hide": 0,
+ "permlevel": 0,
"no_copy": 0,
"oldfieldtype": "Select",
"allow_on_submit": 0,
@@ -88,7 +88,7 @@
"search_index": 0,
"reqd": 1,
"hidden": 0,
- "options": "Material Issue\nMaterial Receipt\nMaterial Transfer\nSales Return\nPurchase Return\nSubcontracting\nProduction Order - Material Transfer\nProduction Order - Update Finished Goods\nOther",
+ "options": "Material Issue\nMaterial Receipt\nMaterial Transfer\nManufacture/Repack\nSubcontract\nSales Return\nPurchase Return",
"report_hide": 0,
"in_filter": 1
},
@@ -231,7 +231,7 @@
},
{
"print_hide": 1,
- "depends_on": "eval:doc.purpose.startsWith(\"Production Order\")",
+ "depends_on": "eval:inList([\"Material Transfer\", \"Manufacture/Repack\"], doc.purpose)",
"no_copy": 0,
"search_index": 1,
"allow_on_submit": 0,
@@ -249,7 +249,7 @@
"in_filter": 1
},
{
- "depends_on": "eval:!doc.purpose.startsWith(\"Production Order\")",
+ "depends_on": "eval:!inList([\"Sales Return\", \"Purchase Return\"], doc.purpose)",
"doctype": "DocField",
"label": "BOM No",
"options": "BOM",
@@ -259,15 +259,16 @@
},
{
"print_hide": 1,
+ "depends_on": "eval:!inList([\"Sales Return\", \"Purchase Return\"], doc.purpose)",
"no_copy": 0,
- "oldfieldtype": "Currency",
+ "search_index": 0,
"allow_on_submit": 0,
"doctype": "DocField",
"label": "Manufacturing Quantity",
"oldfieldname": "fg_completed_qty",
"fieldname": "fg_completed_qty",
"fieldtype": "Currency",
- "search_index": 0,
+ "oldfieldtype": "Currency",
"reqd": 0,
"hidden": 0,
"permlevel": 0,
@@ -275,37 +276,6 @@
"in_filter": 0
},
{
- "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.",
- "doctype": "DocField",
- "label": "Use Multi-Level BOM",
- "fieldname": "use_multi_level_bom",
- "fieldtype": "Check",
- "permlevel": 0
- },
- {
- "print_hide": 1,
- "no_copy": 0,
- "oldfieldtype": "Button",
- "allow_on_submit": 0,
- "doctype": "DocField",
- "label": "Get Items",
- "permlevel": 0,
- "fieldname": "get_items",
- "fieldtype": "Button",
- "search_index": 0,
- "reqd": 0,
- "hidden": 0,
- "options": "get_items",
- "report_hide": 0,
- "in_filter": 0
- },
- {
- "doctype": "DocField",
- "fieldname": "cb1",
- "fieldtype": "Column Break",
- "permlevel": 0
- },
- {
"print_hide": 1,
"depends_on": "eval:doc.purpose==\"Sales Return\"",
"no_copy": 0,
@@ -326,17 +296,6 @@
},
{
"print_hide": 1,
- "depends_on": "eval:doc.purpose==\"Sales Return\"",
- "doctype": "DocField",
- "label": "Sales Invoice No",
- "options": "Sales Invoice",
- "fieldname": "sales_invoice_no",
- "fieldtype": "Link",
- "hidden": 1,
- "permlevel": 0
- },
- {
- "print_hide": 1,
"depends_on": "eval:doc.purpose==\"Purchase Return\"",
"no_copy": 0,
"search_index": 1,
@@ -355,6 +314,51 @@
"in_filter": 0
},
{
+ "doctype": "DocField",
+ "fieldname": "cb1",
+ "fieldtype": "Column Break",
+ "permlevel": 0
+ },
+ {
+ "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",
+ "depends_on": "eval:!inList([\"Sales Return\", \"Purchase Return\"], doc.purpose)",
+ "doctype": "DocField",
+ "label": "Use Multi-Level BOM",
+ "fieldname": "use_multi_level_bom",
+ "fieldtype": "Check",
+ "permlevel": 0
+ },
+ {
+ "print_hide": 1,
+ "depends_on": "eval:!inList([\"Sales Return\", \"Purchase Return\"], doc.purpose)",
+ "no_copy": 0,
+ "search_index": 0,
+ "allow_on_submit": 0,
+ "doctype": "DocField",
+ "label": "Get Items",
+ "permlevel": 0,
+ "fieldname": "get_items",
+ "fieldtype": "Button",
+ "oldfieldtype": "Button",
+ "reqd": 0,
+ "hidden": 0,
+ "options": "get_items",
+ "report_hide": 0,
+ "in_filter": 0
+ },
+ {
+ "print_hide": 1,
+ "depends_on": "eval:doc.purpose==\"Sales Return\"",
+ "doctype": "DocField",
+ "label": "Sales Invoice No",
+ "options": "Sales Invoice",
+ "fieldname": "sales_invoice_no",
+ "fieldtype": "Link",
+ "hidden": 1,
+ "permlevel": 0
+ },
+ {
"depends_on": "eval:(doc.purpose==\"Sales Return\" || doc.purpose==\"Purchase Return\")",
"doctype": "DocField",
"label": "Contact Info",
diff --git a/stock/doctype/stock_entry_detail/stock_entry_detail.txt b/stock/doctype/stock_entry_detail/stock_entry_detail.txt
index 9e0e5df..6926c9a 100644
--- a/stock/doctype/stock_entry_detail/stock_entry_detail.txt
+++ b/stock/doctype/stock_entry_detail/stock_entry_detail.txt
@@ -2,9 +2,9 @@
{
"owner": "Administrator",
"docstatus": 0,
- "creation": "2012-07-03 13:29:47",
+ "creation": "2012-12-18 13:47:41",
"modified_by": "Administrator",
- "modified": "2012-12-17 16:12:42"
+ "modified": "2012-12-18 17:08:52"
},
{
"istable": 1,
@@ -134,17 +134,6 @@
},
{
"print_hide": 1,
- "oldfieldtype": "Currency",
- "doctype": "DocField",
- "label": "Reqd Qty",
- "oldfieldname": "reqd_qty",
- "fieldname": "reqd_qty",
- "fieldtype": "Currency",
- "permlevel": 3,
- "in_filter": 0
- },
- {
- "print_hide": 1,
"no_copy": 1,
"oldfieldtype": "Read Only",
"doctype": "DocField",
@@ -191,23 +180,15 @@
"in_filter": 0
},
{
+ "print_hide": 1,
"description": "BOM No. for a Finished Good Item",
+ "no_copy": 0,
"doctype": "DocField",
"label": "BOM No",
"options": "BOM",
"fieldname": "bom_no",
"fieldtype": "Link",
+ "hidden": 1,
"permlevel": 0
- },
- {
- "print_hide": 1,
- "oldfieldtype": "Check",
- "doctype": "DocField",
- "label": "FG Item",
- "oldfieldname": "fg_item",
- "fieldname": "fg_item",
- "fieldtype": "Check",
- "permlevel": 0,
- "in_filter": 1
}
]
\ No newline at end of file