[WIP] Preferred order and trigger workflow?

bzr revid: jco@openerp.com-20130716101540-ae6tvjbjta1mguua
This commit is contained in:
Josse Colpaert 2013-07-16 12:15:40 +02:00
parent 918a83e069
commit bfecef01b1
4 changed files with 120 additions and 35 deletions

View File

@ -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:

View File

@ -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

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
49 access_board_stock_user board.board user board.model_board_board stock.group_stock_user 1 1 0 0
50 access_stock_warehouse_orderpoint stock.warehouse.orderpoint model_stock_warehouse_orderpoint stock.group_stock_user 1 0 0 0
51 access_stock_warehouse_orderpoint_system stock.warehouse.orderpoint system model_stock_warehouse_orderpoint stock.group_stock_manager 1 1 1 1
52 access_stock_quant_manager stock.quant manager model_stock_quant stock.group_stock_manager 1 1 1 1
53 access_stock_quant_user stock.quant user model_stock_quant stock.group_stock_user 1 1 1 0

View File

@ -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)

View File

@ -1553,12 +1553,43 @@
</xpath>
</field>
</record>
<record model="ir.actions.act_window" id="quantsact">
<field name="context">{}</field>
<field name="name">Quants</field>
<field name="res_model">stock.quant</field>
</record>
<record model="ir.ui.view" id="view_stock_quant_tree">
<field name="name">stock.quant.tree</field>
<field name="model">stock.quant</field>
<field eval="10" name="priority"/>
<field name="arch" type="xml">
<tree string="Quants">
<field name="product_id"/>
<field name="qty"/>
<field name="location_id"/>
<field name="in_date"/>
<field name="reservation_id"/>
<field name="propagated_from_id"/>
</tree>
</field>
</record>
<!-- Procurements are located in Warehouse menu hierarchy, MRP users should come to Stock application to use it. -->
<menuitem id="menu_stock_sched" name="Schedulers" parent="stock.menu_stock_root" sequence="4" groups="stock.group_stock_manager"/>
<menuitem action="procurement.action_compute_schedulers" id="menu_stock_proc_schedulers" parent="menu_stock_sched" sequence="20" groups="stock.group_stock_manager"/>
<menuitem action="procurement.procurement_exceptions" id="menu_stock_procurement_action" parent="menu_stock_sched" sequence="50" groups="stock.group_stock_manager"/>
<menuitem id="menu_stock_procurement" name="Automatic Procurements" parent="stock.menu_stock_configuration" sequence="5"/>
<menuitem action="action_orderpoint_form" id="menu_stock_order_points" parent="stock.menu_stock_configuration" sequence="10"/>
<menuitem id="quants" name="Quants" parent="stock.menu_stock_configuration" action="quantsact" groups="base.group_no_one"/>
<record model="ir.actions.act_window" id="product_open_orderpoint">