[ADD] Add removal and putaway strategies

bzr revid: jco@openerp.com-20130624152850-7y0t36wcj2hyba4o
This commit is contained in:
Josse Colpaert 2013-06-24 17:28:50 +02:00
parent a0edfd14e8
commit c45c32a164
4 changed files with 92 additions and 11 deletions

View File

@ -1,3 +1,8 @@
-
Set product category removal strategy as LIFO
-
!record {model:product.category, id: product.product_category_1}:
removal_strategy: lifo
-
Set a product as using lifo price
-

View File

@ -574,6 +574,7 @@ class product_category(osv.osv):
_inherit = 'product.category'
_columns = {
'removal_strategy': fields.selection([('fifo', 'FIFO'), ('lifo', 'LIFO'), ('nearest', 'Nearest Location')], "Picking Strategy"),
'property_stock_journal': fields.property(
relation='account.journal',
type='many2one',

View File

@ -342,6 +342,10 @@ class stock_location(osv.osv):
'location': ids
})
return product_obj.get_product_available(cr, uid, product_ids, context=context)
def get_removal_strategy(self, cr, uid, id, product_id, context=None):
product = self.pool.get("product.product").browse(cr, uid, product_id, context=context)
return product.categ_id.removal_strategy or 'fifo'
def _product_get(self, cr, uid, id, product_ids=False, context=None, states=None):
"""
@ -534,7 +538,6 @@ class stock_quant(osv.osv):
self.write(cr, uid, quant.id, {'qty': new_qty}, context=context)
quants_to_reserve.append(new_quant)
self.write(cr, uid, quants_to_reserve, {'reservation_id': move.id}, context=context)
print "Assign", quants_to_reserve, "to",
self.pool.get("stock.move").write(cr, uid, [move.id], {'reserved_quant_ids': [(4, x) for x in quants_to_reserve]}, context=context)
@ -679,6 +682,9 @@ class stock_quant(osv.osv):
'history_ids': [(4, move.id)]}, context=context)
return self.filter_quants_with_out_history(cr, uid, quants_rec, context=context)
def choose_quants(self, cr, uid, location_id, product_id, qty, context=None):
"""
@ -691,8 +697,12 @@ class stock_quant(osv.osv):
"""
#TODO Normally, you should check the removal strategy now
#But we will assume it is FIFO for the moment
possible_quants = self.search(cr, uid, [('location_id', 'child_of', location_id), ('product_id','=',product_id),
('qty', '>', 0.0), ('reservation_id', '=', False)], order = 'in_date, id', context=context)
if self.pool.get('stock.location').get_removal_strategy(cr, uid, location_id, product_id, context=context) == 'lifo':
possible_quants = self.search(cr, uid, [('location_id', 'child_of', location_id), ('product_id','=',product_id),
('qty', '>', 0.0), ('reservation_id', '=', False)], order = 'in_date desc, id desc', context=context)
else:
possible_quants = self.search(cr, uid, [('location_id', 'child_of', location_id), ('product_id','=',product_id),
('qty', '>', 0.0), ('reservation_id', '=', False)], order = 'in_date, id', context=context)
qty_todo = qty
res = []
for quant in self.browse(cr, uid, possible_quants, context=context):
@ -2357,7 +2367,7 @@ class stock_move(osv.osv):
return True
#
# Duplicate stock.move
# Duplicate stock.move -> no duplicate: assign quants
#
def check_assign(self, cr, uid, ids, context=None):
""" Checks the product type and accordingly writes the state.
@ -2383,9 +2393,6 @@ class stock_move(osv.osv):
#Convert UoM qty -> check rounding now in product_reserver
qty = uom_obj._compute_qty(cr, uid, move.product_uom.id, move.product_qty, move.product_id.uom_id.id)
res2 = quant_obj.choose_quants(cr, uid, move.location_id.id, move.product_id.id, qty, context=context)
#ASSUME: it is on the same location anyways -> when putaway works: can not be all the time
#Should group quants by location:
quants = {}
qtys = {}
@ -2399,7 +2406,7 @@ class stock_move(osv.osv):
quants[quant.location_id.id] += [quant.id]
qtys[quant.location_id.id] += [tuple[1]]
qty[quant.location_id.id] += tuple[1]
res = [(qty[key], key) for key in quants.keys()]
if res:
#_product_available_test depends on the next status for correct functioning
@ -2418,7 +2425,7 @@ class stock_move(osv.osv):
r = res.pop(0)
move_id = self.copy(cr, uid, move.id, {'product_uos_qty': product_uos_qty, 'product_qty': r[0], 'location_id': r[1]})
done.append(move_id)
quant_obj.split_and_assign_quants(cr, uid, zip(quants[r[1]], qtys[r[1]]), self.browse(cr, uid, move_id, context=context), context=context)
if done:
count += len(done)
self.write(cr, uid, done, {'state': 'assigned'})
@ -2713,7 +2720,7 @@ class stock_move(osv.osv):
quants = {}
for move in self.browse(cr, uid, ids, context=context):
quants[move.id] = []
if (move.location_id.usage in ['supplier']):
if (move.location_id.usage in ['supplier', 'inventory']):
#Create quants
reconciled_quants = self.pool.get("stock.quant").create_quants(cr, uid, move, context=context)
quants[move.id] += reconciled_quants
@ -2722,7 +2729,6 @@ class stock_move(osv.osv):
self.check_total_qty(cr, uid, ids, context=context)
reconciled_quants = self.pool.get("stock.quant").move_quants(cr, uid, quants[move.id], move, context=context)
quants[move.id] += reconciled_quants
print "Reconciled quants", quants[move.id]
#Generate negative quants if necessary

View File

@ -85,6 +85,46 @@ class product_pulled_flow(osv.osv):
'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'product.pulled.flow', context=c),
}
class product_putaway_strategy(osv.osv):
def _calc_product_ids(self, cr, uid, ids, field, arg, context=None):
'''
This function should check on which products (including if the products are in a product category) this putaway strategy is used
'''
pass
_name = 'product.putaway'
_description = 'Put Away Strategy'
_columns = {
'product_ids':fields.function(_calc_product_ids, "Products"),
'product_categ_id':fields.many2one('product.category', 'Product Category'),
'location_id': fields.many2one('stock.location','Parent Location', help="Parent Destination Location from which a child bin location needs to be chosen"), #domain=[('type', '=', 'parent')],
'method': fields.selection([('nearest_empty','Nearest Empty Location'), ('add_or_nearest_empty', 'Try to add on another location, otherwise nearest empty'), ('L2R', 'left to right'), ('R2L', 'right to left'),
('high2low', 'high to low'), ('low2high', 'low to high'), ('fixed', 'Fixed Location')], "Method"),
}
class product_removal_strategy(osv.osv):
def _calc_product_ids(self, cr, uid, ids, field, arg, context=None):
'''
This function should check on which products (including if the products are in a product category) this removal strategy is used
'''
pass
_name = 'product.removal'
_description = 'Removal Strategy'
_columns = {
'product_ids':fields.function(_calc_product_ids, "Products"),
'product_categ_id':fields.many2one('product.category', 'Product Category'),
'location_id': fields.many2one('stock.location', 'Parent Location', help="Parent Source Location from which a child bin location needs to be chosen"), #, domain=[('type', '=', 'parent')]
'method': fields.selection([('fifo', 'FIFO'), ('lifo', 'LIFO'), ('nearest', 'Nearest location')], "Method"),
}
class product_product(osv.osv):
_inherit = 'product.product'
_columns = {
@ -98,6 +138,15 @@ class product_product(osv.osv):
class product_category(osv.osv):
_inherit = 'product.category'
_columns = {
#'route_ids': fields.many2many('stock.route', 'product_catg_id', 'route_id', 'Routes'),
'removal_strategy_ids': fields.many2many('product.removal', 'product_catg_id', 'removal_strat_id' 'Removal Strategies'),
'putaway_strategy_ids': fields.many2many('product.putaway', 'product_catg_id', 'putaway_strat_id', 'Put Away Strategies'),
}
class stock_move(osv.osv):
@ -119,6 +168,26 @@ class stock_move(osv.osv):
class stock_location(osv.osv):
_inherit = 'stock.location'
_columns = {
'removal_strategy_ids': fields.many2many('product.removal', 'location_id', 'removal_strat_id' 'Removal Strategies'),
'putaway_strategy_ids': fields.many2many('product.putaway', 'location_id', 'putaway_strat_id', 'Put Away Strategies'),
}
def get_putaway_strategy(self, cr, uid, id, product_id, context=None):
product = self.pool.get("product.product").browse(cr, uid, product_id, context=context)
strats = self.pool.get('product.removal').search(cr, uid, [('location_id','=',id), ('product_categ_id','child_of', product.categ_id.id)], context=context) #Also child_of for location???
return strats and strats[0] or 'nearest'
def get_removal_strategy(self, cr, uid, id, product_id, context=None):
product = self.pool.get("product.product").browse(cr, uid, product_id, context=context)
strats = self.pool.get('product.removal').search(cr, uid, [('location_id','=',id), ('product_categ_id','child_of', product.categ_id.id)], context=context) #Also child_of for location???
if not strats:
strat = product.categ_id.removal_strategy
else:
strat = strats[0]
return strat or product.categ_id.removal_strategy or 'fifo'
def chained_location_get(self, cr, uid, location, partner=None, product=None, context=None):
if product:
for path in product.path_ids: