From 74f1480e6ee12d9c7ab92c2bffc2bb60504997b4 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 24 Sep 2012 11:23:05 +0530 Subject: [PATCH 1/6] updated incoming_rate in outgoing stock ledger entry --- .../gross_profit/gross_profit.py | 97 ++++++----- erpnext/stock/doctype/bin/bin.py | 156 +++++++++--------- .../stock_reconciliation.py | 2 +- 3 files changed, 136 insertions(+), 119 deletions(-) diff --git a/erpnext/selling/search_criteria/gross_profit/gross_profit.py b/erpnext/selling/search_criteria/gross_profit/gross_profit.py index 97821d3aec..e7f81abeb0 100644 --- a/erpnext/selling/search_criteria/gross_profit/gross_profit.py +++ b/erpnext/selling/search_criteria/gross_profit/gross_profit.py @@ -8,11 +8,11 @@ # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# along with this program. If not, see . # Add Columns # ------------ @@ -24,48 +24,71 @@ colnames[colnames.index('Amount*')] = 'Amount' col_idx['Amount'] = col_idx['Amount*'] col_idx.pop('Amount*') -columns = [['Valuation Rate','Currency','150px',''], - ['Valuation Amount','Currency','150px',''], - ['Gross Profit (%)','Currrency','150px',''], - ['Gross Profit','Currency','150px','']] +columns = [ + ['Purchase Cost','Currency','150px',''], + ['Gross Profit (%)','Currrency','150px',''], + ['Gross Profit','Currency','150px',''] +] for c in columns: - colnames.append(c[0]) - coltypes.append(c[1]) - colwidths.append(c[2]) - coloptions.append(c[3]) - col_idx[c[0]] = len(colnames)-1 + colnames.append(c[0]) + coltypes.append(c[1]) + colwidths.append(c[2]) + coloptions.append(c[3]) + col_idx[c[0]] = len(colnames)-1 -out, tot_amount, tot_val_amount, tot_gross_profit = [], 0, 0, 0 +#out, tot_amount, tot_val_amount, tot_gross_profit = [], 0, 0, 0 +sle = sql(""" + select + item_code, warehouse, posting_date, posting_time, + actual_qty, stock_value, voucher_no, voucher_type + from + `tabStock Ledger Entry` + where + ifnull(is_cancelled, 'No') = 'No' + order by posting_date desc, posting_time desc, name desc + force index posting_sort_index +""", as_dict=1) + +def get_purchase_cost(row): + stock_value = stock_val_after = 0 + item_warehouse = + for d in sle: + if d['voucher_type'] == 'Delivery Note' and d['voucher_no'] == row[col_idx['ID']]: + stock_val_after += flt(d['stock_value']) + for r in res: - tot_val_rate = 0 - packing_list_items = sql("select item_code, warehouse, qty from `tabDelivery Note Packing Item` where parent = %s and parent_item = %s", (r[col_idx['ID']], r[col_idx['Item Code']])) - for d in packing_list_items: - if d[1]: - val_rate = sql("select valuation_rate from `tabStock Ledger Entry` where item_code = %s and warehouse = %s and voucher_type = 'Delivery Note' and voucher_no = %s and is_cancelled = 'No'", (d[0], d[1], r[col_idx['ID']])) - val_rate = val_rate and val_rate[0][0] or 0 - if r[col_idx['Quantity']]: tot_val_rate += (flt(val_rate) * flt(d[2]) / flt(r[col_idx['Quantity']])) - else: tot_val_rate = 0 + r.append(get_purchase_cost(r)) - r.append(fmt_money(tot_val_rate)) - - val_amount = flt(tot_val_rate) * flt(r[col_idx['Quantity']]) - r.append(fmt_money(val_amount)) - - gp = flt(r[col_idx['Amount']]) - flt(val_amount) - - if val_amount: gp_percent = gp * 100 / val_amount - else: gp_percent = gp - - r.append(fmt_money(gp_percent)) - r.append(fmt_money(gp)) - out.append(r) - - tot_gross_profit += flt(gp) - tot_amount += flt(r[col_idx['Amount']]) - tot_val_amount += flt(val_amount) + # tot_val_rate = 0 + # packing_list_items = sql("select item_code, warehouse, qty from `tabDelivery Note Packing Item` where parent = %s and parent_item = %s", (r[col_idx['ID']], r[col_idx['Item Code']])) + # for d in packing_list_items: + # if d[1]: + # val_rate = sql("select valuation_rate from `tabStock Ledger Entry` where item_code = %s and warehouse = %s and voucher_type = 'Delivery Note' and voucher_no = %s and is_cancelled = 'No'", (d[0], d[1], r[col_idx['ID']])) + # val_rate = val_rate and val_rate[0][0] or 0 + # if r[col_idx['Quantity']]: tot_val_rate += (flt(val_rate) * flt(d[2]) / flt(r[col_idx['Quantity']])) + # else: tot_val_rate = 0 + # + # r.append(fmt_money(tot_val_rate)) + # + # val_amount = flt(tot_val_rate) * flt(r[col_idx['Quantity']]) + # r.append(fmt_money(val_amount)) + # + # gp = flt(r[col_idx['Amount']]) - flt(val_amount) + # + # if val_amount: gp_percent = gp * 100 / val_amount + # else: gp_percent = gp + # + # r.append(fmt_money(gp_percent)) + # r.append(fmt_money(gp)) + # out.append(r) + # + # tot_gross_profit += flt(gp) + # tot_amount += flt(r[col_idx['Amount']]) + # tot_val_amount += flt(val_amount) + # # Add Total Row # -------------- l_row = ['' for i in range(len(colnames))] diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py index 348ec13fc7..3430badfb6 100644 --- a/erpnext/stock/doctype/bin/bin.py +++ b/erpnext/stock/doctype/bin/bin.py @@ -40,10 +40,9 @@ class DocType: self.doc = doc self.doclist = doclist - # ------------- - # stock update - # ------------- - def update_stock(self, actual_qty=0, reserved_qty=0, ordered_qty=0, indented_qty=0, planned_qty=0, dt=None, sle_id='', posting_time='', serial_no = '', is_cancelled = 'No',doc_type='',doc_name='',is_amended='No'): + def update_stock(self, actual_qty=0, reserved_qty=0, ordered_qty=0, indented_qty=0, \ + planned_qty=0, dt=None, sle_id='', posting_time='', serial_no = '', \ + is_cancelled = 'No',doc_type='',doc_name='',is_amended='No'): if not dt: dt = nowdate() @@ -73,13 +72,13 @@ class DocType: """ 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] + 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'""" % \ + 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) msgprint(msg, raise_exception=1) @@ -119,7 +118,6 @@ 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 @@ -139,8 +137,6 @@ class DocType: return sle and sle[0] or {} - - #------------------------------------------------------------- def validate_negative_stock(self, cqty, s): """ validate negative stock for entries current datetime onwards @@ -157,33 +153,32 @@ class DocType: s['posting_date'], s['posting_time'], s['voucher_type'], s['voucher_no']), \ raise_exception=1) - - # ------------------------------------ - def get_serialized_inventory_values(self, val_rate, in_rate, opening_qty, actual_qty, is_cancelled, serial_nos): + 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 - in_rate = flt(sql("select ifnull(avg(purchase_rate), 0) from `tabSerial No` where name in (%s)" % (serial_nos))[0][0]) + elif flt(in_rate) == 0 or flt(actual_qty) < 0: + # In case of delivery/stock issue, get average purchase rate + # of serial nos of current entry + in_rate = flt(sql("""select ifnull(avg(purchase_rate), 0) + from `tabSerial No` where name in (%s)""" % (serial_nos))[0][0]) if in_rate and val_rate == 0: # First entry val_rate = in_rate # val_rate is same as previous entry if val_rate is negative # Otherwise it will be calculated as per moving average - elif opening_qty + actual_qty > 0 and ((opening_qty * val_rate) + (actual_qty * in_rate)) > 0: - val_rate = ((opening_qty *val_rate) + (actual_qty * in_rate)) / (opening_qty + actual_qty) - stock_val = val_rate - return val_rate, stock_val + elif opening_qty + actual_qty > 0 and ((opening_qty * val_rate) + \ + (actual_qty * in_rate)) > 0: + val_rate = ((opening_qty *val_rate) + (actual_qty * in_rate)) / \ + (opening_qty + actual_qty) + return val_rate, in_rate - - - # ------------------------------------ - # get moving average inventory values - # ------------------------------------ def get_moving_average_inventory_values(self, val_rate, in_rate, opening_qty, actual_qty, is_cancelled): - if flt(in_rate) == 0: # In case of delivery/stock issue in_rate = 0 or wrong incoming rate + if flt(in_rate) == 0 or flt(actual_qty) < 0: + # In case of delivery/stock issue in_rate = 0 or wrong incoming rate in_rate = val_rate # val_rate is same as previous entry if : @@ -191,18 +186,15 @@ class DocType: # 2. cancelled entry # 3. val_rate is negative # Otherwise it will be calculated as per moving average - if actual_qty > 0 and (opening_qty + actual_qty) > 0 and is_cancelled == 'No' and ((opening_qty * val_rate) + (actual_qty * in_rate)) > 0: + if actual_qty > 0 and (opening_qty + actual_qty) > 0 and is_cancelled == 'No' \ + and ((opening_qty * val_rate) + (actual_qty * in_rate)) > 0: opening_qty = opening_qty > 0 and opening_qty or 0 - val_rate = ((opening_qty *val_rate) + (actual_qty * in_rate)) / (opening_qty + actual_qty) + val_rate = ((opening_qty *val_rate) + (actual_qty * in_rate)) / \ + (opening_qty + actual_qty) elif (opening_qty + actual_qty) <= 0: val_rate = 0 - stock_val = val_rate - return val_rate, stock_val + return val_rate, in_rate - - # -------------------------- - # get fifo inventory values - # -------------------------- def get_fifo_inventory_values(self, in_rate, actual_qty): # add batch to fcfs balance if actual_qty > 0: @@ -210,49 +202,52 @@ class DocType: # remove from fcfs balance else: + incoming_cost = 0 withdraw = flt(abs(actual_qty)) while withdraw: if not self.fcfs_bal: break # nothing in store batch = self.fcfs_bal[0] - + incoming_cost += flt(batch[1])*flt(withdraw) + if batch[0] <= withdraw: # not enough or exactly same qty in current batch, clear batch withdraw -= batch[0] self.fcfs_bal.pop(0) + else: # all from current batch batch[0] -= withdraw withdraw = 0 + + in_rate = incoming_cost / flt(abs(actual_qty)) fcfs_val = sum([flt(d[0])*flt(d[1]) for d in self.fcfs_bal]) fcfs_qty = sum([flt(d[0]) for d in self.fcfs_bal]) val_rate = fcfs_qty and fcfs_val / fcfs_qty or 0 - return val_rate + return val_rate, in_rate - # ------------------- - # get valuation rate - # ------------------- def get_valuation_rate(self, val_method, serial_nos, val_rate, in_rate, stock_val, cqty, s): if serial_nos: - val_rate, stock_val = self.get_serialized_inventory_values(val_rate, in_rate, opening_qty = cqty, actual_qty = s['actual_qty'], is_cancelled = s['is_cancelled'], serial_nos = serial_nos) + val_rate, in_rate = self.get_serialized_inventory_values( \ + val_rate, in_rate, opening_qty = cqty, actual_qty = s['actual_qty'], \ + is_cancelled = s['is_cancelled'], serial_nos = serial_nos) elif val_method == 'Moving Average': - val_rate, stock_val = self.get_moving_average_inventory_values(val_rate, in_rate, opening_qty = cqty, actual_qty = s['actual_qty'], is_cancelled = s['is_cancelled']) + val_rate, in_rate = self.get_moving_average_inventory_values( \ + val_rate, in_rate, opening_qty = cqty, actual_qty = s['actual_qty'], \ + is_cancelled = s['is_cancelled']) elif val_method == 'FIFO': - val_rate = self.get_fifo_inventory_values(in_rate, actual_qty = s['actual_qty']) - return val_rate, stock_val + val_rate, in_rate = self.get_fifo_inventory_values(in_rate, \ + actual_qty = s['actual_qty']) + return val_rate, in_rate - - # ---------------- - # get stock value - # ---------------- - def get_stock_value(self, val_method, cqty, stock_val, serial_nos): + def get_stock_value(self, val_method, cqty, val_rate, serial_nos): if serial_nos: - stock_val = flt(stock_val) * flt(cqty) + stock_val = flt(val_rate) * flt(cqty) elif val_method == 'Moving Average': - stock_val = flt(cqty) > 0 and flt(stock_val) * flt(cqty) or 0 + stock_val = flt(cqty) > 0 and flt(val_rate) * flt(cqty) or 0 elif val_method == 'FIFO': stock_val = sum([flt(d[0])*flt(d[1]) for d in self.fcfs_bal]) return stock_val @@ -296,7 +291,9 @@ class DocType: 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, \ - prev_sle.get('posting_date','1900-01-01'), prev_sle.get('posting_time', '12:00')), as_dict = 1) + prev_sle.get('posting_date','1900-01-01'), \ + prev_sle.get('posting_time', '12:00')), as_dict = 1) + for sle in sll: # block if stock level goes negative on any date if val_method != 'Moving Average' or flt(allow_negative_stock) == 0: @@ -305,36 +302,35 @@ class DocType: 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, sle) - + val_rate, in_rate = self.get_valuation_rate(val_method, serial_nos, \ + val_rate, in_rate, stock_val, cqty, sle) # Qty upto the sle cqty += sle['actual_qty'] - # Stock Value upto the sle - stock_val = self.get_stock_value(val_method, cqty, stock_val, serial_nos) + stock_val = self.get_stock_value(val_method, cqty, val_rate, serial_nos) - # update current sle --> will it be good to update incoming rate in sle - # for outgoing stock entry????? + # update current sle 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, sle['name'])) + set bin_aqat=%s, valuation_rate=%s, fcfs_stack=%s, stock_value=%s, incoming_rate + where name=%s""", (cqty, flt(val_rate), cstr(self.fcfs_bal), \ + stock_val, in_rate, sle['name'])) # update the bin if sll or not prev_sle: - sql("update `tabBin` set valuation_rate=%s, actual_qty=%s, stock_value = %s, projected_qty = (actual_qty + indented_qty + ordered_qty + planned_qty - reserved_qty) where name=%s", \ - (flt(val_rate), cqty, flt(stock_val), self.doc.name)) - - + sql("""update `tabBin` set valuation_rate=%s, actual_qty=%s, stock_value = %s, + projected_qty = (actual_qty + indented_qty + ordered_qty + planned_qty - + reserved_qty) where name=%s + """, (flt(val_rate), cqty, flt(stock_val), self.doc.name)) def reorder_item(self,doc_type,doc_name): """ Reorder item if stock reaches reorder level""" if get_value('Global Defaults', None, 'auto_indent'): #check if re-order is required - ret = sql("select re_order_level, item_name, description, brand, item_group, lead_time_days, min_order_qty, email_notify, re_order_qty from tabItem where name = %s", (self.doc.item_code), as_dict=1) + ret = sql("""select re_order_level, item_name, description, brand, item_group, + lead_time_days, min_order_qty, email_notify, re_order_qty + from tabItem where name = %s""", (self.doc.item_code), as_dict=1) current_qty = sql(""" select sum(t1.actual_qty) + sum(t1.indented_qty) + sum(t1.ordered_qty) -sum(t1.reserved_qty) @@ -349,17 +345,16 @@ class DocType: (flt(ret[0]['re_order_level']) > flt(current_qty[0][0])): self.create_auto_indent(ret[0], doc_type, doc_name, current_qty[0][0]) - - def create_auto_indent(self, i , doc_type, doc_name, cur_qty): - """ Create indent on reaching reorder level """ - + """ Create indent on reaching reorder level """\ indent = Document('Purchase Request') indent.transaction_date = nowdate() indent.naming_series = 'IDT' indent.company = get_defaults()['company'] indent.fiscal_year = get_defaults()['fiscal_year'] - indent.remark = "This is an auto generated Purchase Request. It was raised because the (actual + ordered + indented - reserved) quantity reaches re-order level when %s %s was created"%(doc_type,doc_name) + indent.remark = """This is an auto generated Purchase Request. + It was raised because the (actual + ordered + indented - reserved) quantity + reaches re-order level when %s %s was created""" % (doc_type,doc_name) indent.save(1) indent_obj = get_obj('Purchase Request',indent.name,with_children=1) indent_details_child = addchild(indent_obj.doc,'indent_details','Purchase Request Item',0) @@ -377,27 +372,26 @@ class DocType: indent_obj.validate() set(indent_obj.doc,'docstatus',1) indent_obj.on_submit() - msgprint("Item: " + self.doc.item_code + " is to be re-ordered. Purchase Request %s raised. It was generated from %s %s"%(indent.name,doc_type, doc_name )) + msgprint("""Item: is to be re-ordered. Purchase Request %s raised. + It was generated from %s %s""" % + (self.doc.item_code, indent.name,doc_type, doc_name )) if(i['email_notify']): self.send_email_notification(doc_type,doc_name) - - def send_email_notification(self,doc_type,doc_name): """ Notify user about auto creation of indent""" from webnotes.utils.email_lib import sendmail - email_list=[d[0] for d in sql("select parent from tabUserRole where role in ('Purchase Manager','Material Manager') and parent not in ('Administrator', 'All', 'Guest')")] - msg='A Purchase Request has been raised for item %s: %s on %s '%(doc_type, doc_name, nowdate()) + email_list=[d[0] for d in sql("""select parent from tabUserRole + where role in ('Purchase Manager','Material Manager') + and parent not in ('Administrator', 'All', 'Guest')""")] + msg="""A Purchase Request has been raised + for item %s: %s on %s """ % (doc_type, doc_name, nowdate()) sendmail(email_list, subject='Auto Purchase Request Generation Notification', msg = msg) - - def validate(self): self.validate_mandatory() - - # set defaults in bin def validate_mandatory(self): qf = ['actual_qty', 'reserved_qty', 'ordered_qty', 'indented_qty'] for f in qf: diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index d922a11b45..5416ff80d2 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -226,7 +226,7 @@ class DocType: def on_cancel(self): - msgprint("Cancellation of stock reconciliation is temporarily suspended. The feature will come back within 2-3 days.") + msgprint("Cancellation of stock reconciliation is temporarily suspended. The feature will come back soon.") raise Exception self.get_reconciliation_data(submit = 0) self.do_stock_reco(is_submit = -1) \ No newline at end of file From e4e88c97154b142a0acf5714d3a55d056c1689ae Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 24 Sep 2012 15:40:56 +0530 Subject: [PATCH 2/6] repost_stock patch and gross profit report --- .../patches/september_2012/repost_stock.py | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 erpnext/patches/september_2012/repost_stock.py diff --git a/erpnext/patches/september_2012/repost_stock.py b/erpnext/patches/september_2012/repost_stock.py new file mode 100644 index 0000000000..3a0690187b --- /dev/null +++ b/erpnext/patches/september_2012/repost_stock.py @@ -0,0 +1,32 @@ +# ERPNext - web based ERP (http://erpnext.com) +# Copyright (C) 2012 Web Notes Technologies Pvt Ltd +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from __future__ import unicode_literals +def execute(): + import webnotes + from webnotes.model.code import get_obj + bin = webnotes.conn.sql("select name from `tabBin` where item_code = 'NAS' \ + and warehouse = 'MAHAPE'") + i=0 + for d in bin: + try: + get_obj('Bin', d[0]).update_entries_after('2000-01-01', '12:05') + except: + pass + i += 1 + if i%100 == 0: + webnotes.conn.sql("commit") + webnotes.conn.sql("start transaction") \ No newline at end of file From 6e6dd1b0d5628865936a8ba26e603fc7bd5dae12 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 24 Sep 2012 15:45:02 +0530 Subject: [PATCH 3/6] reload gross profit report --- erpnext/patches/patch_list.py | 8 ++ .../september_2012/reload_gross_profit.py | 21 +++++ .../gross_profit/gross_profit.js | 2 +- .../gross_profit/gross_profit.py | 77 ++++++++----------- .../gross_profit/gross_profit.txt | 22 +++--- erpnext/stock/doctype/bin/bin.py | 17 ++-- 6 files changed, 80 insertions(+), 67 deletions(-) create mode 100644 erpnext/patches/september_2012/reload_gross_profit.py diff --git a/erpnext/patches/patch_list.py b/erpnext/patches/patch_list.py index d0ec56d6ec..3e14469ffa 100644 --- a/erpnext/patches/patch_list.py +++ b/erpnext/patches/patch_list.py @@ -588,4 +588,12 @@ patch_list = [ 'patch_module': 'patches.september_2012', 'patch_file': 'plot_patch', }, + { + 'patch_module': 'patches.september_2012', + 'patch_file': 'repost_stock', + }, + { + 'patch_module': 'patches.september_2012', + 'patch_file': 'reload_gross_profit', + }, ] diff --git a/erpnext/patches/september_2012/reload_gross_profit.py b/erpnext/patches/september_2012/reload_gross_profit.py new file mode 100644 index 0000000000..0a3f9efed7 --- /dev/null +++ b/erpnext/patches/september_2012/reload_gross_profit.py @@ -0,0 +1,21 @@ +# ERPNext - web based ERP (http://erpnext.com) +# Copyright (C) 2012 Web Notes Technologies Pvt Ltd +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from __future__ import unicode_literals +def execute(): + # reload gross profit report + from webnotes.modules import reload_doc + reload_doc('selling', 'search_criteria', 'gross_profit') \ No newline at end of file diff --git a/erpnext/selling/search_criteria/gross_profit/gross_profit.js b/erpnext/selling/search_criteria/gross_profit/gross_profit.js index 4ad67358bf..b274243ce8 100644 --- a/erpnext/selling/search_criteria/gross_profit/gross_profit.js +++ b/erpnext/selling/search_criteria/gross_profit/gross_profit.js @@ -15,7 +15,7 @@ // along with this program. If not, see . report.customize_filters = function() { - this.mytabs.items['Select Columns'].hide(); + //this.mytabs.items['Select Columns'].hide(); this.mytabs.tabs['More Filters'].hide(); this.hide_all_filters(); this.filter_fields_dict['Delivery Note'+FILTER_SEP +'ID'].df.filter_hide = 0; diff --git a/erpnext/selling/search_criteria/gross_profit/gross_profit.py b/erpnext/selling/search_criteria/gross_profit/gross_profit.py index e7f81abeb0..55daa68ad1 100644 --- a/erpnext/selling/search_criteria/gross_profit/gross_profit.py +++ b/erpnext/selling/search_criteria/gross_profit/gross_profit.py @@ -17,6 +17,8 @@ # Add Columns # ------------ from __future__ import unicode_literals +from webnotes.utils import flt + colnames[colnames.index('Rate*')] = 'Rate' col_idx['Rate'] = col_idx['Rate*'] col_idx.pop('Rate*') @@ -26,8 +28,8 @@ col_idx.pop('Amount*') columns = [ ['Purchase Cost','Currency','150px',''], - ['Gross Profit (%)','Currrency','150px',''], - ['Gross Profit','Currency','150px',''] + ['Gross Profit','Currency','150px',''], + ['Gross Profit (%)','Currrency','150px',''] ] for c in columns: @@ -37,65 +39,46 @@ for c in columns: coloptions.append(c[3]) col_idx[c[0]] = len(colnames)-1 -#out, tot_amount, tot_val_amount, tot_gross_profit = [], 0, 0, 0 sle = sql(""" select - item_code, warehouse, posting_date, posting_time, - actual_qty, stock_value, voucher_no, voucher_type + actual_qty, incoming_rate, voucher_no, item_code, warehouse from `tabStock Ledger Entry` where - ifnull(is_cancelled, 'No') = 'No' + voucher_type = 'Delivery Note' + and ifnull(is_cancelled, 'No') = 'No' order by posting_date desc, posting_time desc, name desc - force index posting_sort_index """, as_dict=1) -def get_purchase_cost(row): - stock_value = stock_val_after = 0 - item_warehouse = +def get_purchase_cost(dn, item, wh, qty): + from webnotes.utils import flt + global sle + purchase_cost = 0 for d in sle: - if d['voucher_type'] == 'Delivery Note' and d['voucher_no'] == row[col_idx['ID']]: - stock_val_after += flt(d['stock_value']) + if d['voucher_no'] == dn and d['item_code'] == item \ + and d['warehouse'] == wh and abs(d['actual_qty']) == qty: + purchase_cost += flt(d['incoming_rate'])*flt(abs(d['actual_qty'])) + return purchase_cost - +out, tot_amount, tot_pur_cost = [], 0, 0 for r in res: - r.append(get_purchase_cost(r)) + purchase_cost = get_purchase_cost(r[col_idx['ID']], r[col_idx['Item Code']], \ + r[col_idx['Warehouse']], r[col_idx['Quantity']]) + r.append(purchase_cost) + + gp = flt(r[col_idx['Amount']]) - flt(purchase_cost) + gp_percent = purchase_cost and gp*100/purchase_cost or 0 + r.append(fmt_money(gp)) + r.append(fmt_money(gp_percent)) + out.append(r) + + tot_amount += flt(r[col_idx['Amount']]) + tot_pur_cost += flt(purchase_cost) - - # tot_val_rate = 0 - # packing_list_items = sql("select item_code, warehouse, qty from `tabDelivery Note Packing Item` where parent = %s and parent_item = %s", (r[col_idx['ID']], r[col_idx['Item Code']])) - # for d in packing_list_items: - # if d[1]: - # val_rate = sql("select valuation_rate from `tabStock Ledger Entry` where item_code = %s and warehouse = %s and voucher_type = 'Delivery Note' and voucher_no = %s and is_cancelled = 'No'", (d[0], d[1], r[col_idx['ID']])) - # val_rate = val_rate and val_rate[0][0] or 0 - # if r[col_idx['Quantity']]: tot_val_rate += (flt(val_rate) * flt(d[2]) / flt(r[col_idx['Quantity']])) - # else: tot_val_rate = 0 - # - # r.append(fmt_money(tot_val_rate)) - # - # val_amount = flt(tot_val_rate) * flt(r[col_idx['Quantity']]) - # r.append(fmt_money(val_amount)) - # - # gp = flt(r[col_idx['Amount']]) - flt(val_amount) - # - # if val_amount: gp_percent = gp * 100 / val_amount - # else: gp_percent = gp - # - # r.append(fmt_money(gp_percent)) - # r.append(fmt_money(gp)) - # out.append(r) - # - # tot_gross_profit += flt(gp) - # tot_amount += flt(r[col_idx['Amount']]) - # tot_val_amount += flt(val_amount) - # # Add Total Row -# -------------- l_row = ['' for i in range(len(colnames))] l_row[col_idx['Quantity']] = 'TOTALS' l_row[col_idx['Amount']] = fmt_money(tot_amount) -l_row[col_idx['Valuation Amount']] = fmt_money(tot_val_amount) -if tot_val_amount: l_row[col_idx['Gross Profit (%)']] = fmt_money((tot_amount - tot_val_amount) * 100 / tot_val_amount) -else: l_row[col_idx['Gross Profit (%)']] = fmt_money(tot_amount) -l_row[col_idx['Gross Profit']] = fmt_money(tot_gross_profit) +l_row[col_idx['Purchase Cost']] = fmt_money(tot_pur_cost) +l_row[col_idx['Gross Profit']] = fmt_money(flt(tot_amount) - flt(tot_pur_cost)) out.append(l_row) \ No newline at end of file diff --git a/erpnext/selling/search_criteria/gross_profit/gross_profit.txt b/erpnext/selling/search_criteria/gross_profit/gross_profit.txt index b98ced6198..1f9cbc7e10 100644 --- a/erpnext/selling/search_criteria/gross_profit/gross_profit.txt +++ b/erpnext/selling/search_criteria/gross_profit/gross_profit.txt @@ -3,23 +3,23 @@ # These values are common in all dictionaries { - 'creation': '2012-04-03 12:49:51', - 'docstatus': 0, - 'modified': '2012-04-03 12:49:51', - 'modified_by': u'Administrator', - 'owner': u'Administrator' + u'creation': '2012-05-14 18:22:18', + u'docstatus': 0, + u'modified': '2012-09-24 14:11:39', + u'modified_by': u'Administrator', + u'owner': u'Administrator' }, # These values are common for all Search Criteria { - 'columns': u'Delivery Note\x01ID,Delivery Note\x01Posting Date,Delivery Note\x01Posting Time,Delivery Note Item\x01Item Code,Delivery Note Item\x01Item Name,Delivery Note Item\x01Description,Delivery Note\x01Project Name,Delivery Note Item\x01Quantity,Delivery Note Item\x01Rate*,Delivery Note Item\x01Amount*', + 'columns': u'Delivery Note\x01ID,Delivery Note\x01Posting Date,Delivery Note\x01Posting Time,Delivery Note Item\x01Item Code,Delivery Note Item\x01Item Name,Delivery Note Item\x01Description,Delivery Note Item\x01Warehouse,Delivery Note\x01Project Name,Delivery Note Item\x01Quantity,Delivery Note Item\x01Rate*,Delivery Note Item\x01Amount*', 'criteria_name': u'Gross Profit', 'description': u'Invoice wise', 'doc_type': u'Delivery Note Item', - 'doctype': 'Search Criteria', - 'filters': u"{'Delivery Note\x01Submitted':1,'Delivery Note\x01Status':'','Delivery Note\x01Fiscal Year':''}", + u'doctype': u'Search Criteria', + 'filters': u'{"Delivery Note\\u0001Submitted":1,"Delivery Note\\u0001Status":[],"Delivery Note\\u0001Fiscal Year":[]}', 'module': u'Selling', - 'name': '__common__', + u'name': u'__common__', 'page_len': 50, 'parent_doc_type': u'Delivery Note', 'sort_by': u'`tabDelivery Note`.`name`', @@ -29,7 +29,7 @@ # Search Criteria, gross_profit { - 'doctype': 'Search Criteria', - 'name': u'gross_profit' + u'doctype': u'Search Criteria', + u'name': u'gross_profit' } ] \ No newline at end of file diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py index 3430badfb6..fc2c75e87a 100644 --- a/erpnext/stock/doctype/bin/bin.py +++ b/erpnext/stock/doctype/bin/bin.py @@ -78,7 +78,7 @@ class DocType: 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'""" % + of serial no in store for item: %s and warehouse: %s""" % \ (self.doc.actual_qty, sr_count, self.doc.item_code, self.doc.warehouse) msgprint(msg, raise_exception=1) @@ -209,15 +209,17 @@ class DocType: break # nothing in store batch = self.fcfs_bal[0] - incoming_cost += flt(batch[1])*flt(withdraw) if batch[0] <= withdraw: # not enough or exactly same qty in current batch, clear batch + incoming_cost += flt(batch[1])*flt(batch[0]) withdraw -= batch[0] self.fcfs_bal.pop(0) + else: # all from current batch + incoming_cost += flt(batch[1])*flt(withdraw) batch[0] -= withdraw withdraw = 0 @@ -294,7 +296,7 @@ class DocType: prev_sle.get('posting_date','1900-01-01'), \ prev_sle.get('posting_time', '12:00')), as_dict = 1) - for sle in sll: + for sle in sll: # block if stock level goes negative on any date if val_method != 'Moving Average' or flt(allow_negative_stock) == 0: self.validate_negative_stock(cqty, sle) @@ -309,12 +311,11 @@ class DocType: cqty += sle['actual_qty'] # Stock Value upto the sle stock_val = self.get_stock_value(val_method, cqty, val_rate, serial_nos) - # update current sle sql("""update `tabStock Ledger Entry` - set bin_aqat=%s, valuation_rate=%s, fcfs_stack=%s, stock_value=%s, incoming_rate - where name=%s""", (cqty, flt(val_rate), cstr(self.fcfs_bal), \ - stock_val, in_rate, sle['name'])) + set bin_aqat=%s, valuation_rate=%s, fcfs_stack=%s, stock_value=%s, + incoming_rate = %s where name=%s""", \ + (cqty, flt(val_rate), cstr(self.fcfs_bal), stock_val, in_rate, sle['name'])) # update the bin if sll or not prev_sle: @@ -346,7 +347,7 @@ class DocType: self.create_auto_indent(ret[0], doc_type, doc_name, current_qty[0][0]) def create_auto_indent(self, i , doc_type, doc_name, cur_qty): - """ Create indent on reaching reorder level """\ + """ Create indent on reaching reorder level """ indent = Document('Purchase Request') indent.transaction_date = nowdate() indent.naming_series = 'IDT' From de7fcc66dfcdf444fb4902c47b08c1296225c3ca Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 24 Sep 2012 15:53:01 +0530 Subject: [PATCH 4/6] updated repost_stock patch --- erpnext/patches/september_2012/repost_stock.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/patches/september_2012/repost_stock.py b/erpnext/patches/september_2012/repost_stock.py index 3a0690187b..c6b6ce39f7 100644 --- a/erpnext/patches/september_2012/repost_stock.py +++ b/erpnext/patches/september_2012/repost_stock.py @@ -18,8 +18,7 @@ from __future__ import unicode_literals def execute(): import webnotes from webnotes.model.code import get_obj - bin = webnotes.conn.sql("select name from `tabBin` where item_code = 'NAS' \ - and warehouse = 'MAHAPE'") + bin = webnotes.conn.sql("select name from `tabBin`") i=0 for d in bin: try: From acd39fd9572e0186aa8e848d02a7481ac05645e7 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 24 Sep 2012 15:59:26 +0530 Subject: [PATCH 5/6] rounding issue fixed in gross profit --- erpnext/selling/search_criteria/gross_profit/gross_profit.js | 2 +- erpnext/selling/search_criteria/gross_profit/gross_profit.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/search_criteria/gross_profit/gross_profit.js b/erpnext/selling/search_criteria/gross_profit/gross_profit.js index b274243ce8..4ad67358bf 100644 --- a/erpnext/selling/search_criteria/gross_profit/gross_profit.js +++ b/erpnext/selling/search_criteria/gross_profit/gross_profit.js @@ -15,7 +15,7 @@ // along with this program. If not, see . report.customize_filters = function() { - //this.mytabs.items['Select Columns'].hide(); + this.mytabs.items['Select Columns'].hide(); this.mytabs.tabs['More Filters'].hide(); this.hide_all_filters(); this.filter_fields_dict['Delivery Note'+FILTER_SEP +'ID'].df.filter_hide = 0; diff --git a/erpnext/selling/search_criteria/gross_profit/gross_profit.py b/erpnext/selling/search_criteria/gross_profit/gross_profit.py index 55daa68ad1..7c9b9dafd8 100644 --- a/erpnext/selling/search_criteria/gross_profit/gross_profit.py +++ b/erpnext/selling/search_criteria/gross_profit/gross_profit.py @@ -67,7 +67,7 @@ for r in res: r.append(purchase_cost) gp = flt(r[col_idx['Amount']]) - flt(purchase_cost) - gp_percent = purchase_cost and gp*100/purchase_cost or 0 + gp_percent = purchase_cost and round((gp*100/purchase_cost), 2) or 0 r.append(fmt_money(gp)) r.append(fmt_money(gp_percent)) out.append(r) From 9b4ffcb50703614661dd128d2e7094191d6d6e1d Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 24 Sep 2012 16:12:01 +0530 Subject: [PATCH 6/6] total gross profit % in gross profit report --- erpnext/selling/search_criteria/gross_profit/gross_profit.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/search_criteria/gross_profit/gross_profit.py b/erpnext/selling/search_criteria/gross_profit/gross_profit.py index 7c9b9dafd8..5955222895 100644 --- a/erpnext/selling/search_criteria/gross_profit/gross_profit.py +++ b/erpnext/selling/search_criteria/gross_profit/gross_profit.py @@ -77,8 +77,10 @@ for r in res: # Add Total Row l_row = ['' for i in range(len(colnames))] -l_row[col_idx['Quantity']] = 'TOTALS' +l_row[col_idx['Project Name']] = 'TOTALS' l_row[col_idx['Amount']] = fmt_money(tot_amount) l_row[col_idx['Purchase Cost']] = fmt_money(tot_pur_cost) l_row[col_idx['Gross Profit']] = fmt_money(flt(tot_amount) - flt(tot_pur_cost)) +l_row[col_idx['Gross Profit (%)']] = round((flt(tot_amount) - flt(tot_pur_cost))*100/ \ +flt(tot_pur_cost), 2) out.append(l_row) \ No newline at end of file