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
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):
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..
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""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 |
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