mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-05 13:24:47 +00:00
stock reco fixes with testcases
This commit is contained in:
@@ -31,18 +31,34 @@ class DocType:
|
||||
self.doc = doc
|
||||
self.doclist = doclist
|
||||
|
||||
def validate(self):
|
||||
if not self.doc.stock_uom:
|
||||
self.doc.stock_uom = webnotes.conn.get_value('Item', self.doc.item_code, 'stock_uom')
|
||||
|
||||
if not self.doc.warehouse_type:
|
||||
self.doc.warehouse_type = webnotes.conn.get_value("Warehouse", self.doc.warehouse,
|
||||
"warehouse_type")
|
||||
|
||||
self.validate_mandatory()
|
||||
|
||||
self.doc.projected_qty = flt(self.doc.actual_qty) + flt(self.doc.ordered_qty) + \
|
||||
flt(self.doc.indented_qty) + flt(self.doc.planned_qty) - flt(self.doc.reserved_qty)
|
||||
|
||||
def validate_mandatory(self):
|
||||
qf = ['actual_qty', 'reserved_qty', 'ordered_qty', 'indented_qty']
|
||||
for f in qf:
|
||||
if (not self.doc.fields.has_key(f)) or (not self.doc.fields[f]):
|
||||
self.doc.fields[f] = 0.0
|
||||
|
||||
def update_stock(self, args):
|
||||
from stock.stock_ledger import update_entries_after
|
||||
if not args.get("posting_date"):
|
||||
posting_date = nowdate()
|
||||
|
||||
self.update_qty(args)
|
||||
|
||||
if (flt(args.get("actual_qty")) < 0 or flt(args.get("reserved_qty")) > 0) \
|
||||
and args.get("is_cancelled") == 'No' and args.get("is_amended")=='No':
|
||||
self.reorder_item(args.get("voucher_type"), args.get("voucher_no"))
|
||||
|
||||
if args.get("actual_qty"):
|
||||
from stock.stock_ledger import update_entries_after
|
||||
|
||||
if not args.get("posting_date"):
|
||||
posting_date = nowdate()
|
||||
|
||||
# update valuation and qty after transaction for post dated entry
|
||||
update_entries_after({
|
||||
"item_code": self.doc.item_code,
|
||||
@@ -53,8 +69,8 @@ class DocType:
|
||||
|
||||
def update_qty(self, args):
|
||||
# update the stock values (for current quantities)
|
||||
self.doc.actual_qty = flt(self.doc.actual_qty) + flt(args.get("actual_qty", 0))
|
||||
self.doc.ordered_qty = flt(self.doc.ordered_qty) + flt(args.get("ordered_qty", 0))
|
||||
self.doc.actual_qty = flt(self.doc.actual_qty) + flt(args.get("actual_qty"))
|
||||
self.doc.ordered_qty = flt(self.doc.ordered_qty) + flt(args.get("ordered_qty"))
|
||||
self.doc.reserved_qty = flt(self.doc.reserved_qty) + flt(args.get("reserved_qty"))
|
||||
self.doc.indented_qty = flt(self.doc.indented_qty) + flt(args.get("indented_qty"))
|
||||
self.doc.planned_qty = flt(self.doc.planned_qty) + flt(args.get("planned_qty"))
|
||||
@@ -63,6 +79,10 @@ class DocType:
|
||||
flt(self.doc.indented_qty) + flt(self.doc.planned_qty) - flt(self.doc.reserved_qty)
|
||||
|
||||
self.doc.save()
|
||||
|
||||
if (flt(args.get("actual_qty")) < 0 or flt(args.get("reserved_qty")) > 0) \
|
||||
and args.get("is_cancelled") == 'No' and args.get("is_amended")=='No':
|
||||
self.reorder_item(args.get("voucher_type"), args.get("voucher_no"))
|
||||
|
||||
def get_first_sle(self):
|
||||
sle = sql("""
|
||||
@@ -75,111 +95,6 @@ class DocType:
|
||||
""", (self.doc.item_code, self.doc.warehouse), as_dict=1)
|
||||
return sle and sle[0] or None
|
||||
|
||||
|
||||
|
||||
# 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 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)
|
||||
# return val_rate, in_rate
|
||||
#
|
||||
# def get_moving_average_inventory_values(self, val_rate, in_rate, opening_qty, actual_qty, is_cancelled):
|
||||
# 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 :
|
||||
# # 1. actual qty is negative(delivery note / stock entry)
|
||||
# # 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:
|
||||
# opening_qty = opening_qty > 0 and opening_qty or 0
|
||||
# val_rate = ((opening_qty *val_rate) + (actual_qty * in_rate)) / \
|
||||
# (opening_qty + actual_qty)
|
||||
# elif (opening_qty + actual_qty) <= 0:
|
||||
# val_rate = 0
|
||||
# return val_rate, in_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)])
|
||||
#
|
||||
# # 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]
|
||||
#
|
||||
# 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
|
||||
#
|
||||
# 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, in_rate
|
||||
#
|
||||
# def get_valuation_rate(self, val_method, serial_nos, val_rate, in_rate, stock_val, cqty, s):
|
||||
# if 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, 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, in_rate = self.get_fifo_inventory_values(in_rate, \
|
||||
# actual_qty = s['actual_qty'])
|
||||
# return val_rate, in_rate
|
||||
|
||||
# def get_stock_value(self, val_method, cqty, val_rate, serial_nos):
|
||||
# if serial_nos:
|
||||
# stock_val = flt(val_rate) * flt(cqty)
|
||||
# elif val_method == 'Moving Average':
|
||||
# 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
|
||||
|
||||
|
||||
|
||||
def reorder_item(self,doc_type,doc_name):
|
||||
""" Reorder item if stock reaches reorder level"""
|
||||
|
||||
@@ -246,12 +161,3 @@ class DocType:
|
||||
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()
|
||||
|
||||
def validate_mandatory(self):
|
||||
qf = ['actual_qty', 'reserved_qty', 'ordered_qty', 'indented_qty']
|
||||
for f in qf:
|
||||
if (not self.doc.fields.has_key(f)) or (not self.doc.fields[f]):
|
||||
self.doc.fields[f] = 0.0
|
||||
|
||||
Reference in New Issue
Block a user