[WIP] Simplify procurement check and sales order workflow and that way phantom boms will work

This commit is contained in:
Josse Colpaert 2014-07-15 18:38:06 +02:00
parent 739a1d0051
commit 3b636a5df0
6 changed files with 53 additions and 87 deletions

View File

@ -195,7 +195,7 @@ class procurement_order(osv.osv):
def run(self, cr, uid, ids, context=None):
for procurement_id in ids:
#we intentionnaly do the browse under the for loop to avoid caching all ids which would be ressource greedy
#we intentionnaly do the browse under the for loop to avoid caching all ids which would be resource greedy
#and useless as we'll make a refresh later that will invalidate all the cache (and thus the next iteration
#will fetch all the ids again)
procurement = self.browse(cr, uid, procurement_id, context=context)

View File

@ -741,49 +741,7 @@ class sale_order(osv.osv):
order.write(val)
return True
# if mode == 'finished':
# returns True if all lines are done, False otherwise
# if mode == 'canceled':
# returns True if there is at least one canceled line, False otherwise
def test_state(self, cr, uid, ids, mode, *args):
assert mode in ('finished', 'canceled'), _("invalid mode for test_state")
finished = True
canceled = False
write_done_ids = []
write_cancel_ids = []
for order in self.browse(cr, uid, ids, context={}):
#TODO: Need to rethink what happens when cancelling
for line in order.order_line:
states = [x.state for x in line.procurement_ids]
cancel = states and all([x == 'cancel' for x in states])
doneorcancel = all([x in ('done', 'cancel') for x in states])
if cancel:
canceled = True
if line.state != 'exception':
write_cancel_ids.append(line.id)
if not doneorcancel:
finished = False
if doneorcancel and not cancel:
write_done_ids.append(line.id)
if write_done_ids:
self.pool.get('sale.order.line').write(cr, uid, write_done_ids, {'state': 'done'})
if write_cancel_ids:
self.pool.get('sale.order.line').write(cr, uid, write_cancel_ids, {'state': 'exception'})
if mode == 'finished':
return finished
elif mode == 'canceled':
return canceled
def procurement_lines_get(self, cr, uid, ids, *args):
res = []
for order in self.browse(cr, uid, ids, context={}):
for line in order.order_line:
res += [x.id for x in line.procurement_ids]
return res
def onchange_fiscal_position(self, cr, uid, ids, fiscal_position, order_lines, context=None):
'''Update taxes of order lines for each line where a product is defined
@ -828,6 +786,20 @@ class sale_order(osv.osv):
order_line.append(line)
return {'value': {'order_line': order_line}}
def test_procurements_done(self, cr, uid, ids, context=None):
for sale in self.browse(cr, uid, ids, context=context):
for line in sale.order_line:
if not all([x.state == 'done' for x in line.procurement_ids]):
return False
return True
def test_procurements_except(self, cr, uid, ids, context=None):
for sale in self.browse(cr, uid, ids, context=context):
for line in sale.order_line:
if any([x.state == 'cancel' for x in line.procurement_ids]):
return True
return False
# TODO add a field price_unit_uos
# - update it on change product and unit price
@ -924,6 +896,8 @@ class sale_order_line(osv.osv):
'delay': 0.0,
}
def _get_line_qty(self, cr, uid, line, context=None):
if line.product_uos:
return line.product_uos_qty or 0.0
@ -1268,6 +1242,20 @@ class procurement_order(osv.osv):
'sale_line_id': fields.many2one('sale.order.line', string='Sale Order Line'),
}
def write(self, cr, uid, ids, vals, context=None):
if isinstance(ids, (int, long)):
ids = [ids]
res = super(procurement_order, self).write(cr, uid, ids, vals, context=context)
from openerp import workflow
if vals.get('state') in ['done', 'cancel', 'exception']:
for proc in self.browse(cr, uid, ids, context=context):
if proc.sale_line_id and proc.sale_line_id.order_id and proc.move_ids:
order_id = proc.sale_line_id.order_id.id
if self.pool.get('sale.order').test_procurements_done(cr, uid, [order_id], context=context):
workflow.trg_validate(uid, 'sale.order', order_id, 'ship_end', cr)
if self.pool.get('sale.order').test_procurements_except(cr, uid, [order_id], context=context):
workflow.trg_validate(uid, 'sale.order', order_id, 'ship_except', cr)
return res
class product_product(osv.Model):
_inherit = 'product.product'

View File

@ -262,9 +262,7 @@
<record id="trans_ship_ship_end" model="workflow.transition">
<field name="act_from" ref="act_ship"/>
<field name="act_to" ref="act_ship_end"/>
<field name="trigger_model">procurement.order</field>
<field name="trigger_expr_id">procurement_lines_get()</field>
<field name="condition">test_state('finished')</field>
<field name="signal">ship_end</field>
</record>
<record id="trans_ship_ship_except" model="workflow.transition">

View File

@ -350,15 +350,6 @@ class sale_order_line(osv.osv):
class stock_move(osv.osv):
_inherit = 'stock.move'
def action_cancel(self, cr, uid, ids, context=None):
sale_ids = []
for move in self.browse(cr, uid, ids, context=context):
if move.procurement_id and move.procurement_id.sale_line_id:
sale_ids.append(move.procurement_id.sale_line_id.order_id.id)
if sale_ids:
self.pool.get('sale.order').signal_workflow(cr, uid, sale_ids, 'ship_except')
return super(stock_move, self).action_cancel(cr, uid, ids, context=context)
def _create_invoice_line_from_vals(self, cr, uid, move, invoice_line_vals, context=None):
invoice_line_id = super(stock_move, self)._create_invoice_line_from_vals(cr, uid, move, invoice_line_vals, context=context)
if move.procurement_id and move.procurement_id.sale_line_id:

View File

@ -222,27 +222,18 @@ class procurement_order(osv.osv):
'''
if procurement.rule_id and procurement.rule_id.action == 'move':
uom_obj = self.pool.get('product.uom')
done_test_list = []
done_cancel_test_list = []
qty_done = 0
for move in procurement.move_ids:
done_test_list.append(move.state == 'done')
done_cancel_test_list.append(move.state in ('done', 'cancel'))
qty_done += move.product_qty if move.state == 'done' else 0
qty_done = uom_obj._compute_qty(cr, uid, procurement.product_id.uom_id.id, qty_done, procurement.product_uom.id)
at_least_one_done = any(done_test_list)
cancel_test_list = [x.state == 'cancel' for x in procurement.move_ids]
done_cancel_test_list = [x.state in ('done', 'cancel') for x in procurement.move_ids]
at_least_one_cancel = any(cancel_test_list)
all_done_or_cancel = all(done_cancel_test_list)
all_cancel = all(cancel_test_list)
if not all_done_or_cancel:
return False
elif all_done_or_cancel and procurement.product_qty == qty_done:
elif all_done_or_cancel and not all_cancel:
return True
elif at_least_one_done:
#some move cancelled and some validated
self.message_post(cr, uid, [procurement.id], body=_('Some stock moves have been cancelled for this procurement. Run the procurement again to trigger a move for the remaining quantity or change the procurement quantity to finish it directly'), context=context)
else:
#all move are cancelled
elif all_cancel:
self.message_post(cr, uid, [procurement.id], body=_('All stock moves have been cancelled for this procurement.'), context=context)
self.write(cr, uid, [procurement.id], {'state': 'exception'}, context=context)
self.write(cr, uid, [procurement.id], {'state': 'cancel'}, context=context)
return False
return super(procurement_order, self)._check(cr, uid, procurement, context)

View File

@ -2148,20 +2148,18 @@ class stock_move(osv.osv):
if move.propagate:
procurement_ids = procurement_obj.search(cr, uid, [('move_dest_id', '=', move.id)], context=context)
procurement_obj.cancel(cr, uid, procurement_ids, context=context)
elif move.move_dest_id:
#cancel chained moves
if move.propagate:
self.action_cancel(cr, uid, [move.move_dest_id.id], context=context)
# If we have a long chain of moves to be cancelled, it is easier for the user to handle
# only the last procurement which will go into exception, instead of all procurements
# along the chain going into exception. We need to check if there are no split moves not cancelled however
if move.procurement_id:
proc = move.procurement_id
if all([x.state == 'cancel' for x in proc.move_ids if x.id != move.id]):
procurement_obj.write(cr, uid, [proc.id], {'state': 'cancel'})
elif move.move_dest_id.state == 'waiting':
self.write(cr, uid, [move.move_dest_id.id], {'state': 'confirmed'}, context=context)
else:
if move.move_dest_id:
if move.propagate:
self.action_cancel(cr, uid, [move.move_dest_id.id], context=context)
elif move.move_dest_id.state == 'waiting':
#If waiting, the chain will be broken and we are not sure if we can still wait for it (=> could take from stock instead)
self.write(cr, uid, [move.move_dest_id.id], {'state': 'confirmed'}, context=context)
if move.procurement_id:
# Does the same as procurement check, only eliminating a refresh
proc = move.procurement_id
if all([x.state == 'cancel' for x in proc.move_ids if x.id != move.id]):
procurement_obj.write(cr, uid, [proc.id], {'state': 'cancel'})
return self.write(cr, uid, ids, {'state': 'cancel', 'move_dest_id': False}, context=context)
def _check_package_from_moves(self, cr, uid, ids, context=None):
@ -3934,7 +3932,8 @@ class stock_warehouse_orderpoint(osv.osv):
continue
procurement_qty = uom_obj._compute_qty_obj(cr, uid, procurement.product_uom, procurement.product_qty, procurement.product_id.uom_id, context=context)
for move in procurement.move_ids:
if move.state not in ('draft', 'cancel'):
#need to add the moves in draft as they aren't in the virtual quantity + moves that have not been created yet
if move.state not in ('draft'):
#if move is already confirmed, assigned or done, the virtual stock is already taking this into account so it shouldn't be deducted
procurement_qty -= move.product_qty
qty += procurement_qty
@ -3950,7 +3949,6 @@ class stock_warehouse_orderpoint(osv.osv):
for rule in self.browse(cr, uid, ids, context=context):
if rule.product_id.uom_id.category_id.id != rule.product_uom.category_id.id:
return False
return True
def action_view_proc_to_process(self, cr, uid, ids, context=None):