diff --git a/erpnext/accounts/doctype/payable_voucher/payable_voucher.py b/erpnext/accounts/doctype/payable_voucher/payable_voucher.py index 9f2848ad6f7..9f77935a5ef 100644 --- a/erpnext/accounts/doctype/payable_voucher/payable_voucher.py +++ b/erpnext/accounts/doctype/payable_voucher/payable_voucher.py @@ -232,15 +232,11 @@ class DocType(TransactionBase): # Validate Acc Head of Supplier and Credit To Account entered # ------------------------------------------------------------ def check_for_acc_head_of_supplier(self): - acc_head = sql("select name from `tabAccount` where name = %s", (cstr(self.doc.supplier) + " - " + self.get_company_abbr())) - if self.doc.supplier: - if acc_head and acc_head[0][0]: - if not cstr(acc_head[0][0]) == cstr(self.doc.credit_to): - msgprint("Credit To: %s do not match with Supplier: %s for Company: %s i.e. %s" %(self.doc.credit_to,self.doc.supplier,self.doc.company,cstr(acc_head[0][0]))) - raise Exception, "Validation Error " - if not acc_head: - msgprint("Supplier %s does not have an Account Head in %s. You must first create it from the Supplier Master" % (self.doc.supplier, self.doc.company)) - raise Exception, "Validation Error " + if self.doc.supplier and self.doc.credit_to: + acc_head = sql("select master_name from `tabAccount` where name = %s", self.doc.credit_to) + + if (acc_head and cstr(acc_head[0][0]) != cstr(self.doc.credit_to)) or (not acc_head and (self.doc.credit_to != cstr(self.doc.supplier) + " - " + self.get_company_abbr())): + msgprint("Credit To: %s do not match with Supplier: %s for Company: %s.\n If both correctly entered, please select Master Type and Master Name in account master." %(self.doc.credit_to,self.doc.supplier,self.doc.company), raise_exception=1) # Check for Stopped PO # --------------------- diff --git a/erpnext/accounts/doctype/receivable_voucher/receivable_voucher.js b/erpnext/accounts/doctype/receivable_voucher/receivable_voucher.js index 4478c5cafe9..bb19681b98c 100644 --- a/erpnext/accounts/doctype/receivable_voucher/receivable_voucher.js +++ b/erpnext/accounts/doctype/receivable_voucher/receivable_voucher.js @@ -74,6 +74,9 @@ cur_frm.cscript.hide_fields = function(doc, cdt, cdn) { // ------- cur_frm.cscript.refresh = function(doc, dt, dn) { + cur_frm.cscript.is_opening(doc, dt, dn); + cur_frm.cscript.hide_fields(doc, cdt, cdn); + // Show / Hide button cur_frm.clear_custom_buttons(); @@ -90,8 +93,6 @@ cur_frm.cscript.refresh = function(doc, dt, dn) { } else hide_field('Repair Outstanding Amt'); - cur_frm.cscript.is_opening(doc, dt, dn); - cur_frm.cscript.hide_fields(doc, cdt, cdn); } //fetch retail transaction related fields diff --git a/erpnext/home/doctype/home_control/home_control.py b/erpnext/home/doctype/home_control/home_control.py index 8242e744315..ce15bfd57ac 100644 --- a/erpnext/home/doctype/home_control/home_control.py +++ b/erpnext/home/doctype/home_control/home_control.py @@ -31,6 +31,8 @@ class DocType: def get_modules(self): rl = webnotes.user.get_roles() ml = sql("select distinct t1.name, t1.module_icon, t1.module_label, t1.module_desc, t1.module_page from `tabModule Def` t1, `tabModule Def Role` t2 where t2.role in ('%s') and t1.disabled !='Yes' and ifnull(t1.is_hidden, 'No') != 'Yes' and t1.name = t2.parent order by t1.module_seq asc" % "','".join(rl), as_dict=1) + webnotes.response['login_url'] = session['data'].get('login_from', '') + return ml def get_module_details(self,m): @@ -48,8 +50,6 @@ class DocType: AND t2.role IN ("%s") AND ifnull(standard,"No")="No"''' % (m, '", "'.join(webnotes.user.get_roles())), as_dict=1) - ret['login_url'] = session['data'].get('login_from', '') - return ret # ---------------------------------------------------------------------------------------------------------------- diff --git a/erpnext/hr/doctype/appraisal/appraisal.js b/erpnext/hr/doctype/appraisal/appraisal.js index b2683d82de1..d040c5353df 100644 --- a/erpnext/hr/doctype/appraisal/appraisal.js +++ b/erpnext/hr/doctype/appraisal/appraisal.js @@ -1,13 +1,20 @@ cur_frm.add_fetch('employee', 'company', 'company'); cur_frm.cscript.onload = function(doc,cdt,cdn){ - if(!doc.status) set_multiple(dt,dn,{status:'Draft'}); - if(doc.employee) cur_frm.cscript.employee(doc,cdt,cdn); - if(doc.amended_from && doc.__islocal) cur_frm.cscript.refresh_appraisal_details(doc, cdt, cdn); + if(!doc.status) + set_multiple(cdt,cdn,{status:'Draft'}); + if(doc.amended_from && doc.__islocal) + cur_frm.cscript.refresh_appraisal_details(doc, cdt, cdn); +} + +cur_frm.cscript.onload_post_render = function(doc,cdt,cdn){ + if(doc.employee) + cur_frm.cscript.employee(doc,cdt,cdn); } cur_frm.cscript.refresh = function(doc,cdt,cdn){ - if(user == doc.kra_approver && doc.status == 'Submitted') unhide_field(['Update', 'Declare Completed', 'Calculate Total Score']); + if(user == doc.kra_approver && doc.status == 'Submitted') + unhide_field(['Update', 'Declare Completed', 'Calculate Total Score']); else hide_field(['Update', 'Declare Completed', 'Calculate Total Score']); if(!doc.docstatus) unhide_field('Fetch Template'); diff --git a/erpnext/hr/doctype/salary_manager/salary_manager.py b/erpnext/hr/doctype/salary_manager/salary_manager.py index 50710568e81..badd69bd8e5 100644 --- a/erpnext/hr/doctype/salary_manager/salary_manager.py +++ b/erpnext/hr/doctype/salary_manager/salary_manager.py @@ -46,7 +46,7 @@ class DocType: self.check_mandatory() cond = '' - for f in ['company', 'branch', 'department', 'designation', 'grade', 'employment_type']: + for f in ['company', 'branch', 'department', 'designation', 'grade']: if self.doc.fields.get(f): cond += " and t1." + f + " = '" + self.doc.fields.get(f) + "'" diff --git a/erpnext/hr/doctype/salary_manager/salary_manager.txt b/erpnext/hr/doctype/salary_manager/salary_manager.txt index 66a72823803..c1ca4c6038e 100644 --- a/erpnext/hr/doctype/salary_manager/salary_manager.txt +++ b/erpnext/hr/doctype/salary_manager/salary_manager.txt @@ -5,18 +5,19 @@ { 'creation': '2011-08-11 16:40:04', 'docstatus': 0, - 'modified': '2011-08-25 12:02:57', + 'modified': '2011-11-07 10:47:32', 'modified_by': 'Administrator', 'owner': 'Administrator' }, # These values are common for all DocType { - '_last_update': '1314179318', + '_last_update': '1314253977', 'allow_copy': 1, 'allow_email': 1, 'allow_print': 1, 'colour': 'White:FFF', + 'default_print_format': 'Standard', 'doctype': 'DocType', 'document_type': 'Other', 'issingle': 1, @@ -24,7 +25,7 @@ 'name': '__common__', 'section_style': 'Simple', 'show_in_menu': 1, - 'version': 29 + 'version': 30 }, # These values are common for all DocField @@ -59,21 +60,18 @@ # DocPerm { 'doctype': 'DocPerm', - 'idx': 1, 'role': 'System Manager' }, # DocPerm { 'doctype': 'DocPerm', - 'idx': 2, 'role': 'HR User' }, # DocPerm { 'doctype': 'DocPerm', - 'idx': 3, 'role': 'HR Manager' }, @@ -82,7 +80,6 @@ 'colour': 'White:FFF', 'doctype': 'DocField', 'fieldtype': 'HTML', - 'idx': 1, 'label': 'Document Description', 'options': '
You can generate multiple salary slips based on the selected criteria, submit and mail those to the employee directly from here
' }, @@ -90,15 +87,13 @@ # DocField { 'doctype': 'DocField', - 'fieldtype': 'Section Break', - 'idx': 2 + 'fieldtype': 'Section Break' }, # DocField { 'doctype': 'DocField', 'fieldtype': 'Column Break', - 'idx': 3, 'width': '50%' }, @@ -107,7 +102,6 @@ 'doctype': 'DocField', 'fieldname': 'company', 'fieldtype': 'Select', - 'idx': 4, 'label': 'Company', 'options': 'link:Company', 'reqd': 1 @@ -118,7 +112,6 @@ 'doctype': 'DocField', 'fieldname': 'branch', 'fieldtype': 'Link', - 'idx': 5, 'label': 'Branch', 'options': 'Branch' }, @@ -128,7 +121,6 @@ 'doctype': 'DocField', 'fieldname': 'department', 'fieldtype': 'Link', - 'idx': 6, 'label': 'Department', 'options': 'Department' }, @@ -138,7 +130,6 @@ 'doctype': 'DocField', 'fieldname': 'designation', 'fieldtype': 'Link', - 'idx': 7, 'label': 'Designation', 'options': 'Designation' }, @@ -147,7 +138,6 @@ { 'doctype': 'DocField', 'fieldtype': 'Column Break', - 'idx': 8, 'width': '50%' }, @@ -156,28 +146,16 @@ 'doctype': 'DocField', 'fieldname': 'grade', 'fieldtype': 'Select', - 'idx': 9, 'label': 'Grade', 'options': 'link:Grade' }, - # DocField - { - 'doctype': 'DocField', - 'fieldname': 'employment_type', - 'fieldtype': 'Select', - 'idx': 10, - 'label': 'Employment Type', - 'options': 'link:Employment Type' - }, - # DocField { 'colour': 'White:FFF', 'doctype': 'DocField', 'fieldname': 'fiscal_year', 'fieldtype': 'Select', - 'idx': 11, 'label': 'Fiscal Year', 'options': 'link:Fiscal Year', 'reqd': 1 @@ -189,7 +167,6 @@ 'doctype': 'DocField', 'fieldname': 'month', 'fieldtype': 'Select', - 'idx': 12, 'label': 'Month', 'options': '\n01\n02\n03\n04\n05\n06\n07\n08\n09\n10\n11\n12', 'reqd': 1 @@ -202,22 +179,19 @@ 'doctype': 'DocField', 'fieldname': 'send_email', 'fieldtype': 'Check', - 'idx': 13, 'label': 'Send Email' }, # DocField { 'doctype': 'DocField', - 'fieldtype': 'Section Break', - 'idx': 14 + 'fieldtype': 'Section Break' }, # DocField { 'doctype': 'DocField', 'fieldtype': 'Column Break', - 'idx': 15, 'width': '50%' }, @@ -227,7 +201,6 @@ 'description': 'Creates salary slip for above mentioned criteria.', 'doctype': 'DocField', 'fieldtype': 'Button', - 'idx': 16, 'label': 'Create Salary Slip', 'trigger': 'Client' }, @@ -236,7 +209,6 @@ { 'doctype': 'DocField', 'fieldtype': 'Column Break', - 'idx': 17, 'width': '25%' }, @@ -246,7 +218,6 @@ 'description': 'Submit all salary slips for the above selected criteria', 'doctype': 'DocField', 'fieldtype': 'Button', - 'idx': 18, 'label': 'Submit Salary Slip', 'trigger': 'Client' }, @@ -255,7 +226,6 @@ { 'doctype': 'DocField', 'fieldtype': 'Column Break', - 'idx': 19, 'width': '25%' }, @@ -265,7 +235,6 @@ 'description': 'Create Bank Voucher for the total salary paid for the above selected criteria', 'doctype': 'DocField', 'fieldtype': 'Button', - 'idx': 20, 'label': 'Make Bank Voucher', 'trigger': 'Client' }, @@ -273,15 +242,13 @@ # DocField { 'doctype': 'DocField', - 'fieldtype': 'Section Break', - 'idx': 21 + 'fieldtype': 'Section Break' }, # DocField { 'doctype': 'DocField', 'fieldtype': 'HTML', - 'idx': 22, 'label': 'Activity Log' } ] \ No newline at end of file diff --git a/erpnext/patches/patch.py b/erpnext/patches/patch.py index 5dd8b8d443d..7e8a083d4db 100644 --- a/erpnext/patches/patch.py +++ b/erpnext/patches/patch.py @@ -344,8 +344,7 @@ def execute(patch_no): bin = sql("select name from tabBin") for b in bin: bobj = get_obj('Bin',b[0]) - prev_sle = bobj.get_prev_sle(posting_date = '2011-09-01', posting_time = '01:00') - bobj.update_item_valuation(posting_date = '2011-09-01', posting_time = '01:00', prev_sle = prev_sle) + bobj.update_entries_after(posting_date = '2011-09-01', posting_time = '01:00') elif patch_no == 368: from webnotes.utils import nestedset t = [ diff --git a/erpnext/patches/reload_bom.py b/erpnext/patches/reload_bom.py new file mode 100644 index 00000000000..f050f3c710d --- /dev/null +++ b/erpnext/patches/reload_bom.py @@ -0,0 +1,5 @@ +def execute(): + import webnotes + from webnotes.modules.module_manager import reload_doc + + reload_doc('production', 'doctype', 'bill_of_materials') diff --git a/erpnext/patches/reload_flat_bom.py b/erpnext/patches/reload_flat_bom.py new file mode 100644 index 00000000000..b493610d16a --- /dev/null +++ b/erpnext/patches/reload_flat_bom.py @@ -0,0 +1,6 @@ +def execute(): + from webnotes.modules import webnotes + from webnotes.modules.module_manager import reload_doc + + reload_doc('production', 'doctype', 'flat_bom_detail') + reload_doc('production', 'doctype', 'bom_material') diff --git a/erpnext/patches/sal_man_patch.py b/erpnext/patches/sal_man_patch.py new file mode 100644 index 00000000000..aef625154bf --- /dev/null +++ b/erpnext/patches/sal_man_patch.py @@ -0,0 +1,8 @@ + +def execute(): + import webnotes + from webnotes.modules.module_manager import reload_doc + sql = webnotes.conn.sql + + reload_doc('hr', 'doctype', 'salary_manager') + sql("delete from `tabDocField` where parent = 'Salary Manager' and fieldname = 'employment_type'") diff --git a/erpnext/production/doctype/bill_of_materials/bill_of_materials.py b/erpnext/production/doctype/bill_of_materials/bill_of_materials.py index 9fcfb81f518..a62e4f2c3b8 100644 --- a/erpnext/production/doctype/bill_of_materials/bill_of_materials.py +++ b/erpnext/production/doctype/bill_of_materials/bill_of_materials.py @@ -32,9 +32,10 @@ class DocType: #----------- Client Trigger function ---------- def get_item_detail(self, item_code): - item = sql("select description from `tabItem` where (ifnull(end_of_life,'')='' or end_of_life = '0000-00-00' or end_of_life > now()) and name = %s",item_code , as_dict =1) + item = sql("select description, stock_uom from `tabItem` where (ifnull(end_of_life,'')='' or end_of_life = '0000-00-00' or end_of_life > now()) and name = %s",item_code , as_dict =1) ret={ - 'description' : item and item[0]['description'] or '' + 'description' : item and item[0]['description'] or '', + 'uom' : item and item[0]['stock_uom'] or '' } return ret @@ -411,8 +412,8 @@ class DocType: if val_method == 'FIFO': if warehouse: bin_obj = get_obj('Warehouse',warehouse).get_bin(item_code) - prev_sle = bin_obj.get_prev_sle('',nowdate(), (now().split(' ')[1])[:-3]) - fcfs_stack = prev_sle and (prev_sle[0][3] and eval(prev_sle[0][3]) or []) or [] + prev_sle = bin_obj.get_prev_sle(nowdate(), (now().split(' ')[1])[:-3]) + fcfs_stack = prev_sle and prev_sle['fcfs_stack'] and eval(prev_sle['fcfs_stack']) or [] else: prev_sle = sql("select fcfs_stack from `tabStock Ledger Entry` where item_code = '%s' and posting_date <= '%s' order by posting_date DESC, posting_time DESC, name DESC limit 1" % (item_code, nowdate())) fcfs_stack = prev_sle and (prev_sle[0][0] and eval(prev_sle[0][0]) or []) or [] @@ -499,20 +500,11 @@ class DocType: def get_child_flat_bom_items(self, item, d): child_flat_bom_items=[] -# if item and (item[0]['is_sub_contracted_item'] == 'Yes' or item[0]['is_pro_applicable'] == 'Yes'): child_flat_bom_items = sql("select fbom.item_code, fbom.description, fbom.qty_consumed_per_unit, fbom.stock_uom, fbom.moving_avg_rate, fbom.last_purchase_rate, fbom.standard_rate, '%s' as parent_bom, fbom.bom_mat_no, 'No' as is_pro_applicable from `tabFlat BOM Detail` fbom,`tabBill Of Materials` bom where fbom.parent=bom.name and fbom.parent = '%s' and fbom.is_pro_applicable = 'No' and bom.docstatus = 1" % ( d.bom_no, cstr(d.bom_no))) self.cur_flat_bom_items.append([d.item_code, d.description, flt(d.qty), d.stock_uom, flt(d.moving_avg_rate), flt(d.amount_as_per_mar), flt(d.last_purchase_rate), flt(d.amount_as_per_lpr), flt(d.standard_rate), flt(d.amount_as_per_sr), flt(d.qty_consumed_per_unit), (item[0]['is_sub_contracted_item'] == 'Yes') and d.parent or d.bom_no, d.name, (item[0]['is_sub_contracted_item'] == 'Yes') and 'No' or 'Yes']) return child_flat_bom_items -# else: -# child_flat_bom_items = sql("select item_code, description, qty_consumed_per_unit, stock_uom, moving_avg_rate, last_purchase_rate, standard_rate, if(parent_bom = '%s', '%s', parent_bom) as parent_bom, bom_mat_no, is_pro_applicable from `tabFlat BOM Detail` where parent = '%s' and docstatus = 1" % ( d.bom_no, d.parent, cstr(d.bom_no))) - -# if not child_flat_bom_items: -# msgprint("Please Submit Child BOM := %s first." % cstr(d.bom_no)) -# raise Exception -# else:""" - # Get Current Flat BOM Items # ----------------------------- diff --git a/erpnext/production/doctype/bill_of_materials/bill_of_materials.txt b/erpnext/production/doctype/bill_of_materials/bill_of_materials.txt index 259081793dc..3713667fb7e 100644 --- a/erpnext/production/doctype/bill_of_materials/bill_of_materials.txt +++ b/erpnext/production/doctype/bill_of_materials/bill_of_materials.txt @@ -5,14 +5,14 @@ { 'creation': '2010-08-08 17:08:52', 'docstatus': 0, - 'modified': '2010-12-20 17:27:58', - 'modified_by': 'umair@iwebnotes.com', + 'modified': '2011-11-09 12:47:50', + 'modified_by': 'Administrator', 'owner': 'Administrator' }, # These values are common for all DocType { - '_last_update': '1309508837', + '_last_update': '1319016431', 'allow_attach': 0, 'allow_copy': 0, 'allow_email': 0, @@ -20,6 +20,7 @@ 'allow_rename': 0, 'allow_trash': 1, 'colour': 'White:FFF', + 'default_print_format': 'Standard', 'doctype': 'DocType', 'document_type': 'Master', 'hide_heading': 0, @@ -35,7 +36,7 @@ 'server_code_error': ' ', 'show_in_menu': 0, 'subject': '%(item)s', - 'version': 170 + 'version': 171 }, # These values are common for all DocField @@ -49,6 +50,7 @@ # These values are common for all DocPerm { + 'amend': 0, 'doctype': 'DocPerm', 'name': '__common__', 'parent': 'Bill Of Materials', @@ -68,7 +70,6 @@ 'cancel': 1, 'create': 1, 'doctype': 'DocPerm', - 'idx': 1, 'permlevel': 0, 'role': 'System Manager', 'submit': 1, @@ -77,10 +78,13 @@ # DocPerm { + 'cancel': 0, + 'create': 0, 'doctype': 'DocPerm', - 'idx': 2, 'permlevel': 1, - 'role': 'System Manager' + 'role': 'System Manager', + 'submit': 0, + 'write': 0 }, # DocPerm @@ -88,7 +92,6 @@ 'cancel': 1, 'create': 1, 'doctype': 'DocPerm', - 'idx': 3, 'permlevel': 0, 'role': 'Production Manager', 'submit': 1, @@ -97,10 +100,13 @@ # DocPerm { + 'cancel': 0, + 'create': 0, 'doctype': 'DocPerm', - 'idx': 4, 'permlevel': 1, - 'role': 'Production Manager' + 'role': 'Production Manager', + 'submit': 0, + 'write': 0 }, # DocPerm @@ -108,7 +114,6 @@ 'cancel': 1, 'create': 1, 'doctype': 'DocPerm', - 'idx': 5, 'permlevel': 0, 'role': 'Production User', 'submit': 1, @@ -117,10 +122,13 @@ # DocPerm { + 'cancel': 0, + 'create': 0, 'doctype': 'DocPerm', - 'idx': 6, 'permlevel': 1, - 'role': 'Production User' + 'role': 'Production User', + 'submit': 0, + 'write': 0 }, # DocField @@ -128,7 +136,6 @@ 'doctype': 'DocField', 'fieldname': 'trash_reason', 'fieldtype': 'Small Text', - 'idx': 1, 'label': 'Trash Reason', 'oldfieldname': 'trash_reason', 'oldfieldtype': 'Small Text', @@ -139,7 +146,6 @@ { 'doctype': 'DocField', 'fieldtype': 'HTML', - 'idx': 2, 'label': 'TreeView1', 'oldfieldtype': 'HTML', 'options': '
', @@ -150,7 +156,6 @@ { 'doctype': 'DocField', 'fieldtype': 'Section Break', - 'idx': 3, 'oldfieldtype': 'Section Break', 'options': 'Simple', 'permlevel': 0 @@ -162,7 +167,6 @@ 'colour': 'White:FFF', 'doctype': 'DocField', 'fieldtype': 'Button', - 'idx': 4, 'label': 'Set as Default BOM', 'oldfieldtype': 'Button', 'permlevel': 0, @@ -173,7 +177,6 @@ { 'doctype': 'DocField', 'fieldtype': 'Column Break', - 'idx': 5, 'oldfieldtype': 'Column Break', 'permlevel': 0 }, @@ -185,7 +188,6 @@ 'doctype': 'DocField', 'fieldtype': 'Button', 'hidden': 1, - 'idx': 6, 'label': 'Activate BOM', 'oldfieldtype': 'Button', 'permlevel': 0, @@ -199,7 +201,6 @@ 'doctype': 'DocField', 'fieldtype': 'Button', 'hidden': 1, - 'idx': 7, 'label': 'Inactivate BOM', 'oldfieldtype': 'Button', 'permlevel': 0, @@ -210,7 +211,6 @@ { 'doctype': 'DocField', 'fieldtype': 'Section Break', - 'idx': 8, 'label': 'Details', 'oldfieldtype': 'Section Break', 'permlevel': 0 @@ -223,7 +223,6 @@ 'doctype': 'DocField', 'fieldname': 'item', 'fieldtype': 'Link', - 'idx': 9, 'in_filter': 1, 'label': 'Item', 'oldfieldname': 'item', @@ -240,7 +239,6 @@ 'doctype': 'DocField', 'fieldname': 'description', 'fieldtype': 'Text', - 'idx': 10, 'label': 'Description', 'oldfieldname': 'description', 'oldfieldtype': 'Text', @@ -255,7 +253,6 @@ 'doctype': 'DocField', 'fieldname': 'quantity', 'fieldtype': 'Currency', - 'idx': 11, 'label': 'Quantity', 'oldfieldname': 'quantity', 'oldfieldtype': 'Currency', @@ -263,13 +260,22 @@ 'reqd': 1 }, + # DocField + { + 'doctype': 'DocField', + 'fieldname': 'uom', + 'fieldtype': 'Select', + 'label': 'UOM', + 'options': 'link:UOM', + 'permlevel': 1 + }, + # DocField { 'colour': 'White:FFF', 'doctype': 'DocField', 'fieldname': 'is_active', 'fieldtype': 'Select', - 'idx': 12, 'label': 'Is Active', 'no_copy': 1, 'oldfieldname': 'is_active', @@ -285,7 +291,6 @@ 'doctype': 'DocField', 'fieldname': 'is_default', 'fieldtype': 'Check', - 'idx': 13, 'label': 'Is Default', 'no_copy': 1, 'oldfieldname': 'is_default', @@ -299,7 +304,6 @@ 'doctype': 'DocField', 'fieldname': 'project_name', 'fieldtype': 'Link', - 'idx': 14, 'in_filter': 1, 'label': 'Project Name', 'oldfieldname': 'project_name', @@ -313,7 +317,6 @@ { 'doctype': 'DocField', 'fieldtype': 'Section Break', - 'idx': 15, 'label': 'Operations', 'oldfieldtype': 'Section Break', 'permlevel': 0 @@ -326,7 +329,6 @@ 'doctype': 'DocField', 'fieldname': 'bom_operations', 'fieldtype': 'Table', - 'idx': 16, 'label': 'BOM Operations', 'oldfieldname': 'bom_operations', 'oldfieldtype': 'Table', @@ -338,7 +340,6 @@ { 'doctype': 'DocField', 'fieldtype': 'Section Break', - 'idx': 17, 'label': 'Materials', 'oldfieldtype': 'Section Break', 'permlevel': 0 @@ -351,7 +352,6 @@ 'doctype': 'DocField', 'fieldname': 'bom_materials', 'fieldtype': 'Table', - 'idx': 18, 'label': 'BOM Material', 'oldfieldname': 'bom_materials', 'oldfieldtype': 'Table', @@ -363,7 +363,6 @@ { 'doctype': 'DocField', 'fieldtype': 'Section Break', - 'idx': 19, 'label': 'Costing', 'oldfieldtype': 'Section Break', 'permlevel': 0 @@ -375,7 +374,6 @@ 'doctype': 'DocField', 'fieldname': 'remarks', 'fieldtype': 'Text', - 'idx': 20, 'label': 'Remarks', 'no_copy': 1, 'oldfieldname': 'remarks', @@ -388,7 +386,6 @@ 'doctype': 'DocField', 'fieldname': 'cost_as_per_mar', 'fieldtype': 'Currency', - 'idx': 21, 'label': 'Cost As Per Valuation Rate', 'oldfieldname': 'cost_as_per_mar', 'oldfieldtype': 'Currency', @@ -400,7 +397,6 @@ 'doctype': 'DocField', 'fieldname': 'cost_as_per_lpr', 'fieldtype': 'Currency', - 'idx': 22, 'label': 'Cost As Per LPR', 'oldfieldname': 'cost_as_per_lpr', 'oldfieldtype': 'Currency', @@ -412,7 +408,6 @@ 'doctype': 'DocField', 'fieldname': 'cost_as_per_sr', 'fieldtype': 'Currency', - 'idx': 23, 'label': 'Cost As Per SR', 'oldfieldname': 'cost_as_per_sr', 'oldfieldtype': 'Currency', @@ -425,7 +420,6 @@ 'doctype': 'DocField', 'fieldname': 'cost_as_on', 'fieldtype': 'Data', - 'idx': 24, 'label': 'Cost as on', 'oldfieldname': 'cost_as_on', 'oldfieldtype': 'Data', @@ -437,7 +431,6 @@ 'doctype': 'DocField', 'fieldname': 'dir_mat_as_per_mar', 'fieldtype': 'Currency', - 'idx': 25, 'label': 'Direct Material As Per Valuation', 'oldfieldname': 'dir_mat_as_per_mar', 'oldfieldtype': 'Currency', @@ -449,7 +442,6 @@ 'doctype': 'DocField', 'fieldname': 'dir_mat_as_per_lpr', 'fieldtype': 'Currency', - 'idx': 26, 'label': 'Direct Material As Per LPR', 'oldfieldname': 'dir_mat_as_per_lpr', 'oldfieldtype': 'Currency', @@ -461,7 +453,6 @@ 'doctype': 'DocField', 'fieldname': 'dir_mat_as_per_sr', 'fieldtype': 'Currency', - 'idx': 27, 'label': 'Direct Material As Per SR', 'oldfieldname': 'dir_mat_as_per_sr', 'oldfieldtype': 'Currency', @@ -473,7 +464,6 @@ 'doctype': 'DocField', 'fieldname': 'operating_cost', 'fieldtype': 'Currency', - 'idx': 28, 'label': 'Operating Cost', 'oldfieldname': 'operating_cost', 'oldfieldtype': 'Currency', @@ -485,7 +475,6 @@ 'doctype': 'DocField', 'fieldname': 'maintained_by', 'fieldtype': 'Data', - 'idx': 29, 'label': 'Maintained By', 'oldfieldname': 'maintained_by', 'oldfieldtype': 'Data', @@ -497,7 +486,6 @@ 'doctype': 'DocField', 'fieldtype': 'Section Break', 'hidden': 1, - 'idx': 30, 'label': 'BOM Report', 'oldfieldtype': 'Section Break', 'permlevel': 0 @@ -508,7 +496,6 @@ 'doctype': 'DocField', 'fieldtype': 'Section Break', 'hidden': 0, - 'idx': 31, 'label': 'Flat BOM', 'oldfieldtype': 'Section Break', 'permlevel': 0 @@ -522,7 +509,6 @@ 'fieldname': 'flat_bom_details', 'fieldtype': 'Table', 'hidden': 0, - 'idx': 32, 'label': 'Flat BOM Detail', 'no_copy': 1, 'oldfieldname': 'flat_bom_details', diff --git a/erpnext/production/doctype/bom_material/bom_material.txt b/erpnext/production/doctype/bom_material/bom_material.txt index 1ce35a2f144..436519e889d 100644 --- a/erpnext/production/doctype/bom_material/bom_material.txt +++ b/erpnext/production/doctype/bom_material/bom_material.txt @@ -329,11 +329,11 @@ { 'doctype': 'DocField', 'fieldname': 'qty_consumed_per_unit', - 'fieldtype': 'Currency', + 'fieldtype': 'Float', 'idx': 21, 'label': 'Qty Consumed Per Unit', 'oldfieldname': 'qty_consumed_per_unit', - 'oldfieldtype': 'Currency', + 'oldfieldtype': 'Float', 'permlevel': 1 } -] \ No newline at end of file +] diff --git a/erpnext/production/doctype/flat_bom_detail/flat_bom_detail.txt b/erpnext/production/doctype/flat_bom_detail/flat_bom_detail.txt index 73393005fb2..f0d5d58bdfd 100644 --- a/erpnext/production/doctype/flat_bom_detail/flat_bom_detail.txt +++ b/erpnext/production/doctype/flat_bom_detail/flat_bom_detail.txt @@ -5,8 +5,8 @@ { 'creation': '2010-08-08 17:09:02', 'docstatus': 0, - 'modified': '2010-09-20 14:06:57', - 'modified_by': 'umair@iwebnotes.com', + 'modified': '2011-11-10 14:21:40', + 'modified_by': 'Administrator', 'owner': 'jai@webnotestech.com' }, @@ -14,6 +14,7 @@ { 'autoname': 'FBD/.######', 'colour': 'White:FFF', + 'default_print_format': 'Standard', 'doctype': 'DocType', 'istable': 1, 'module': 'Production', @@ -22,7 +23,7 @@ 'section_style': 'Simple', 'server_code_error': ' ', 'show_in_menu': 0, - 'version': 15 + 'version': 18 }, # These values are common for all DocField @@ -46,7 +47,6 @@ 'doctype': 'DocField', 'fieldname': 'item_code', 'fieldtype': 'Link', - 'idx': 1, 'label': 'Item Code', 'oldfieldname': 'item_code', 'oldfieldtype': 'Link', @@ -58,7 +58,6 @@ 'doctype': 'DocField', 'fieldname': 'description', 'fieldtype': 'Text', - 'idx': 2, 'label': 'Description', 'oldfieldname': 'description', 'oldfieldtype': 'Text', @@ -70,7 +69,6 @@ 'doctype': 'DocField', 'fieldname': 'moving_avg_rate', 'fieldtype': 'Currency', - 'idx': 5, 'label': 'Valuation Rate', 'oldfieldname': 'moving_avg_rate', 'oldfieldtype': 'Currency' @@ -81,7 +79,6 @@ 'doctype': 'DocField', 'fieldname': 'amount_as_per_mar', 'fieldtype': 'Currency', - 'idx': 6, 'label': 'Amount As Per Valuation Rate', 'oldfieldname': 'amount_as_per_mar', 'oldfieldtype': 'Currency' @@ -92,7 +89,6 @@ 'doctype': 'DocField', 'fieldname': 'last_purchase_rate', 'fieldtype': 'Currency', - 'idx': 7, 'label': 'Last Purchase Rate', 'oldfieldname': 'last_purchase_rate', 'oldfieldtype': 'Currency' @@ -103,7 +99,6 @@ 'doctype': 'DocField', 'fieldname': 'amount_as_per_lpr', 'fieldtype': 'Currency', - 'idx': 8, 'label': 'Amount As Per LPR', 'oldfieldname': 'amount_as_per_lpr', 'oldfieldtype': 'Currency' @@ -114,7 +109,6 @@ 'doctype': 'DocField', 'fieldname': 'qty', 'fieldtype': 'Currency', - 'idx': 9, 'label': 'Qty', 'oldfieldname': 'qty', 'oldfieldtype': 'Currency' @@ -125,7 +119,6 @@ 'doctype': 'DocField', 'fieldname': 'standard_rate', 'fieldtype': 'Currency', - 'idx': 9, 'label': 'Standard Rate', 'oldfieldname': 'standard_rate', 'oldfieldtype': 'Currency' @@ -136,7 +129,6 @@ 'doctype': 'DocField', 'fieldname': 'amount_as_per_sr', 'fieldtype': 'Currency', - 'idx': 10, 'label': 'Amount As Per SR', 'oldfieldname': 'amount_as_per_sr', 'oldfieldtype': 'Currency' @@ -146,11 +138,10 @@ { 'doctype': 'DocField', 'fieldname': 'qty_consumed_per_unit', - 'fieldtype': 'Currency', - 'idx': 11, + 'fieldtype': 'Float', 'label': 'Qty Consumed Per Unit', 'oldfieldname': 'qty_consumed_per_unit', - 'oldfieldtype': 'Currency' + 'oldfieldtype': 'Float' }, # DocField @@ -158,12 +149,10 @@ 'doctype': 'DocField', 'fieldname': 'stock_uom', 'fieldtype': 'Link', - 'idx': 12, 'label': 'Stock UOM', 'oldfieldname': 'stock_uom', 'oldfieldtype': 'Link', - 'options': 'UOM', - 'search_index': 0 + 'options': 'UOM' }, # DocField @@ -172,7 +161,6 @@ 'fieldname': 'flat_bom_no', 'fieldtype': 'Data', 'hidden': 1, - 'idx': 12, 'label': 'Flat BOM No', 'oldfieldname': 'flat_bom_no', 'oldfieldtype': 'Data' @@ -184,7 +172,6 @@ 'fieldname': 'bom_mat_no', 'fieldtype': 'Data', 'hidden': 0, - 'idx': 13, 'label': 'BOM Mat No', 'oldfieldname': 'bom_mat_no', 'oldfieldtype': 'Data' @@ -196,7 +183,6 @@ 'fieldname': 'parent_bom', 'fieldtype': 'Link', 'hidden': 0, - 'idx': 14, 'label': 'Parent BOM', 'oldfieldname': 'parent_bom', 'oldfieldtype': 'Link' @@ -207,11 +193,10 @@ 'doctype': 'DocField', 'fieldname': 'is_pro_applicable', 'fieldtype': 'Select', - 'idx': 15, 'label': 'Is PRO Applicable', 'oldfieldname': 'is_pro_applicable', 'oldfieldtype': 'Select', 'options': '\nYes\nNo', 'reqd': 0 } -] \ No newline at end of file +] diff --git a/erpnext/sandbox/test_stock_entry.py b/erpnext/sandbox/test_stock_entry.py index f1035bc455a..d1e00970c2d 100644 --- a/erpnext/sandbox/test_stock_entry.py +++ b/erpnext/sandbox/test_stock_entry.py @@ -101,7 +101,7 @@ class TestStockEntry(unittest.TestCase): self.save_stock_entry('Material Transfer') mtn = get_obj('Stock Entry', stock_entry.mtn[0].name, with_children=1) - tn = self.submit_stock_entry(mtn) + mtn = self.submit_stock_entry(mtn) # stock ledger entry print "Checking stock ledger entry........." @@ -202,8 +202,54 @@ class TestStockEntry(unittest.TestCase): [{'doctype': 'Serial No', 'item_code': 'it', 'warehouse': 'wh1', 'status': 'In Store', 'docstatus': 0}, 10] ]) + #=========================================================================== + def test_entries_on_same_datetime(self): + print "Test Case: Multiple entries on same datetime, cancel first one" + # submitted 1st MR + self.save_stock_entry('Material Receipt') + mr = get_obj('Stock Entry', stock_entry.mr[0].name, with_children=1) + mr = self.submit_stock_entry(mr) + + # submitted 2nd MR + for each in stock_entry.mr1: + each.save(1) + for t in stock_entry.mr1[1:]: + sql("update `tabStock Entry Detail` set parent = '%s' where name = '%s'" % (stock_entry.mr1[0].name, t.name)) + + mr1 = get_obj('Stock Entry', stock_entry.mr1[0].name, with_children=1) + mr1 = self.submit_stock_entry(mr1) + + # submitted MTN + self.save_stock_entry('Material Transfer') + mtn = get_obj('Stock Entry', stock_entry.mtn[0].name, with_children=1) + mtn = self.submit_stock_entry(mtn) + + # cancel prev MR + mr.on_cancel() + mr.doc.cancel_reason = "testing" + mr.doc.docstatus = 2 + mr.doc.save() + + + # stock ledger entry + print "Checking stock ledger entry........." + self.assertDoc(self.get_expected_sle('entries_on_same_datetime')) + + # bin qty + print "Checking Bin qty........." + self.assertDoc([ + {'doctype':'Bin', 'actual_qty':0, 'item_code':'it', 'warehouse':'wh1'}, + {'doctype':'Bin', 'actual_qty':5, 'item_code':'it', 'warehouse':'wh2'} + ]) + + # serial no + self.assertCount([ + [{'doctype': 'Serial No', 'item_code': 'it', 'warehouse': 'wh1', 'status': 'In Store', 'docstatus': 0}, 0], + [{'doctype': 'Serial No', 'item_code': 'it', 'warehouse': 'wh2', 'status': 'In Store', 'docstatus': 0}, 5] + ]) + #=========================================================================== def save_stock_entry(self, t): if t == 'Material Receipt': @@ -373,8 +419,58 @@ class TestStockEntry(unittest.TestCase): 'ifnull(bin_aqat, 0)': 0, 'ifnull(valuation_rate, 0)': 0, "ifnull(is_cancelled, 'No')": 'Yes' + }], + 'entries_on_same_datetime': [{ + 'doctype': 'Stock Ledger Entry', + 'item_code':'it', + 'warehouse':'wh1', + 'voucher_type': 'Stock Entry', + 'voucher_no': stock_entry.mr[0].name, + 'actual_qty': 10, + 'bin_aqat': 10, + 'valuation_rate': 100, + 'is_cancelled': 'Yes' + }, { + 'doctype': 'Stock Ledger Entry', + 'item_code':'it', + 'warehouse':'wh1', + 'voucher_type': 'Stock Entry', + 'voucher_no': stock_entry.mr[0].name, + 'actual_qty': -10, + 'ifnull(bin_aqat, 0)': 0, + 'ifnull(valuation_rate, 0)': 0, + "ifnull(is_cancelled, 'No')": 'Yes' + }, { + 'doctype': 'Stock Ledger Entry', + 'item_code':'it', + 'warehouse':'wh1', + 'voucher_type': 'Stock Entry', + 'voucher_no': stock_entry.mr1[0].name, + 'actual_qty': 5, + 'bin_aqat': 5, + 'valuation_rate': 400, + 'is_cancelled': 'No' + }, { + 'doctype': 'Stock Ledger Entry', + 'item_code':'it', + 'warehouse':'wh1', + 'voucher_type': 'Stock Entry', + 'voucher_no': stock_entry.mtn[0].name, + 'actual_qty': -5, + 'bin_aqat': 0, + 'valuation_rate': 400, + 'is_cancelled': 'No' + }, { + 'doctype': 'Stock Ledger Entry', + 'item_code':'it', + 'warehouse':'wh2', + 'voucher_type': 'Stock Entry', + 'voucher_no': stock_entry.mtn[0].name, + 'actual_qty': 5, + 'bin_aqat': 5, + 'valuation_rate': 100, + 'is_cancelled': 'No' }] - } return expected_sle[action] diff --git a/erpnext/sandbox/testdata/stock_entry.py b/erpnext/sandbox/testdata/stock_entry.py index 8f2a30c64af..3316016a439 100644 --- a/erpnext/sandbox/testdata/stock_entry.py +++ b/erpnext/sandbox/testdata/stock_entry.py @@ -34,6 +34,38 @@ mr = [ ) ] +mr1 = [ + Document( + fielddata = { + 'doctype': 'Stock Entry', + 'posting_date': '2011-09-01', + 'transfer_date': '2011-09-01', + 'posting_time': '12:00', + 'company': 'comp', + 'fiscal_year' : '2011-2012', + 'purpose': 'Material Receipt', + 'name': 'mr1' + } + ), + Document( + fielddata ={ + 'doctype': 'Stock Entry Detail', + 'parenttype': 'Stock Entry', + 'parentfield' : 'mtn_details', + 'parent' : 'mr1', + 'item_code' : 'it', + 't_warehouse' : 'wh1', + 'qty' : 5, + 'transfer_qty' : 5, + 'incoming_rate': 400, + 'stock_uom': 'Nos', + 'conversion_factor': 1, + 'serial_no': 'srno11, srno12, srno13, srno14, srno15' + } + ) +] + + # Material Transfer #-------------------- @@ -43,7 +75,7 @@ mtn = [ 'doctype': 'Stock Entry', 'posting_date': '2011-09-01', 'transfer_date': '2011-09-01', - 'posting_time': '13:00', + 'posting_time': '12:00', 'company': 'comp', 'fiscal_year' : '2011-2012', 'purpose': 'Material Transfer', diff --git a/erpnext/startup/startup.js b/erpnext/startup/startup.js index 3e2fde404ef..fcb097bd4ed 100644 --- a/erpnext/startup/startup.js +++ b/erpnext/startup/startup.js @@ -56,6 +56,14 @@ pscript.startup_make_sidebar = function() { // menu var ml = r.message; + // login-file + if(r.login_url){ + login_file = 'http://' + r.login_url; + } + else if(pscript.is_erpnext_saas) { + login_file = 'https://www.erpnext.com'; + } + // clear page_body.left_sidebar.innerHTML = ''; @@ -292,13 +300,6 @@ SidebarItem.prototype.show_items = function() { } } - if(r.login_url){ - login_file = 'http://' + r.login_url; - } - else if(pscript.is_erpnext_saas) { - login_file = 'https://www.erpnext.com'; - } - $(me.items_area).slideDown(); diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py index 13c0fee2cf4..e3af0a27894 100644 --- a/erpnext/stock/doctype/bin/bin.py +++ b/erpnext/stock/doctype/bin/bin.py @@ -1,18 +1,11 @@ # Please edit this list and import only required elements import webnotes -from webnotes.utils import add_days, add_months, add_years, cint, cstr, date_diff, default_fields, flt, fmt_money, formatdate, generate_hash, getTraceback, get_defaults, get_first_day, get_last_day, getdate, has_common, month_name, now, nowdate, replace_newlines, sendmail, set_default, str_esc_quote, user_format, validate_email_add -from webnotes.model import db_exists -from webnotes.model.doc import Document, addchild, removechild, getchildren, make_autoname, SuperDocType -from webnotes.model.doclist import getlist, copy_doclist -from webnotes.model.code import get_obj, get_server_obj, run_server_obj, updatedb, check_syntax -from webnotes import session, form, is_testing, msgprint, errprint +from webnotes.utils import cint, cstr, flt, nowdate +from webnotes.model.code import get_obj +from webnotes import msgprint -set = webnotes.conn.set sql = webnotes.conn.sql -get_value = webnotes.conn.get_value -in_transaction = webnotes.conn.in_transaction -convert_to_lists = webnotes.conn.convert_to_lists # ----------------------------------------------------------------------------------------- @@ -39,19 +32,13 @@ class DocType: self.doc.save() - # update valuation for post dated entry if actual_qty: # check actual qty with total number of serial no if serial_no: self.check_qty_with_serial_no() - - prev_sle = self.get_prev_sle(dt, posting_time, sle_id) - cqty = flt(prev_sle.get('bin_aqat', 0)) - # Block if actual qty becomes negative - if (flt(cqty) + flt(actual_qty)) < 0 and flt(actual_qty) < 0 and is_cancelled == 'No': - msgprint('Not enough quantity (requested: %s, current: %s) for Item %s in Warehouse %s as on %s %s' % (flt(actual_qty), flt(cqty), self.doc.item_code, self.doc.warehouse, dt, posting_time), raise_exception = 1) - - self.update_item_valuation(sle_id, dt, posting_time, serial_no, prev_sle) + + # update valuation and qty after transaction for post dated entry + self.update_entries_after(dt, posting_time) def check_qty_with_serial_no(self): """ @@ -59,11 +46,19 @@ class DocType: Temporary validation added on: 18-07-2011 """ if sql("select name from `tabItem` where ifnull(has_serial_no, 'No') = 'Yes' and name = '%s'" % self.doc.item_code): - sr_count = sql("select count(name) from `tabSerial No` where item_code = '%s' and warehouse = '%s' and status ='In Store' and docstatus != 2" % (self.doc.item_code, self.doc.warehouse))[0][0] + sr_count = sql("""select count(name) from `tabSerial No` + where item_code = '%s' and warehouse = '%s' + and status ='In Store' and docstatus != 2 + """ % (self.doc.item_code, self.doc.warehouse))[0][0] + if sr_count != self.doc.actual_qty: - msg = "Actual Qty(%s) in Bin is mismatched with total number(%s) of serial no in store for item: '%s' and warehouse: '%s'" % (self.doc.actual_qty, sr_count, self.doc.item_code, self.doc.warehouse) + msg = """Actual Qty(%s) in Bin is mismatched with total number(%s) of serial no in store + for item: '%s' and warehouse: '%s'""" % \ + (self.doc.actual_qty, sr_count, self.doc.item_code, self.doc.warehouse) + if getattr(webnotes.defs,'admin_email_notification',1): - sendmail(['developers@iwebnotes.com'], sender='automail@webnotestech.com', subject='Serial No Count vs Bin Actual Qty', parts=[['text/plain', msg]]) + sendmail(['developers@iwebnotes.com'], sender='automail@webnotestech.com', \ + subject='Serial No Count vs Bin Actual Qty', parts=[['text/plain', msg]]) msgprint(msg, raise_exception=1) # -------------------------------- @@ -81,29 +76,19 @@ class DocType: """, (self.doc.item_code, self.doc.warehouse), as_dict=1) return sle and sle[0] or None - # -------------------------------- - # get previous stock ledger entry - # -------------------------------- - - def get_prev_sle(self, posting_date, posting_time, sle_id = ''): - # this function will only be called for a live entry - # for which the "name" will be the latest (even for the same timestamp) - # and even for a back-dated entry - # hence there cannot be any "backdated entries" with a name greater than the - # current one + def get_prev_sle(self, posting_date = '1900-01-01', posting_time = '12:00', sle_id = ''): + """ + get the last sle on or before the current time-bucket, + to get actual qty before transaction, this function + is called from various transaction like stock entry, reco etc + """ - # if there are multiple entries on this timestamp, then the last one will be with - # the last "name" - # else, the last entry will be the highest name at the previous timestamp - # hence, the double sort on timestamp and name should be sufficient condition - # to get the last sle - sle = sql(""" select * from `tabStock Ledger Entry` where item_code = %s and warehouse = %s - and name != %s and ifnull(is_cancelled, 'No') = 'No' + and name != %s and timestamp(posting_date, posting_time) <= timestamp(%s, %s) order by timestamp(posting_date, posting_time) desc, name desc limit 1 @@ -112,25 +97,49 @@ class DocType: return sle and sle[0] or {} + def get_sle_prev_timebucket(self, posting_date = '1900-01-01', posting_time = '12:00'): + """get previous stock ledger entry before current time-bucket""" + # get the last sle before the current time-bucket, so that all values + # are reposted from the current time-bucket onwards. + # this is necessary because at the time of cancellation, there may be + # entries between the cancelled entries in the same time-bucket + + sle = sql(""" + select * from `tabStock Ledger Entry` + where item_code = %s + and warehouse = %s + and ifnull(is_cancelled, 'No') = 'No' + and timestamp(posting_date, posting_time) < timestamp(%s, %s) + order by timestamp(posting_date, posting_time) desc, name desc + limit 1 + """, (self.doc.item_code, self.doc.warehouse, posting_date, posting_time), as_dict=1) + + return sle and sle[0] or {} - # -------------------------------------------------------------------------------------------------------------------------------------- - # validate negative stock (validate if stock is going -ve in between for back dated entries will consider only is_cancel = 'No' entries) - # -------------------------------------------------------------------------------------------------------------------------------------- + #------------------------------------------------------------- def validate_negative_stock(self, cqty, s): + """ + validate negative stock for entries current datetime onwards + will not consider cancelled entries + """ diff = cqty + s['actual_qty'] - if diff < 0 and (abs(diff) > 0.0001) and s['is_cancelled'] != 'Yes': + if diff < 0 and (abs(diff) > 0.0001) and s['is_cancelled'] == 'No': msgprint(""" + Negative stock error: Cannot complete this transaction because stock will - become negative (%s) in future transaction for Item - %s in Warehouse %s on %s %s""" % \ - (str(diff), self.doc.item_code, self.doc.warehouse, - s['posting_date'], s['posting_time']), raise_exception=1) + become negative (%s) for Item %s in Warehouse + %s on %s %s in Transaction %s %s""" % \ + (str(diff), self.doc.item_code, self.doc.warehouse, \ + s['posting_date'], s['posting_time'], s['voucher_type'], s['voucher_no']), \ + raise_exception=1) + - # ------------------------------------ - # get serialized inventory values # ------------------------------------ def get_serialized_inventory_values(self, val_rate, in_rate, opening_qty, actual_qty, is_cancelled, serial_nos): + """ + get serialized inventory values + """ if flt(in_rate) < 0: # wrong incoming rate in_rate = val_rate elif flt(in_rate) == 0: # In case of delivery/stock issue, get average purchase rate of serial nos of current entry @@ -222,11 +231,16 @@ class DocType: stock_val = sum([flt(d[0])*flt(d[1]) for d in self.fcfs_bal]) return stock_val - # ---------------------- - # update item valuation - # ---------------------- - def update_item_valuation(self, sle_id=None, posting_date=None, posting_time=None, serial_no=None, prev_sle=None): - # no sle given, start from the first one (for repost) + def update_entries_after(self, posting_date, posting_time): + """ + update valution rate and qty after transaction + from the current time-bucket onwards + """ + + # Get prev sle + prev_sle = self.get_sle_prev_timebucket(posting_date, posting_time) + + # if no prev sle, start from the first one (for repost) if not prev_sle: cqty, cval, val_rate, self.fcfs_bal = 0, 0, 0, [] @@ -237,11 +251,11 @@ class DocType: val_rate = flt(prev_sle.get('valuation_rate', 0)) self.fcfs_bal = eval(prev_sle.get('fcfs_stack', '[]') or '[]') - val_method = get_obj('Valuation Control').get_valuation_method(self.doc.item_code) # get valuation method + # get valuation method + val_method = get_obj('Valuation Control').get_valuation_method(self.doc.item_code) # recalculate the balances for all stock ledger entries - # after this one (so that the corrected balance will reflect - # correctly in all entries after this one) + # after the prev sle sll = sql(""" select * from `tabStock Ledger Entry` @@ -250,30 +264,32 @@ class DocType: and ifnull(is_cancelled, 'No') = 'No' and timestamp(posting_date, posting_time) > timestamp(%s, %s) order by timestamp(posting_date, posting_time) asc, name asc""", \ - (self.doc.item_code, self.doc.warehouse, posting_date, posting_time), as_dict = 1) + (self.doc.item_code, self.doc.warehouse, \ + prev_sle.get('posting_date','1900-01-01'), prev_sle.get('posting_time', '12:00')), as_dict = 1) - # if in live entry - update the values of the current sle - if sle_id: - sll = sql("select * from `tabStock Ledger Entry` where name=%s and ifnull(is_cancelled, 'No') = 'No'", sle_id, as_dict=1) + sll - for s in sll: + for sle in sll: # block if stock level goes negative on any date - self.validate_negative_stock(cqty, s) + self.validate_negative_stock(cqty, sle) - stock_val, in_rate = 0, s['incoming_rate'] # IN - serial_nos = s["serial_no"] and ("'"+"', '".join(cstr(s["serial_no"]).split('\n')) + "'") or '' + stock_val, in_rate = 0, sle['incoming_rate'] # IN + serial_nos = sle["serial_no"] and ("'"+"', '".join(cstr(sle["serial_no"]).split('\n')) \ + + "'") or '' # Get valuation rate - val_rate, stock_val = self.get_valuation_rate(val_method, serial_nos, val_rate, in_rate, stock_val, cqty, s) + val_rate, stock_val = self.get_valuation_rate(val_method, serial_nos, \ + val_rate, in_rate, stock_val, cqty, sle) # Qty upto the sle - cqty += s['actual_qty'] + cqty += sle['actual_qty'] # Stock Value upto the sle stock_val = self.get_stock_value(val_method, cqty, stock_val, serial_nos) - # update current sle --> will it be good to update incoming rate in sle for outgoing stock entry????? + + # update current sle --> will it be good to update incoming rate in sle + # for outgoing stock entry????? sql("""update `tabStock Ledger Entry` set bin_aqat=%s, valuation_rate=%s, fcfs_stack=%s, stock_value=%s - where name=%s""", (cqty, flt(val_rate), cstr(self.fcfs_bal), stock_val, s['name'])) + where name=%s""", (cqty, flt(val_rate), cstr(self.fcfs_bal), stock_val, sle['name'])) # update the bin if sll: diff --git a/erpnext/stock/doctype/landed_cost_wizard/landed_cost_wizard.py b/erpnext/stock/doctype/landed_cost_wizard/landed_cost_wizard.py index 58ba751f43e..e1d57746d6a 100644 --- a/erpnext/stock/doctype/landed_cost_wizard/landed_cost_wizard.py +++ b/erpnext/stock/doctype/landed_cost_wizard/landed_cost_wizard.py @@ -86,9 +86,9 @@ class DocType: d.save() sql("update `tabStock Ledger Entry` set incoming_rate = '%s' where voucher_detail_no = '%s'"%(flt(d.valuation_rate), d.name)) - bin_name = sql("select t1.name, t2.name, t2.posting_date, t2.posting_time from `tabBin` t1, `tabStock Ledger Entry` t2 where t2.voucher_detail_no = '%s' and t2.item_code = t1.item_code and t2.warehouse = t1.warehouse LIMIT 1"%(d.name)) + bin_name = sql("select t1.name, t2.posting_date, t2.posting_time from `tabBin` t1, `tabStock Ledger Entry` t2 where t2.voucher_detail_no = '%s' and t2.item_code = t1.item_code and t2.warehouse = t1.warehouse LIMIT 1"%(d.name)) if bin_name and bin_name[0][0]: - obj = get_obj('Bin', bin_name[0][0]).update_item_valuation(bin_name[0][1], bin_name[0][2], bin_name[0][3]) + obj = get_obj('Bin', bin_name[0][0]).update_entries_after(bin_name[0][1], bin_name[0][2]) # now distribute the taxes among the PRs for lc in getlist(self.doclist, 'landed_cost_details'): @@ -140,11 +140,11 @@ class DocType: d.valuation_rate = (flt(d.purchase_rate) + (flt(d.rm_supp_cost) / flt(d.qty)) + (flt(d.item_tax_amount)/flt(d.qty))) / flt(d.conversion_factor) d.save() sql("update `tabStock Ledger Entry` set incoming_rate = '%s' where voucher_detail_no = '%s'"%(flt(d.valuation_rate), d.name)) - bin_name = sql("select t1.name, t2.name, t2.posting_date, t2.posting_time from `tabBin` t1, `tabStock Ledger Entry` t2 where t2.voucher_detail_no = '%s' and t2.item_code = t1.item_code and t2.warehouse = t1.warehouse LIMIT 1"%(d.name)) + bin_name = sql("select t1.name, t2.posting_date, t2.posting_time from `tabBin` t1, `tabStock Ledger Entry` t2 where t2.voucher_detail_no = '%s' and t2.item_code = t1.item_code and t2.warehouse = t1.warehouse LIMIT 1"%(d.name)) # update valuation of the item if bin_name and bin_name[0][0]: - obj = get_obj('Bin', bin_name[0][0]).update_item_valuation(bin_name[0][1], bin_name[0][2], bin_name[0][3]) + obj = get_obj('Bin', bin_name[0][0]).update_entries_after(bin_name[0][1], bin_name[0][2]) def add_deduct_taxes(self, ocd, oc, tax_amount, total_amount, total, prev_total, f=1): ocd[oc].total_amount = flt(tax_amount.toFixed(2)) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 2c36d0f1b34..208dd288f45 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -121,19 +121,46 @@ class DocType: self.item_dict[i[0]] = [flt(i[1]), cstr(i[2]), cstr(i[3])] def get_raw_materials(self,pro_obj): - # get all items from flat bom except, child items of sub-contracted and sub assembly items and sub assembly items itself. -# flat_bom_items = sql("select item_code, ifnull(sum(qty_consumed_per_unit), 0) * '%s', description, stock_uom from `tabFlat BOM Detail` where parent = '%s' and parent_bom = '%s' and is_pro_applicable = 'No' and docstatus < 2 group by item_code" % ((self.doc.process == 'Backflush') and flt(self.doc.fg_completed_qty) or flt(pro_obj.doc.qty), cstr(pro_obj.doc.bom_no), cstr(pro_obj.doc.bom_no))) -# self.make_items_dict(flat_bom_items) + """ + get all items from flat bom except + child items of sub-contracted and sub assembly items + and sub assembly items itself. + """ if pro_obj.doc.consider_sa_items == 'Yes': - # get all Sub Assembly items only from flat bom - fl_bom_sa_items = sql("select item_code, ifnull(sum(qty_consumed_per_unit), 0) * '%s', description, stock_uom from `tabFlat BOM Detail` where parent = '%s' and parent_bom = '%s' and is_pro_applicable = 'Yes' and docstatus < 2 group by item_code" % ((self.doc.process == 'Backflush') and flt(self.doc.fg_completed_qty) or flt(pro_obj.doc.qty), cstr(pro_obj.doc.bom_no), cstr(pro_obj.doc.bom_no))) + # Get all raw materials considering SA items as raw materials, + # so no childs of SA items + + fl_bom_sa_items = sql(""" + select item_code, ifnull(sum(qty_consumed_per_unit), 0) * '%s', description, stock_uom + from `tabBOM Material` + where parent = '%s' and docstatus < 2 + group by item_code + """ % ((self.doc.process == 'Backflush') and flt(self.doc.fg_completed_qty) \ + or flt(pro_obj.doc.qty), cstr(pro_obj.doc.bom_no))) + self.make_items_dict(fl_bom_sa_items) if pro_obj.doc.consider_sa_items == 'No': - # get all sub assembly childs only from flat bom - #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 `tabFlat BOM Detail` fb,`tabBOM Material` bm where bm.parent=fb.parent_bom and bm.docstatus<2 and fb.is_pro_applicable='Yes' and fb.docstatus<2 and fb.parent='%s' and bm.bom_no is null)a group by item_code,stock_uom - 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 `tabFlat BOM Detail` fb,`tabBOM Material` bm where bm.parent=fb.parent_bom and bm.docstatus<2 and fb.is_pro_applicable='Yes' and fb.docstatus<2 and fb.parent='%s' and bm.bom_no is null)a group by item_code,stock_uom" % ((self.doc.process == 'Backflush') and flt(self.doc.fg_completed_qty) or flt(pro_obj.doc.qty), cstr(pro_obj.doc.bom_no))) + # 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 `tabFlat BOM Detail` 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.process == 'Backflush') and flt(self.doc.fg_completed_qty) \ + or flt(pro_obj.doc.qty), cstr(pro_obj.doc.bom_no))) + + + self.make_items_dict(fl_bom_sa_child_item) def add_to_stock_entry_detail(self, pro_obj, item_dict, fg_item = 0): @@ -179,11 +206,10 @@ class DocType: if flt(d.transfer_qty) <= 0: msgprint("Transfer Quantity can not be less than or equal to zero at Row No " + cstr(d.idx)) raise Exception - if d.s_warehouse: - if flt(d.transfer_qty) > flt(d.actual_qty): - msgprint("Transfer Quantity is more than Available Qty at Row No " + cstr(d.idx)) - raise Exception - + if d.s_warehouse and flt(d.transfer_qty) > flt(d.actual_qty): + msgprint("Transfer Quantity is more than Available Qty at Row No " + cstr(d.idx)) + raise Exception + def calc_amount(self): total_amount = 0 for d in getlist(self.doclist, 'mtn_details'): diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index f040df38392..e133d6fad30 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -131,18 +131,18 @@ class DocType: update item valuation in previous date and also on post date if no qty diff """ - self.update_item_valuation_pre_date(d) + self.update_entries_pre_date(d) if not flt(d[self.label['qty']]) and not flt(d[self.label['actual_qty']]): # seems like a special condition when there is no actual quanitity but there is a rate, may be only for setting a rate! self.make_sl_entry(1,d,1) self.make_sl_entry(1,d,-1) elif not qty_diff: - self.update_item_valuation_post_date(d) + self.update_entries_post_date(d) # update valuation rate as csv file in all sle before reconciliation date # ------------------------------------------------------------------------ - def update_item_valuation_pre_date(self, d): + def update_entries_pre_date(self, d): mar = flt(d[self.label['mar']]) # previous sle @@ -168,15 +168,12 @@ class DocType: # Update item valuation in all sle after the reconcliation date # --------------------------------------------------------- - def update_item_valuation_post_date(self, d): + def update_entries_post_date(self, d): bin = sql("select name from `tabBin` where item_code = '%s' and warehouse = '%s'" % (d[self.label['item_code']], d[self.label['warehouse']])) bin_obj = get_obj('Bin', bin[0][0]) - # prev sle - prev_sle = bin_obj.get_prev_sle(self.doc.reconciliation_date,self.doc.reconciliation_time) - # update valuation in sle posted after reconciliation datetime - bin_obj.update_item_valuation(posting_date = self.doc.reconciliation_date, posting_time = self.doc.reconciliation_time, prev_sle = prev_sle) + bin_obj.update_entries_after(posting_date = self.doc.reconciliation_date, posting_time = self.doc.reconciliation_time) # -------------- # make sl entry diff --git a/erpnext/stock/doctype/stock_uom_replace_utility/stock_uom_replace_utility.py b/erpnext/stock/doctype/stock_uom_replace_utility/stock_uom_replace_utility.py index f8ac7372a52..0fa7a3bf06f 100644 --- a/erpnext/stock/doctype/stock_uom_replace_utility/stock_uom_replace_utility.py +++ b/erpnext/stock/doctype/stock_uom_replace_utility/stock_uom_replace_utility.py @@ -85,7 +85,7 @@ class DocType: for w in wh: bin = sql("select name from `tabBin` where item_code = '%s' and warehouse = '%s'" % (self.doc.item_code, w[0])) if bin and bin[0][0]: - get_obj("Bin", bin[0][0]).update_item_valuation(sle_id = '', posting_date = '', posting_time = '') + get_obj("Bin", bin[0][0]).update_entries_after(posting_date = '', posting_time = '') # acknowledge user msgprint("Item Valuation Updated Successfully.") diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py index f3d600de133..b59ce753971 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.py +++ b/erpnext/stock/doctype/warehouse/warehouse.py @@ -68,7 +68,7 @@ class DocType: bl = sql("select name from tabBin where warehouse=%s", self.doc.name) for b in bl: bobj = get_obj('Bin',b[0]) - bobj.update_item_valuation(posting_date = '2000-01-01', posting_time = '12:00') + bobj.update_entries_after(posting_date = '0000-00-00', posting_time = '00:00') sql("COMMIT") sql("START TRANSACTION") diff --git a/erpnext/utilities/doctype/reposting_tool/reposting_tool.py b/erpnext/utilities/doctype/reposting_tool/reposting_tool.py index c4f021b2979..489a3b67782 100644 --- a/erpnext/utilities/doctype/reposting_tool/reposting_tool.py +++ b/erpnext/utilities/doctype/reposting_tool/reposting_tool.py @@ -88,7 +88,7 @@ class DocType: bin_act_qty = flt(bin_obj.doc.actual_qty) try: # udpate actual qty and item valuation - bin_obj.update_item_valuation('', '2000-01-01', '00:00') + bin_obj.update_entries_after('0000-00-00', '00:00') # get bin qty qty_dict = self.get_bin_qty(bin_obj.doc.warehouse, bin_obj.doc.item_code) diff --git a/index.html b/index.html index 4ab0b9bd555..c28b0aa3a9a 100644 --- a/index.html +++ b/index.html @@ -79,4 +79,4 @@ throw new SyntaxError('JSON.parse');};}}());wn.versions.check();wn.require("lib/
- \ No newline at end of file +