From cfc6bb1d6ce075f9420236a07610e2ecfd2c74ee Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 11 Jul 2012 13:14:52 +0530 Subject: [PATCH] consider warehouse, serial no and batch no from packing list for sales bom item and populate packing list only for sales bom items --- .../doctype/sales_common/sales_common.js | 24 +++++ .../doctype/sales_common/sales_common.py | 25 ++--- .../doctype/delivery_note/delivery_note.py | 69 ++++-------- .../doctype/delivery_note/delivery_note.txt | 101 ++++++++++++++---- .../delivery_note_packing_item.txt | 6 +- .../doctype/stock_ledger/stock_ledger.py | 51 ++------- 6 files changed, 144 insertions(+), 132 deletions(-) diff --git a/erpnext/selling/doctype/sales_common/sales_common.js b/erpnext/selling/doctype/sales_common/sales_common.js index c852230ad4..bf80d8f3ee 100644 --- a/erpnext/selling/doctype/sales_common/sales_common.js +++ b/erpnext/selling/doctype/sales_common/sales_common.js @@ -127,10 +127,34 @@ cur_frm.cscript.dynamic_label = function(doc, cdt, cdn, base_curr, callback) { cur_frm.cscript.base_currency = base_curr; set_dynamic_label_par(doc, cdt, cdn, base_curr); set_dynamic_label_child(doc, cdt, cdn, base_curr); + set_sales_bom_help(doc); if (callback) callback(doc, cdt, cdn); } +// Help for Sales BOM items +var set_sales_bom_help = function(doc) { + if (getchildren('Delivery Note Packing Item', doc.name, 'packing_details').length) { + $(cur_frm.fields_dict.packing_list.row.wrapper).toggle(true); + + if (inList(['Delivery Note', 'Sales Invoice'], doc.doctype)) { + help_msg = "
\ + For 'Sales BOM' items, warehouse, serial no and batch no \ + will be considered from the 'Packing List' table. \ + If warehouse and batch no are same for all packing items for any 'Sales BOM' item, \ + those values can be entered in the main item table, values will be copied to 'Packing List' table. \ +
"; + get_field(doc.doctype, 'sales_bom_help', doc.name).options = help_msg; + } + } else { + $(cur_frm.fields_dict.packing_list.row.wrapper).toggle(false); + if (inList(['Delivery Note', 'Sales Invoice'], doc.doctype)) { + get_field(doc.doctype, 'sales_bom_help', doc.name).options = ''; + } + } + refresh_field('sales_bom_help'); +} + // hide / unhide price list currency based on availability of price list in customer's currency //--------------------------------------------------------------------------------------------------- diff --git a/erpnext/selling/doctype/sales_common/sales_common.py b/erpnext/selling/doctype/sales_common/sales_common.py index 74963fbc07..a658209596 100644 --- a/erpnext/selling/doctype/sales_common/sales_common.py +++ b/erpnext/selling/doctype/sales_common/sales_common.py @@ -385,7 +385,7 @@ class DocType(TransactionBase): return ret - def get_item_list(self, obj, is_stopped): + def get_item_list(self, obj, is_stopped=0): """get item list""" il = [] for d in getlist(obj.doclist,obj.fname): @@ -394,7 +394,7 @@ class DocType(TransactionBase): if is_stopped: qty = flt(d.qty) > flt(d.delivered_qty) and flt(flt(d.qty) - flt(d.delivered_qty)) or 0 - if d.prevdoc_doctype == 'Sales Order': + if d.prevdoc_doctype == 'Sales Order': # used in delivery note to reduce reserved_qty # Eg.: if SO qty is 10 and there is tolerance of 20%, then it will allow DN of 12. # But in this case reserved qty should only be reduced by 10 and not 12. @@ -413,7 +413,7 @@ class DocType(TransactionBase): if p.parent_detail_docname == d.name: # the packing details table's qty is already multiplied with parent's qty il.append({ - 'warehouse': d.warehouse, + 'warehouse': p.warehouse, 'reserved_warehouse': reserved_wh, 'item_code': p.item_code, 'qty': flt(p.qty), @@ -496,23 +496,21 @@ class DocType(TransactionBase): pi.qty = flt(qty) pi.actual_qty = bin and flt(bin['actual_qty']) or 0 pi.projected_qty = bin and flt(bin['projected_qty']) or 0 - pi.warehouse = warehouse pi.prevdoc_doctype = line.prevdoc_doctype - if packing_item_code == line.item_code: - pi.serial_no = cstr(line.serial_no) + if not pi.warehouse: + pi.warehouse = warehouse + if not pi.batch_no: pi.batch_no = cstr(line.batch_no) pi.idx = self.packing_list_idx - # has to be saved, since this function is called on_update of delivery note + # saved, since this function is called on_update of delivery note pi.save() self.packing_list_idx += 1 - # ------------------ - # make packing list from sales bom if exists or directly copy item with balance - # ------------------ def make_packing_list(self, obj, fname): + """make packing list for sales bom item""" self.packing_list_idx = 0 parent_items = [] for d in getlist(obj.doclist, fname): @@ -520,10 +518,9 @@ class DocType(TransactionBase): if self.has_sales_bom(d.item_code): for i in self.get_sales_bom_items(d.item_code): self.update_packing_list_item(obj, i['item_code'], flt(i['qty'])*flt(d.qty), warehouse, d) - else: - self.update_packing_list_item(obj, d.item_code, d.qty, warehouse, d) - if [d.item_code, d.name] not in parent_items: - parent_items.append([d.item_code, d.name]) + + if [d.item_code, d.name] not in parent_items: + parent_items.append([d.item_code, d.name]) self.cleanup_packing_list(obj, parent_items) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index ad95cf1e9e..490051bcc0 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -197,51 +197,6 @@ class DocType(TransactionBase): get_obj('DocType Mapper', 'Sales Order-Delivery Note', with_children = 1).validate_reference_value(self, self.doc.name) - def validate_prevdoc_details(self): - for d in getlist(self.doclist,'delivery_note_details'): - prevdoc = d.prevdoc_doctype - prevdoc_docname = d.prevdoc_docname - - if prevdoc_docname and prevdoc: - # Validates Transaction Date of DN and previous doc (i.e. SO , PO, PR) - trans_date = sql("select posting_date from `tab%s` where name = '%s'" %(prevdoc,prevdoc_docname))[0][0] - if trans_date and getdate(self.doc.posting_date) < (trans_date): - msgprint("Your Posting Date cannot be before "+cstr(prevdoc)+" Date.") - raise Exception - # Validates DN and previous doc details - get_name = sql("select name from `tab%s` where name = '%s'" % (prevdoc, prevdoc_docname)) - name = get_name and get_name[0][0] or '' - if name: #check for incorrect docname - if prevdoc == 'Sales Order': - dt = sql("select company, docstatus, customer, currency, sales_partner from `tab%s` where name = '%s'" % (prevdoc, name)) - cust_name = dt and dt[0][2] or '' - if cust_name != self.doc.customer: - msgprint(cstr(prevdoc) + ": " + cstr(prevdoc_docname) + " customer :" + cstr(cust_name) + " does not match with customer : " + cstr(self.doc.customer) + " of current document.") - raise Exception, "Validation Error. " - sal_partner = dt and dt[0][4] or '' - if sal_partner != self.doc.sales_partner: - msgprint(cstr(prevdoc) + ": " + cstr(prevdoc_docname) + " sales partner name :" + cstr(sal_partner) + " does not match with sales partner name : " + cstr(self.doc.sales_partner_name) + " of current document.") - raise Exception, "Validation Error. " - else: - dt = sql("select company, docstatus, supplier, currency from `tab%s` where name = '%s'" % (prevdoc, name)) - supp_name = dt and dt[0][2] or '' - company_name = dt and dt[0][0] or '' - docstatus = dt and dt[0][1] or 0 - currency = dt and dt[0][3] or '' - if (currency != self.doc.currency): - msgprint(cstr(prevdoc) + ": " + cstr(prevdoc_docname) + " currency : "+ cstr(currency) + "does not match with Currency: " + cstr(self.doc.currency) + "of current document") - raise Exception, "Validation Error." - if (company_name != self.doc.company): - msgprint(cstr(prevdoc) + ": " + cstr(prevdoc_docname) + " does not belong to the Company: " + cstr(self.doc.company_name)) - raise Exception, "Validation Error." - if (docstatus != 1): - msgprint(cstr(prevdoc) + ": " + cstr(prevdoc_docname) + " is not Submitted Document.") - raise Exception, "Validation Error." - else: - msgprint(cstr(prevdoc) + ": " + cstr(prevdoc_docname) + " is not a valid " + cstr(prevdoc)) - raise Exception, "Validation Error." - - def validate_for_items(self): check_list, chk_dupl_itm = [], [] for d in getlist(self.doclist,'delivery_note_details'): @@ -292,11 +247,22 @@ class DocType(TransactionBase): set(self.doc, 'message', 'Items against your Order #%s have been delivered. Delivery #%s: ' % (self.doc.po_no, self.doc.name)) # Check for Approving Authority get_obj('Authorization Control').validate_approving_authority(self.doc.doctype, self.doc.company, self.doc.grand_total, self) + + # validate serial no for item table (non-sales-bom item) and packing list (sales-bom item) sl_obj = get_obj("Stock Ledger") + sl_obj.validate_serial_no(self, 'delivery_note_details') + sl_obj.validate_serial_no_warehouse(self, 'delivery_note_details') sl_obj.validate_serial_no(self, 'packing_details') sl_obj.validate_serial_no_warehouse(self, 'packing_details') + + # update delivery details in serial no + sl_obj.update_serial_record(self, 'delivery_note_details', is_submit = 1, is_incoming = 0) sl_obj.update_serial_record(self, 'packing_details', is_submit = 1, is_incoming = 0) + + # update delivered qty in sales order get_obj("Sales Common").update_prevdoc_detail(1,self) + + # create stock ledger entry self.update_stock_ledger(update_stock = 1) self.credit_limit() @@ -332,10 +298,14 @@ class DocType(TransactionBase): sales_com_obj = get_obj(dt = 'Sales Common') sales_com_obj.check_stop_sales_order(self) self.check_next_docstatus() - get_obj('Stock Ledger').update_serial_record(self, 'packing_details', is_submit = 0, is_incoming = 0) + + # remove delivery details from serial no + sl = get_obj('Stock Ledger') + sl.update_serial_record(self, 'delivery_note_details', is_submit = 0, is_incoming = 0) + sl.update_serial_record(self, 'packing_details', is_submit = 0, is_incoming = 0) + sales_com_obj.update_prevdoc_detail(0,self) self.update_stock_ledger(update_stock = -1) - # :::::: set DN status ::::::: set(self.doc, 'status', 'Cancelled') self.cancel_packing_slips() @@ -435,7 +405,8 @@ class DocType(TransactionBase): def on_update(self): get_obj('Sales Common').make_packing_list(self,'delivery_note_details') - self.set_actual_qty() - get_obj('Stock Ledger').scrub_serial_nos(self) + sl = get_obj('Stock Ledger') + sl.scrub_serial_nos(self) + sl.scrub_serial_nos(self, 'packing_details') diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.txt b/erpnext/stock/doctype/delivery_note/delivery_note.txt index 7db951328b..a0c2df0e9c 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.txt +++ b/erpnext/stock/doctype/delivery_note/delivery_note.txt @@ -3,9 +3,9 @@ # These values are common in all dictionaries { - 'creation': '2012-05-15 12:15:05', + 'creation': '2012-06-11 12:10:09', 'docstatus': 0, - 'modified': '2012-05-18 18:06:26', + 'modified': '2012-07-11 11:56:53', 'modified_by': u'Administrator', 'owner': u'Administrator' }, @@ -72,26 +72,14 @@ # DocPerm { - 'amend': 1, - 'cancel': 1, - 'create': 1, + 'amend': 0, + 'cancel': 0, + 'create': 0, 'doctype': u'DocPerm', - 'permlevel': 0, - 'role': u'Sales User', - 'submit': 1, - 'write': 1 - }, - - # DocPerm - { - 'amend': 1, - 'cancel': 1, - 'create': 1, - 'doctype': u'DocPerm', - 'permlevel': 0, - 'role': u'Material Master Manager', - 'submit': 1, - 'write': 1 + 'permlevel': 1, + 'role': u'Material User', + 'submit': 0, + 'write': 0 }, # DocPerm @@ -108,16 +96,72 @@ # DocPerm { + 'amend': 0, + 'cancel': 0, + 'create': 0, 'doctype': u'DocPerm', 'permlevel': 1, - 'role': u'All' + 'role': u'Material Manager', + 'submit': 0, + 'write': 0 + }, + + # DocPerm + { + 'amend': 1, + 'cancel': 1, + 'create': 1, + 'doctype': u'DocPerm', + 'permlevel': 0, + 'role': u'Sales User', + 'submit': 1, + 'write': 1 + }, + + # DocPerm + { + 'amend': 0, + 'cancel': 0, + 'create': 0, + 'doctype': u'DocPerm', + 'permlevel': 1, + 'role': u'Sales User', + 'submit': 0, + 'write': 0 + }, + + # DocPerm + { + 'cancel': 0, + 'create': 0, + 'doctype': u'DocPerm', + 'permlevel': 0, + 'role': u'Accounts User', + 'submit': 0, + 'write': 0 + }, + + # DocPerm + { + 'doctype': u'DocPerm', + 'permlevel': 1, + 'role': u'Accounts User' + }, + + # DocPerm + { + 'doctype': u'DocPerm', + 'match': u'customer_name', + 'permlevel': 0, + 'role': u'Customer' }, # DocPerm { 'doctype': u'DocPerm', 'permlevel': 2, - 'role': u'All' + 'role': u'All', + 'write': 1 }, # DocField @@ -340,6 +384,7 @@ # DocField { 'allow_on_submit': 1, + 'colour': u'White:FFF', 'doctype': u'DocField', 'fieldname': u'delivery_note_details', 'fieldtype': u'Table', @@ -352,6 +397,16 @@ 'print_hide': 0 }, + # DocField + { + 'doctype': u'DocField', + 'fieldname': u'sales_bom_help', + 'fieldtype': u'HTML', + 'label': u'Sales BOM Help', + 'permlevel': 0, + 'print_hide': 1 + }, + # DocField { 'doctype': u'DocField', diff --git a/erpnext/stock/doctype/delivery_note_packing_item/delivery_note_packing_item.txt b/erpnext/stock/doctype/delivery_note_packing_item/delivery_note_packing_item.txt index 51001fc9a0..db512c64c0 100644 --- a/erpnext/stock/doctype/delivery_note_packing_item/delivery_note_packing_item.txt +++ b/erpnext/stock/doctype/delivery_note_packing_item/delivery_note_packing_item.txt @@ -3,9 +3,9 @@ # These values are common in all dictionaries { - 'creation': '2012-04-13 11:56:35', + 'creation': '2012-06-11 12:10:10', 'docstatus': 0, - 'modified': '2012-05-09 12:55:23', + 'modified': '2012-07-10 12:05:31', 'modified_by': u'Administrator', 'owner': u'Administrator' }, @@ -111,7 +111,7 @@ 'oldfieldname': u'warehouse', 'oldfieldtype': u'Link', 'options': u'Warehouse', - 'permlevel': 1 + 'permlevel': 0 }, # DocField diff --git a/erpnext/stock/doctype/stock_ledger/stock_ledger.py b/erpnext/stock/doctype/stock_ledger/stock_ledger.py index 0cb4b96c3e..42ad6f6df7 100644 --- a/erpnext/stock/doctype/stock_ledger/stock_ledger.py +++ b/erpnext/stock/doctype/stock_ledger/stock_ledger.py @@ -51,19 +51,16 @@ class DocType: self.doclist = doclist - # ----------------- - # scrub serial nos - # ----------------- - def scrub_serial_nos(self, obj): - for d in getlist(obj.doclist, obj.fname): + def scrub_serial_nos(self, obj, table_name = ''): + if not table_name: + table_name = obj.fname + + for d in getlist(obj.doclist, table_name): if d.serial_no: d.serial_no = d.serial_no.replace(',', '\n') d.save() - # ----------------------------- - # validate serial no warehouse - # ----------------------------- def validate_serial_no_warehouse(self, obj, fname): for d in getlist(obj.doclist, fname): wh = d.warehouse or d.s_warehouse @@ -80,10 +77,8 @@ class DocType: msgprint("Serial No : %s for Item : %s doesn't exists in Warehouse : %s" % (s, d.item_code, wh), raise_exception = 1) - # ------------------------------------ - # check whether serial no is required - # ------------------------------------ def validate_serial_no(self, obj, fname): + """check whether serial no is required""" for d in getlist(obj.doclist, fname): is_stock_item = get_value('Item', d.item_code, 'is_stock_item') ar_required = get_value('Item', d.item_code, 'has_serial_no') @@ -101,18 +96,10 @@ class DocType: msgprint("Rejected serial no is mandatory for rejected qty of item: "+ d.item_code, raise_exception = 1) - - - - # ------------------- - # get serial no list - # ------------------- def get_sr_no_list(self, sr_nos, qty = 0, item_code = ''): return get_sr_no_list(sr_nos, qty, item_code) - # --------------------- - # set serial no values - # --------------------- + def set_pur_serial_no_values(self, obj, serial_no, d, s, new_rec): item_details = sql("select item_group, warranty_period from `tabItem` where name = '%s' and \ (ifnull(end_of_life,'')='' or end_of_life = '0000-00-00' or end_of_life > now()) " %(d.item_code), as_dict=1) @@ -143,9 +130,6 @@ class DocType: s.save(new_rec) - # ---------------------------------- - # update serial no purchase details - # ---------------------------------- def update_serial_purchase_details(self, obj, d, serial_no, is_submit, purpose = ''): exists = sql("select name, status, docstatus from `tabSerial No` where name = '%s'" % (serial_no)) if is_submit: @@ -168,9 +152,6 @@ class DocType: sql("update `tabSerial No` set docstatus = 2, status = 'Not in Use', purchase_document_type = '', purchase_document_no = '', purchase_date = null, purchase_rate = 0, supplier = null, supplier_name = '', supplier_address = '', warehouse = '' where name = '%s'" % serial_no) - # ------------------------------- - # check whether serial no exists - # ------------------------------- def check_serial_no_exists(self, serial_no, item_code): chk = sql("select name, status, docstatus, item_code from `tabSerial No` where name = %s", (serial_no), as_dict=1) if not chk: @@ -182,9 +163,7 @@ class DocType: elif chk and chk[0]['status'] == 'Delivered': msgprint("Serial No: %s of Item : %s is already delivered." % (serial_no, item_code), raise_exception = 1) - # --------------------- - # set serial no values - # --------------------- + def set_delivery_serial_no_values(self, obj, serial_no): s = Document('Serial No', serial_no) s.delivery_document_type = obj.doc.doctype @@ -203,9 +182,6 @@ class DocType: s.save() - # ---------------------------------- - # update serial no delivery details - # ---------------------------------- def update_serial_delivery_details(self, obj, d, serial_no, is_submit): if is_submit: self.check_serial_no_exists(serial_no, d.item_code) @@ -214,9 +190,6 @@ class DocType: sql("update `tabSerial No` set docstatus = 0, status = 'In Store', delivery_document_type = '', delivery_document_no = '', delivery_date = null, customer = null, customer_name = '', delivery_address = '', territory = null where name = '%s'" % (serial_no)) - # --------------------- - # update serial record - # --------------------- def update_serial_record(self, obj, fname, is_submit = 1, is_incoming = 0): import datetime for d in getlist(obj.doclist, fname): @@ -235,11 +208,6 @@ class DocType: self.update_serial_purchase_details(obj, d, a, is_submit) - - - # ------------- - # update stock - # ------------- def update_stock(self, values, is_amended = 'No'): for v in values: sle_id, serial_nos = '', '' @@ -261,9 +229,6 @@ class DocType: v["posting_date"], sle_id, v["posting_time"], '', v["is_cancelled"],v["voucher_type"],v["voucher_no"], is_amended) - # ----------- - # make entry - # ----------- def make_entry(self, args): sle = Document(doctype = 'Stock Ledger Entry') for k in args.keys():