ignored cancelled sle in valuation, fixed isue in fetching as on qty and rate in stock entry and cleaning up of code

This commit is contained in:
nabinhait 2011-06-10 16:57:48 +05:30
parent c47ef60b8d
commit daa7c74d3e
7 changed files with 102 additions and 97 deletions

View File

@ -25,7 +25,7 @@ class DocType:
# -------------
# 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 = ''):
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'):
if not dt: dt = nowdate()
# update the stock values (for current quantities)
@ -40,10 +40,10 @@ class DocType:
# update valuation for post dated entry
if actual_qty:
prev_sle = self.get_prev_sle(sle_id, dt, posting_time, 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:
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 <b>%s</b> in Warehouse <b>%s</b> 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)
@ -57,6 +57,7 @@ class DocType:
select * from `tabStock Ledger Entry`
where item_code = %s
and warehouse = %s
and ifnull(is_cancelled, 'No') = 'No'
order by timestamp(posting_date, posting_time) asc, name asc
limit 1
""", (self.doc.item_code, self.doc.warehouse), as_dict=1)
@ -66,7 +67,7 @@ class DocType:
# get previous stock ledger entry
# --------------------------------
def get_prev_sle(self, sle_id, posting_date, posting_time, serial_no = ''):
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
@ -84,6 +85,7 @@ class DocType:
where item_code = %s
and warehouse = %s
and name != %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
@ -101,8 +103,8 @@ class DocType:
if cqty + s['actual_qty'] < 0 and s['is_cancelled'] != 'Yes':
msgprint(cqty)
msgprint(s['actual_qty'])
msgprint('Cannot complete this transaction because stock will become negative for Item <b>%s</b> in Warehouse <b>%s</b> on Posting Date <b>%s</b>' % \
(self.doc.item_code, self.doc.warehouse, s['posting_date']))
msgprint('Cannot complete this transaction because stock will become negative in future transaction for Item <b>%s</b> in Warehouse <b>%s</b> on <b>%s %s</b>' % \
(self.doc.item_code, self.doc.warehouse, s['posting_date'], s['posting_time']))
raise Exception
# ------------------------------------
@ -148,14 +150,13 @@ class DocType:
# --------------------------
# get fifo inventory values
# --------------------------
def get_fifo_inventory_values(self, val_rate, in_rate, actual_qty, incoming_rate):
def get_fifo_inventory_values(self, in_rate, actual_qty):
# add batch to fcfs balance
if actual_qty > 0:
self.fcfs_bal.append([flt(actual_qty), flt(in_rate)])
val_rate = incoming_rate
# remove from fcfs balance
else:
fcfs_val = 0
withdraw = flt(abs(actual_qty))
while withdraw:
if not self.fcfs_bal:
@ -163,21 +164,21 @@ class DocType:
batch = self.fcfs_bal[0]
if batch[0] < withdraw:
# not enough in current batch, clear batch
if batch[0] <= withdraw:
# not enough or exactly same qty in current batch, clear batch
withdraw -= batch[0]
fcfs_val += (flt(batch[0]) * flt(batch[1]))
self.fcfs_bal.pop(0)
else:
# all from current batch
fcfs_val += (flt(withdraw) * flt(batch[1]))
batch[0] -= withdraw
withdraw = 0
val_rate = flt(fcfs_val) / 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
# -------------------
# get valuation rate
# -------------------
@ -187,7 +188,7 @@ class DocType:
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'])
elif val_method == 'FIFO':
val_rate = self.get_fifo_inventory_values(val_rate, in_rate, actual_qty = s['actual_qty'], incoming_rate = s['incoming_rate'])
val_rate = self.get_fifo_inventory_values(in_rate, actual_qty = s['actual_qty'])
return val_rate, stock_val
@ -198,8 +199,7 @@ class DocType:
if val_method == 'Moving Average' or serial_nos:
stock_val = flt(stock_val) * flt(cqty)
elif val_method == 'FIFO':
for d in self.fcfs_bal:
stock_val += (flt(d[0]) * flt(d[1]))
stock_val = sum([flt(d[0])*flt(d[1]) for d in self.fcfs_bal])
return stock_val
# ----------------------
@ -227,19 +227,20 @@ class DocType:
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) asc, name asc""", \
(self.doc.item_code, self.doc.warehouse, posting_date, posting_time), 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", sle_id, as_dict=1) + sll
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:
# block if stock level goes negative on any date
self.validate_negative_stock(cqty, s)
stock_val, in_rate = 0, s['incoming_rate'] # IN
serial_nos = "'"+"', '".join(cstr(s["serial_no"]).split('\n')) + "'"
serial_nos = s["serial_no"] and ("'"+"', '".join(cstr(s["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)
@ -249,7 +250,6 @@ class DocType:
# 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?????
sql("""update `tabStock Ledger Entry`
set bin_aqat=%s, valuation_rate=%s, fcfs_stack=%s, stock_value=%s

View File

@ -137,13 +137,14 @@ cur_frm.fields_dict['mtn_details'].grid.get_field('batch_no').get_query= functio
cur_frm.cscript.item_code = function(doc, cdt, cdn) {
var d = locals[cdt][cdn];
cal_back = function(r, rt){ /*cur_frm.cscript.calc_amount(doc)*/}
// get values
args = {
item_code: d.item_code,
warehouse: cstr(d.s_warehouse)
'item_code' : d.item_code,
'warehouse' : cstr(d.s_warehouse),
'transfer_qty' : d.transfer_qty,
'serial_no' : d.serial_no
};
get_server_fields('get_item_details',JSON.stringify(args),'mtn_details',doc,cdt,cdn,1,cal_back);
get_server_fields('get_item_details',JSON.stringify(args),'mtn_details',doc,cdt,cdn,1);
}
//==================================================================================================================

View File

@ -33,25 +33,25 @@ class DocType:
# get item details
# ----------------
def get_item_details(self, arg):
arg, bin, in_rate = eval(arg), None, 0
item = sql("select stock_uom, description, item_name from `tabItem` where name = %s and (ifnull(end_of_life,'')='' or end_of_life ='0000-00-00' or end_of_life > now())", (arg['item_code']), as_dict = 1)
if not item:
if arg['item_code']:
msgprint("Item is not active. You can restore it from Trash")
raise webnotes.ValidationError
arg, actual_qty, in_rate = eval(arg), 0, 0
item = sql("select stock_uom, description, item_name from `tabItem` where name = %s and (ifnull(end_of_life,'')='' or end_of_life ='0000-00-00' or end_of_life > now())", (arg.get('item_code')), as_dict = 1)
if not item:
msgprint("Item is not active", raise_exception=1)
if arg.get('warehouse'):
actual_qty = self.get_as_on_stock(arg.get('item_code'), arg.get('warehouse'), self.doc.posting_date, self.doc.posting_time)
in_rate = self.get_incoming_rate(arg.get('item_code'), arg.get('warehouse'), self.doc.posting_date, self.doc.posting_time, arg.get('transfer_qty'), arg.get('serial_no')) or 0
if arg['warehouse']:
bin = sql("select actual_qty from `tabBin` where item_code = %s and warehouse = %s", (arg['item_code'], arg['warehouse']), as_dict = 1)
in_rate = get_obj('Valuation Control').get_incoming_rate(self.doc.posting_date, self.doc.posting_time, arg['item_code'],arg['warehouse'])
ret = {
'uom' : item and item[0]['stock_uom'] or '',
'stock_uom' : item and item[0]['stock_uom'] or '',
'description' : item and item[0]['description'] or '',
'item_name' : item and item[0]['item_name'] or '',
'actual_qty' : bin and flt(bin[0]['actual_qty']) or 0,
'actual_qty' : actual_qty,
'qty' : 0,
'transfer_qty' : 0,
'incoming_rate' : flt(in_rate),
'incoming_rate' : in_rate,
'conversion_factor' : 1,
'batch_no' : ''
}
@ -73,32 +73,42 @@ class DocType:
return str(ret)
# get rate of FG item
#---------------------------
def get_in_rate(self, pro_obj):
# calculate_cost for production item
get_obj('BOM Control').calculate_cost(pro_obj.doc.bom_no)
# return cost
return flt(get_obj('Bill Of Materials', pro_obj.doc.bom_no).doc.cost_as_per_mar)
# get current_stock
# ----------------
def get_current_stock(self, pro_obj = ''):
# get stock and incoming rate on posting date
# ---------------------------------------------
def get_stock_and_rate(self, bom_no = ''):
for d in getlist(self.doclist, 'mtn_details'):
d.s_warehouse = (self.doc.purpose != 'Production Order') and self.doc.from_warehouse or cstr(d.s_warehouse)
d.t_warehouse = (self.doc.purpose != 'Production Order') and self.doc.to_warehouse or cstr(d.t_warehouse)
# assign parent warehouse
d.s_warehouse = cstr(d.s_warehouse) or self.doc.purpose != 'Production Order' and self.doc.from_warehouse or ''
d.t_warehouse = cstr(d.t_warehouse) or self.doc.purpose != 'Production Order' and self.doc.to_warehouse or ''
if d.s_warehouse:
bin = sql("select actual_qty from `tabBin` where item_code = %s and warehouse = %s", (d.item_code, d.s_warehouse), as_dict = 1)
d.actual_qty = bin and flt(bin[0]['actual_qty']) or 0
else:
d.actual_qty = 0
if d.fg_item:
d.incoming_rate = pro_obj and self.get_in_rate(pro_obj) or ''
elif self.doc.purpose not in ['Material Receipt', 'Sales Return'] and not d.incoming_rate and d.s_warehouse:
d.incoming_rate = flt(get_obj('Valuation Control').get_incoming_rate(self.doc.posting_date, self.doc.posting_time, d.item_code, d.s_warehouse, d.transfer_qty, d.serial_no))
d.save()
# get current stock at source warehouse
d.actual_qty = d.s_warehouse and self.get_as_on_stock(d.item_code, d.s_warehouse, self.doc.posting_date, self.doc.posting_time) or 0
# get incoming rate
if not flt(d.incoming_rate):
d.incoming_rate = self.get_incoming_rate(d.item_code, d.s_warehouse, self.doc.posting_date, self.doc.posting_time, d.transfer_qty, d.serial_no, d.fg_item, bom_no)
# Get stock qty on any date
# ---------------------------
def get_as_on_stock(self, item, wh, dt, tm):
bin = sql("select name from tabBin where item_code = %s and warehouse = %s", (item, wh))
bin_id = bin and bin[0][0] or ''
prev_sle = get_obj('Bin', bin_id).get_prev_sle(dt, tm)
qty = flt(prev_sle.get('bin_aqat', 0))
return qty
# Get incoming rate
# -------------------
def get_incoming_rate(self, item, wh, dt, tm, qty = 0, serial_no = '', fg_item = 'No', bom_no = ''):
in_rate = 0
if fg_item == 'Yes':
# re-calculate cost for production item from bom
get_obj('BOM Control').calculate_cost(bom_no)
in_rate = get_value('Bill Of Materials', bom_no, 'cost_as_per_mar')
elif wh:
in_rate = get_obj('Valuation Control').get_incoming_rate(dt, tm, item, wh, qty, serial_no)
return in_rate
# makes dict of unique items with it's qty
#-----------------------------------------
@ -242,7 +252,7 @@ class DocType:
self.validate_for_production_order(pro_obj)
self.validate_incoming_rate()
self.validate_warehouse(pro_obj)
self.get_current_stock(pro_obj)
self.get_current_stock(pro_obj.doc.bom_no)
self.calc_amount()
get_obj('Sales Common').validate_fiscal_year(self.doc.fiscal_year,self.doc.posting_date,'Posting Date')

View File

@ -217,7 +217,7 @@ class DocType:
if v["actual_qty"]:
sle_id = self.make_entry(v)
get_obj('Warehouse', v["warehouse"]).update_bin(flt(v["actual_qty"]), 0, 0, 0, 0, v["item_code"], v["posting_date"], sle_id, v["posting_time"], '')
get_obj('Warehouse', v["warehouse"]).update_bin(flt(v["actual_qty"]), 0, 0, 0, 0, v["item_code"], v["posting_date"], sle_id, v["posting_time"], '', v["is_cancelled"])
# -----------

View File

@ -118,7 +118,7 @@ class DocType:
# ------------------
def get_current_stock(self, item_code, warehouse):
bin = sql("select name from `tabBin` where item_code = '%s' and warehouse = '%s'" % (item_code, warehouse))
prev_sle = bin and get_obj('Bin', bin[0][0]).get_prev_sle('', self.doc.reconciliation_date,self.doc.reconciliation_time) or 0
prev_sle = bin and get_obj('Bin', bin[0][0]).get_prev_sle(self.doc.reconciliation_date,self.doc.reconciliation_time) or 0
stock_uom = sql("select stock_uom from `tabItem` where name = %s",item_code)
return {'actual_qty': prev_sle.get('bin_aqat', 0), 'stock_uom': stock_uom[0][0]}
@ -169,7 +169,7 @@ class DocType:
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)
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)

View File

@ -23,31 +23,23 @@ class DocType:
# Get FIFO Rate from Stack
# -------------------------
def get_fifo_rate(self, fcfs_bal, qty):
if qty:
fcfs_val = 0
withdraw = flt(qty)
while withdraw:
if not fcfs_bal:
break # nothing in store
batch = fcfs_bal[0]
if batch[0] < withdraw:
# not enough in current batch, clear batch
withdraw -= batch[0]
fcfs_val += (flt(batch[0]) * flt(batch[1]))
fcfs_bal.pop(0)
else:
# all from current batch
fcfs_val += (flt(withdraw) * flt(batch[1]))
batch[0] -= withdraw
withdraw = 0
fcfs_rate = flt(fcfs_val) / flt(qty)
return fcfs_rate
else:
return fcfs_bal and fcfs_bal[0][1] or 0
def get_fifo_rate(self, fcfs_stack, qty):
fcfs_val = 0
withdraw = flt(qty)
while withdraw:
batch = fcfs_stack[0]
if batch[0] <= withdraw:
# not enough or exactly same qty in current batch, clear batch
withdraw -= batch[0]
fcfs_val += (flt(batch[0]) * flt(batch[1]))
fcfs_stack.pop(0)
else:
# all from current batch
fcfs_val += (flt(withdraw) * flt(batch[1]))
batch[0] -= withdraw
withdraw = 0
fcfs_rate = flt(fcfs_val) / flt(qty)
return fcfs_rate
# --------------------------------
# get serializable inventory rate
@ -76,15 +68,17 @@ class DocType:
def get_incoming_rate(self, posting_date, posting_time, item, warehouse, qty = 0, serial_no = ''):
in_rate = 0
val_method = self.get_valuation_method(item)
bin_obj = get_obj('Warehouse',warehouse).get_bin(item)
if serial_no:
in_rate = self.get_serializable_inventory_rate(serial_no)
elif val_method == 'FIFO':
bin_obj = get_obj('Warehouse',warehouse).get_bin(item)
prev_sle = bin_obj.get_prev_sle('',posting_date, posting_time)
fcfs_stack = eval(prev_sle.get('fcfs_stack', '[]') or '[]')
in_rate = fcfs_stack and self.get_fifo_rate(fcfs_stack, qty) or 0
in_rate = 0
if qty:
prev_sle = bin_obj.get_prev_sle(posting_date, posting_time)
fcfs_stack = eval(prev_sle.get('fcfs_stack', '[]') or '[]')
in_rate = fcfs_stack and self.get_fifo_rate(fcfs_stack, qty) or 0
elif val_method == 'Moving Average':
bin_obj = get_obj('Warehouse',warehouse).get_bin(item)
prev_sle = bin_obj.get_prev_sle('',posting_date, posting_time)
prev_sle = bin_obj.get_prev_sle(posting_date, posting_time)
in_rate = prev_sle and prev_sle.get('valuation_rate', 0) or 0
return in_rate
return in_rate

View File

@ -52,12 +52,12 @@ class DocType:
# update bin
# ----------
def update_bin(self, actual_qty, reserved_qty, ordered_qty, indented_qty, planned_qty, item_code, dt, sle_id = '',posting_time = '', serial_no = ''):
def update_bin(self, actual_qty, reserved_qty, ordered_qty, indented_qty, planned_qty, item_code, dt, sle_id = '',posting_time = '', serial_no = '', is_cancelled = 'No'):
self.validate_asset(item_code)
it_det = get_value('Item', item_code, 'is_stock_item')
if it_det and it_det == 'Yes':
bin = self.get_bin(item_code)
bin.update_stock(actual_qty, reserved_qty, ordered_qty, indented_qty, planned_qty, dt, sle_id, posting_time, serial_no)
bin.update_stock(actual_qty, reserved_qty, ordered_qty, indented_qty, planned_qty, dt, sle_id, posting_time, serial_no, is_cancelled)
return bin
else:
msgprint("[Stock Update] Ignored %s since it is not a stock item" % item_code)