03 Agustus 2014

Tentang perhitungan stock

Bismillahirrahmaanirrahiim

Udah lama gak update blog ini...
Ini mau bahas perhitungan stock...

Dikarenakan ada request untuk membuat modul yang menampilkan tree product dengan data future stock 7 minggu ke depan, maka saya harus memperhatikan dengan seksama method perhitungan stock di OpenERP.

Method nya ada di modul stock/product/product.py
def _product_available

yang di dalamnya memanggil method
def get_product_available

7 minggu ke depan maksudnya..
contoh hari ini tanggal 3 agustus adalah minggu ke 32,
maka saya harus menampilkan
jumlah stock saat ini, future stock minggu ke-32, minggu ke-33 sampai minggu ke-38

untuk jumlah stock saat ini tidak masalah, karena memang menghitung dari tabel stock_move dengan state 'done'

nah saat menghitung future stock ada keanehan (setidaknya menurut saya aneh, mohon dikoreksi ya)

contoh:
ada PO utk product X tanggal 03/08 dengan expected delivery tanggal 25 agustus (minggu ke-35) dengan jumlah 15
Stock saat ini adalah 20

maka yang saya harapkan adalah
current stock = 20
future stock minggu-32 = masih 20
future stock minggu-33 = masih 20
future stock minggu-34 = masih 20 
future stock minggu-35 = baru berubah menjadi 35

ehh tapi hasilnyaaa....
current stock = 20
future stock minggu-32 = 35
future stock minggu-33 = 35
future stock minggu-34 = 35
future stock minggu-35 = 35

kenapa hasilnya seperti itu?
karena di method
    def get_product_available(self, cr, uid, ids, context=None):

ada baris berikut:

        if from_date and to_date:
            date_str = "date>=%s and date<=%s"
            where.append(tuple([from_date]))
            where.append(tuple([to_date]))
        elif from_date:
            date_str = "date>=%s"
            date_values = [from_date]
        elif to_date:
            date_str = "date<=%s"
            date_values = [to_date]
        if date_values:
            where.append(tuple(date_values))

Maksudnya adalah, dia akan menghitung stock dari table stock_move dengan where clause (jika ada filter date) adalah where stock_move.date >= from_date and date <= to_date dst
sedangkan field date sendiri digunakan untuk menyimpan tanggal order (tanggal dibuatnya IS - dari tanggal PO) dan jika sudah done maka field date adalah tanggal dikirimkan/diterimanya barang.

Sedangkan future stock harusnya diambil dari date_expected nya dongs... 

jadi where stock_move.date_expected >= blablabla

Karena itu saya lakukan perubahan hmm 'lumayan sedikit #eeh' di method tsb seperti ini kira2..
"""siti: saya harus perbaiki method ini, karena aslinya... where clause nya dibuat sama semua baik
untuk semua state. Sedangkan seharusnya utk state selain done, diambil where clause dari date_expected
bukan date (tanggal create PO atau tanggal proses selesai) saja
"""
def get_product_available(self, cr, uid, ids, context=None):
""" Finds whether product is available or not in particular warehouse.
@return: Dictionary of values
"""
if context is None:
context = {}
location_obj = self.pool.get('stock.location')
warehouse_obj = self.pool.get('stock.warehouse')
shop_obj = self.pool.get('sale.shop')
states = context.get('states',[])
what = context.get('what',())
if not ids:
ids = self.search(cr, uid, [])
res = {}.fromkeys(ids, 0.0)
if not ids:
return res
_log.info (context)
if context.get('shop', False):
warehouse_id = shop_obj.read(cr, uid, int(context['shop']), ['warehouse_id'])['warehouse_id'][0]
if warehouse_id:
context['warehouse'] = warehouse_id
if context.get('warehouse', False):
lot_id = warehouse_obj.read(cr, uid, int(context['warehouse']), ['lot_stock_id'])['lot_stock_id'][0]
if lot_id:
context['location'] = lot_id
if context.get('location', False):
if type(context['location']) == type(1):
location_ids = [context['location']]
elif type(context['location']) in (type(''), type(u'')):
location_ids = location_obj.search(cr, uid, [('name','ilike',context['location'])], context=context)
else:
location_ids = context['location']
else:
location_ids = []
wids = warehouse_obj.search(cr, uid, [], context=context)
if not wids:
return res
for w in warehouse_obj.browse(cr, uid, wids, context=context):
location_ids.append(w.lot_stock_id.id)
# build the list of ids of children of the location given by id
if context.get('compute_child',True):
child_location_ids = location_obj.search(cr, uid, [('location_id', 'child_of', location_ids)])
location_ids = child_location_ids or location_ids
# this will be a dictionary of the product UoM by product id
product2uom = {}
uom_ids = []
for product in self.read(cr, uid, ids, ['uom_id'], context=context):
product2uom[product['id']] = product['uom_id'][0]
uom_ids.append(product['uom_id'][0])
# this will be a dictionary of the UoM resources we need for conversion purposes, by UoM id
uoms_o = {}
for uom in self.pool.get('product.uom').browse(cr, uid, uom_ids, context=context):
uoms_o[uom.id] = uom
if 'done' in states:
results = []
results2 = []
from_date = context.get('from_date',False)
to_date = context.get('to_date',False)
date_str = False
date_values = False
donestates=['done',]
where = [tuple(location_ids),tuple(location_ids),tuple(ids),tuple(donestates)]
if from_date and to_date:
date_str = "date>=%s and date<=%s"
where.append(tuple([from_date]))
where.append(tuple([to_date]))
elif from_date:
date_str = "date>=%s"
date_values = [from_date]
elif to_date:
date_str = "date<=%s"
date_values = [to_date]
if date_values:
where.append(tuple(date_values))
prodlot_id = context.get('prodlot_id', False)
prodlot_clause = ''
if prodlot_id:
prodlot_clause = ' and prodlot_id = %s '
where += [prodlot_id]
# TODO: perhaps merge in one query.
if 'in' in what:
# all moves from a location out of the set to a location in the set
cr.execute(
'select sum(product_qty), product_id, product_uom '\
'from stock_move '\
'where location_id NOT IN %s '\
'and location_dest_id IN %s '\
'and product_id IN %s '\
'and state IN %s ' + (date_str and 'and '+date_str+' ' or '') +' '\
+ prodlot_clause +
'group by product_id,product_uom',tuple(where))
results = cr.fetchall()
_log.info (where)
if 'out' in what:
# all moves from a location in the set to a location out of the set
cr.execute(
'select sum(product_qty), product_id, product_uom '\
'from stock_move '\
'where location_id IN %s '\
'and location_dest_id NOT IN %s '\
'and product_id IN %s '\
'and state in %s ' + (date_str and 'and '+date_str+' ' or '') + ' '\
+ prodlot_clause +
'group by product_id,product_uom',tuple(where))
results2 = cr.fetchall()
# Get the missing UoM resources
uom_obj = self.pool.get('product.uom')
uoms = map(lambda x: x[2], results) + map(lambda x: x[2], results2)
if context.get('uom', False):
uoms += [context['uom']]
uoms = filter(lambda x: x not in uoms_o.keys(), uoms)
if uoms:
uoms = uom_obj.browse(cr, uid, list(set(uoms)), context=context)
for o in uoms:
uoms_o[o.id] = o
#TOCHECK: before change uom of product, stock move line are in old uom.
context.update({'raise-exception': False})
# Count the incoming quantities
for amount, prod_id, prod_uom in results:
amount = uom_obj._compute_qty_obj(cr, uid, uoms_o[prod_uom], amount,
uoms_o[context.get('uom', False) or product2uom[prod_id]], context=context)
res[prod_id] += amount
# Count the outgoing quantities
for amount, prod_id, prod_uom in results2:
amount = uom_obj._compute_qty_obj(cr, uid, uoms_o[prod_uom], amount,
uoms_o[context.get('uom', False) or product2uom[prod_id]], context=context)
res[prod_id] -= amount
#untuk states selain done
if 'confirmed' in states or ('waiting' in states) or ('assigned' in states):
results = []
results2 = []
from_date = context.get('from_date',False)
to_date = context.get('to_date',False)
date_str = False
date_values = False
notdonestates = ['confirmed','waiting','assigned']
where = [tuple(location_ids),tuple(location_ids),tuple(ids),tuple(notdonestates)]
if from_date and to_date:
date_str = "date_expected>=%s and date_expected<=%s"
where.append(tuple([from_date]))
where.append(tuple([to_date]))
elif from_date:
date_str = "date_expected>=%s"
date_values = [from_date]
elif to_date:
date_str = "date_expected<=%s"
date_values = [to_date]
if date_values:
where.append(tuple(date_values))
prodlot_id = context.get('prodlot_id', False)
prodlot_clause = ''
if prodlot_id:
prodlot_clause = ' and prodlot_id = %s '
where += [prodlot_id]
# TODO: perhaps merge in one query.
if 'in' in what:
# all moves from a location out of the set to a location in the set
cr.execute(
'select sum(product_qty), product_id, product_uom '\
'from stock_move '\
'where location_id NOT IN %s '\
'and location_dest_id IN %s '\
'and product_id IN %s '\
'and state IN %s ' + (date_str and 'and '+date_str+' ' or '') +' '\
+ prodlot_clause +
'group by product_id,product_uom',tuple(where))
results = cr.fetchall()
_log.info (where)
if 'out' in what:
# all moves from a location in the set to a location out of the set
cr.execute(
'select sum(product_qty), product_id, product_uom '\
'from stock_move '\
'where location_id IN %s '\
'and location_dest_id NOT IN %s '\
'and product_id IN %s '\
'and state in %s ' + (date_str and 'and '+date_str+' ' or '') + ' '\
+ prodlot_clause +
'group by product_id,product_uom',tuple(where))
results2 = cr.fetchall()
# Get the missing UoM resources
uom_obj = self.pool.get('product.uom')
uoms = map(lambda x: x[2], results) + map(lambda x: x[2], results2)
if context.get('uom', False):
uoms += [context['uom']]
uoms = filter(lambda x: x not in uoms_o.keys(), uoms)
if uoms:
uoms = uom_obj.browse(cr, uid, list(set(uoms)), context=context)
for o in uoms:
uoms_o[o.id] = o
#TOCHECK: before change uom of product, stock move line are in old uom.
context.update({'raise-exception': False})
# Count the incoming quantities
for amount, prod_id, prod_uom in results:
amount = uom_obj._compute_qty_obj(cr, uid, uoms_o[prod_uom], amount,
uoms_o[context.get('uom', False) or product2uom[prod_id]], context=context)
res[prod_id] += amount
# Count the outgoing quantities
for amount, prod_id, prod_uom in results2:
amount = uom_obj._compute_qty_obj(cr, uid, uoms_o[prod_uom], amount,
uoms_o[context.get('uom', False) or product2uom[prod_id]], context=context)
res[prod_id] -= amount
return res
view raw product.py hosted with ❤ by GitHub



jadi saya bagi-2
yg pertama utk states done saja pakai yang biasa
yang satu lagi selain done diambil dari date_expected

Mohon bantu review dan dikoreksi jika ada salah atau disederhanakan

terima kasihhh