diff --git a/addons/stock/procurement.py b/addons/stock/procurement.py
index 09a76607c58..d6f9875459d 100644
--- a/addons/stock/procurement.py
+++ b/addons/stock/procurement.py
@@ -106,7 +106,8 @@ class procurement_order(osv.osv):
#
# Scheduler
- # When stock is installed, it should also check for the different
+ # When stock is installed, it should also check for the different confirmed stock moves
+ # if they can not be installed
#
#
def run_scheduler(self, cr, uid, use_new_cursor=False, context=None):
@@ -132,7 +133,7 @@ class procurement_order(osv.osv):
company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id
move_obj = self.pool.get('stock.move')
#Search all confirmed stock_moves and try to assign them
- confirmed_ids = move_obj.search(cr, uid, [('state', 'in', ['confirmed', 'waiting']), ('company_id','=', company.id)], context=context) #Type = stockable product?
+ confirmed_ids = move_obj.search(cr, uid, [('state', '=', 'confirmed'), ('company_id','=', company.id)], context=context) #Type = stockable product?
move_obj.action_assign(cr, uid, confirmed_ids, context=context)
finally:
if use_new_cursor:
diff --git a/addons/stock/security/ir.model.access.csv b/addons/stock/security/ir.model.access.csv
index f2f049d92c3..d1679e0e522 100644
--- a/addons/stock/security/ir.model.access.csv
+++ b/addons/stock/security/ir.model.access.csv
@@ -49,3 +49,5 @@ access_product_pricelist_item_stock_manager,product.pricelist.item stock_manager
access_board_stock_user,board.board user,board.model_board_board,stock.group_stock_user,1,1,0,0
access_stock_warehouse_orderpoint,stock.warehouse.orderpoint,model_stock_warehouse_orderpoint,stock.group_stock_user,1,0,0,0
access_stock_warehouse_orderpoint_system,stock.warehouse.orderpoint system,model_stock_warehouse_orderpoint,stock.group_stock_manager,1,1,1,1
+access_stock_quant_manager,stock.quant manager,model_stock_quant,stock.group_stock_manager,1,1,1,1
+access_stock_quant_user,stock.quant user,model_stock_quant,stock.group_stock_user,1,1,1,0
diff --git a/addons/stock/stock.py b/addons/stock/stock.py
index 2069fa3cb54..cff7efdba53 100644
--- a/addons/stock/stock.py
+++ b/addons/stock/stock.py
@@ -205,7 +205,7 @@ class stock_quant(osv.osv):
# FP Note: TODO: implement domain preference that tries to retrieve first with this domain
# This will be used for reservation
- def quants_get(self, cr, uid, location, product, qty, domain=None, domain_preference=[], context=None):
+ def quants_get(self, cr, uid, location, product, qty, domain=None, prefered_order=False, reservedcontext=None, context=None):
"""
Use the removal strategies of product to search for the correct quants
If you inherit, put the super at the end of your method.
@@ -215,20 +215,20 @@ class stock_quant(osv.osv):
:qty in UoM of product
:lot_id NOT USED YET !
"""
+
result= []
- # for domain2 in domain_preference:
- # result = self._quants_get(cr, uid, location, product, qty, domain+domain2, context=context)
- # qty = remaining quants qty - sum(result)
+ if domain is None:
+ domain = []
+
domain = domain or [('qty','>',0.0)]
- if location:
+ if location and qty>0:
removal_strategy = self.pool.get('stock.location').get_removal_strategy(cr, uid, location, product, context=context) or 'fifo'
if removal_strategy=='fifo':
- result += self._quants_get_fifo(cr, uid, location, product, qty, domain, context=context)
+ result += self._quants_get_fifo(cr, uid, location, product, qty, domain, prefered_order=prefered_order, context=context)
elif removal_strategy=='lifo':
- result += self._quants_get_lifo(cr, uid, location, product, qty, domain, context=context)
+ result += self._quants_get_lifo(cr, uid, location, product, qty, domain, prefered_order=prefered_order, context=context)
else:
raise osv.except_osv(_('Error!'),_('Removal strategy %s not implemented.' % (removal_strategy,)))
-
return result
@@ -293,7 +293,6 @@ class stock_quant(osv.osv):
'cost': 0.0,
}, context=context)
-
#TODO: In case of negative quants no removal strategy is applied -> actually removal strategy should be reversed? OR just by in_date?
quants2 = self._quants_get_order(cr, uid, False, quant.product_id, -quant_neg.qty, domain=[('propagated_from_id','=',quant_neg.id)], orderby='in_date', context=None)
for qu2, qt2 in quants2:
@@ -309,12 +308,19 @@ class stock_quant(osv.osv):
def _price_update(self, cr, uid, quant, newprice, context=None):
self.write(cr, uid, [quant.id], {'cost': newprice}, context=context)
+
+ def quants_unreserve(self, cr, uid, move, context=None):
+ cr.execute('update stock_quant set reservation_id=NULL where reservation_id=%s', (move.id,))
+ return True
+
+
#
# Implementation of removal strategies
+ # If it can not reserve, it will return a tuple (None, qty)
#
def _quants_get_order(self, cr, uid, location, product, quantity, domain=[], orderby='in_date', context=None):
domain += location and [('location_id', 'child_of', location.id)] or []
- domain += [('product_id','=',product.id), ('reservation_id', '=', False)] + domain
+ domain += [('product_id','=',product.id)] + domain
res = []
offset = 0
while quantity > 0:
@@ -335,13 +341,19 @@ class stock_quant(osv.osv):
- def _quants_get_fifo(self, cr, uid, location, product, quantity, domain=[], context=None):
+ def _quants_get_fifo(self, cr, uid, location, product, quantity, domain=[], prefered_order=False,context=None):
+ order = 'in_date'
+ if prefered_order:
+ order = prefered_order + ', in_date'
return self._quants_get_order(cr, uid, location, product, quantity,
- domain, 'in_date', context=context)
+ domain, order, context=context)
- def _quants_get_lifo(self, cr, uid, location, product, quantity, domain=[], context=None):
+ def _quants_get_lifo(self, cr, uid, location, product, quantity, domain=[], prefered_order=False, context=None):
+ order = 'in_date desc'
+ if prefered_order:
+ order = prefered_order + ', in_date desc'
return self._quants_get_order(cr, uid, location, product, quantity,
- domain, 'in_date desc', context=context)
+ domain, order, context=context)
# Return the company owning the location if any
def _location_owner(self, cr, uid, quant, location, context=None):
@@ -1206,6 +1218,15 @@ class stock_move(osv.osv):
['location_id','location_dest_id'])
]
+
+ def copy(self, cr, uid, id, default=None, context=None):
+ if default is None:
+ default = {}
+ default = default.copy()
+ default['procurement_group'] = False
+ return super(stock_move, self).copy(cr, uid, id, default, context)
+
+
def _default_location_destination(self, cr, uid, context=None):
""" Gets default address of partner for destination location
@return: Address id or False
@@ -1576,8 +1597,10 @@ class stock_move(osv.osv):
""" Changes the state to assigned.
@return: True
"""
- self.action_assign(cr, uid, ids, context=context)
- return self.write(cr, uid, ids, {'state': 'assigned'})
+ done = self.action_assign(cr, uid, ids, context=context)
+ self.write(cr, uid, list(set(ids) - set(done)), {'state': 'assigned'})
+ return True
+
def cancel_assign(self, cr, uid, ids, context=None):
""" Changes the state to confirmed.
@@ -1593,6 +1616,7 @@ class stock_move(osv.osv):
quant_obj = self.pool.get("stock.quant")
uom_obj = self.pool.get("product.uom")
done = []
+ pickings = set()
for move in self.browse(cr, uid, ids, context=context):
if move.state not in ('confirmed', 'waiting'):
continue
@@ -1602,19 +1626,24 @@ class stock_move(osv.osv):
else:
qty = uom_obj._compute_qty(cr, uid, move.product_uom.id, move.product_qty, move.product_id.uom_id.id)
dp = []
- if move.move_orig_ids:
- for m2 in move.move_orig_ids:
- for q in m2.quant_ids:
- dp.append(q.id)
- quants = quant_obj.quants_get(cr, uid, move.location_id, move.product_id, qty, domain_preference=dp and [('id', 'in', dp)], context=context)
+ for m2 in move.move_orig_ids:
+ for q in m2.quant_ids:
+ dp.append(str(q.id))
+ domain = ['|', ('reservation_id', '=', False), ('reservation_id', '=', move.id)]
+ quants = quant_obj.quants_get(cr, uid, move.location_id, move.product_id, qty, domain=domain, prefered_order = dp and ('id not in ('+','.join(dp)+')') or False, context=context)
+ #Will only reserve physical quants, no negative
quant_obj.quants_reserve(cr, uid, quants, move, context=context)
- #TODO Should have a check to really check it passed
- sum_of_qua = 0
- for qua in quants:
- sum_of_qua += qua[1]
- if qua >= move.product_uom_qty:
+ # the total quantity is provided by existing quants
+ if all(map(lambda x:x[0], quants)):
done.append(move.id)
+ pickings.add(move.picking_id and move.picking_id.id or False)
self.write(cr, uid, done, {'state': 'assigned'})
+ #TODO: More elegant way to solve this
+ pick_obj = self.pool.get("stock.picking")
+ for pick in list(pickings):
+ if pick_obj.test_assigned(cr, uid, [pick]):
+ pick_obj.write(cr, uid, [pick], {'state': 'assigned'})
+ return done
#
@@ -1646,6 +1675,8 @@ class stock_move(osv.osv):
def action_done(self, cr, uid, ids, context=None):
""" Makes the move done and if all moves are done, it will finish the picking.
+ If quants are not assigned yet, it should assign them
+ Putaway strategies should be applied
@return:
"""
context = context or {}
@@ -1656,13 +1687,33 @@ class stock_move(osv.osv):
if todo:
self.action_confirm(cr, uid, todo, context=context)
- qty = move.product_qty
- # for qty, location_id in move_id.prefered_location_ids:
- # quants = quant_obj.quants_get(cr, uid, move.location_id, move.product_id, qty, context=context)
- # quant_obj.quants_move(cr, uid, quants, move, location_dest_id, context=context)
- # should replace the above 2 lines
- quants = quant_obj.quants_get(cr, uid, move.location_id, move.product_id, qty, context=context)
- quant_obj.quants_move(cr, uid, quants, move, context=context)
+ for move in self.browse(cr, uid, ids, context=context):
+ qty = move.product_uom_qty
+
+ # for qty, location_id in move_id.prefered_location_ids:
+ # quants = quant_obj.quants_get(cr, uid, move.location_id, move.product_id, qty, context=context)
+ # quant_obj.quants_move(cr, uid, quants, move, location_dest_id, context=context)
+ # should replace the above 2 lines
+ domain = ['|', ('reservation_id', '=', False), ('reservation_id', '=', move.id)]
+ quants = quant_obj.quants_get(cr, uid, move.location_id, move.product_id, qty, domain=domain, prefered_order = 'reservation_id<>'+str(move.id), context=context)
+ #Will move all quants_get and as such create negative quants
+ quant_obj.quants_move(cr, uid, quants, move, context=context)
+ quant_obj.quants_unreserve(cr, uid, move, context=context)
+
+ #
+ #Check moves that were pushed
+ if move.move_dest_id.state in ('waiting', 'confirmed'):
+ other_upstream_move_ids = self.search(cr, uid, [('id','!=',move.id),('state','not in',['done','cancel']),
+ ('move_dest_id','=',move.move_dest_id.id)], context=context)
+ #If no other moves for the move that got pushed:
+ if not other_upstream_move_ids and move.move_dest_id.state in ('waiting', 'confirmed'):
+ self.action_assign(cr, uid, [move.move_dest_id.id], context=context)
+
+# quants = quant_obj.search(cr, uid, [('history_ids', 'in', move.id), ('location_id', '=', move.location_dest_id.id), ('reservation_id', '=', False)], context=context)
+# if quants:
+# quant_obj.write(cr, uid, quants, {'reservation_id':move.move_dest_id.id}, context=context)
+ if move.move_dest_id.auto_validate: #TO be removed everywhere
+ self.action_done(cr, uid, [move.move_dest_id.id], context=context)
self.write(cr, uid, ids, {'state': 'done', 'date': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)}, context=context)
diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml
index 26aa382ddf6..22a6ee18b07 100644
--- a/addons/stock/stock_view.xml
+++ b/addons/stock/stock_view.xml
@@ -1553,12 +1553,43 @@
+
+
+
+
+ {}
+ Quants
+ stock.quant
+
+
+
+
+ stock.quant.tree
+ stock.quant
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+