[WIP] Simplify procurement check and sales order workflow and that way phantom boms will work
This commit is contained in:
parent
739a1d0051
commit
3b636a5df0
|
@ -195,7 +195,7 @@ class procurement_order(osv.osv):
|
||||||
|
|
||||||
def run(self, cr, uid, ids, context=None):
|
def run(self, cr, uid, ids, context=None):
|
||||||
for procurement_id in ids:
|
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
|
#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)
|
#will fetch all the ids again)
|
||||||
procurement = self.browse(cr, uid, procurement_id, context=context)
|
procurement = self.browse(cr, uid, procurement_id, context=context)
|
||||||
|
|
|
@ -741,49 +741,7 @@ class sale_order(osv.osv):
|
||||||
order.write(val)
|
order.write(val)
|
||||||
return True
|
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):
|
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
|
'''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)
|
order_line.append(line)
|
||||||
return {'value': {'order_line': order_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
|
# TODO add a field price_unit_uos
|
||||||
# - update it on change product and unit price
|
# - update it on change product and unit price
|
||||||
|
@ -924,6 +896,8 @@ class sale_order_line(osv.osv):
|
||||||
'delay': 0.0,
|
'delay': 0.0,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _get_line_qty(self, cr, uid, line, context=None):
|
def _get_line_qty(self, cr, uid, line, context=None):
|
||||||
if line.product_uos:
|
if line.product_uos:
|
||||||
return line.product_uos_qty or 0.0
|
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'),
|
'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):
|
class product_product(osv.Model):
|
||||||
_inherit = 'product.product'
|
_inherit = 'product.product'
|
||||||
|
|
|
@ -262,9 +262,7 @@
|
||||||
<record id="trans_ship_ship_end" model="workflow.transition">
|
<record id="trans_ship_ship_end" model="workflow.transition">
|
||||||
<field name="act_from" ref="act_ship"/>
|
<field name="act_from" ref="act_ship"/>
|
||||||
<field name="act_to" ref="act_ship_end"/>
|
<field name="act_to" ref="act_ship_end"/>
|
||||||
<field name="trigger_model">procurement.order</field>
|
<field name="signal">ship_end</field>
|
||||||
<field name="trigger_expr_id">procurement_lines_get()</field>
|
|
||||||
<field name="condition">test_state('finished')</field>
|
|
||||||
</record>
|
</record>
|
||||||
|
|
||||||
<record id="trans_ship_ship_except" model="workflow.transition">
|
<record id="trans_ship_ship_except" model="workflow.transition">
|
||||||
|
|
|
@ -350,15 +350,6 @@ class sale_order_line(osv.osv):
|
||||||
class stock_move(osv.osv):
|
class stock_move(osv.osv):
|
||||||
_inherit = 'stock.move'
|
_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):
|
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)
|
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:
|
if move.procurement_id and move.procurement_id.sale_line_id:
|
||||||
|
|
|
@ -222,27 +222,18 @@ class procurement_order(osv.osv):
|
||||||
'''
|
'''
|
||||||
if procurement.rule_id and procurement.rule_id.action == 'move':
|
if procurement.rule_id and procurement.rule_id.action == 'move':
|
||||||
uom_obj = self.pool.get('product.uom')
|
uom_obj = self.pool.get('product.uom')
|
||||||
done_test_list = []
|
cancel_test_list = [x.state == 'cancel' for x in procurement.move_ids]
|
||||||
done_cancel_test_list = []
|
done_cancel_test_list = [x.state in ('done', 'cancel') for x in procurement.move_ids]
|
||||||
qty_done = 0
|
at_least_one_cancel = any(cancel_test_list)
|
||||||
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)
|
|
||||||
all_done_or_cancel = all(done_cancel_test_list)
|
all_done_or_cancel = all(done_cancel_test_list)
|
||||||
|
all_cancel = all(cancel_test_list)
|
||||||
if not all_done_or_cancel:
|
if not all_done_or_cancel:
|
||||||
return False
|
return False
|
||||||
elif all_done_or_cancel and procurement.product_qty == qty_done:
|
elif all_done_or_cancel and not all_cancel:
|
||||||
return True
|
return True
|
||||||
elif at_least_one_done:
|
elif all_cancel:
|
||||||
#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
|
|
||||||
self.message_post(cr, uid, [procurement.id], body=_('All stock moves have been cancelled for this procurement.'), context=context)
|
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 False
|
||||||
|
|
||||||
return super(procurement_order, self)._check(cr, uid, procurement, context)
|
return super(procurement_order, self)._check(cr, uid, procurement, context)
|
||||||
|
|
|
@ -2148,20 +2148,18 @@ class stock_move(osv.osv):
|
||||||
if move.propagate:
|
if move.propagate:
|
||||||
procurement_ids = procurement_obj.search(cr, uid, [('move_dest_id', '=', move.id)], context=context)
|
procurement_ids = procurement_obj.search(cr, uid, [('move_dest_id', '=', move.id)], context=context)
|
||||||
procurement_obj.cancel(cr, uid, procurement_ids, context=context)
|
procurement_obj.cancel(cr, uid, procurement_ids, context=context)
|
||||||
elif move.move_dest_id:
|
else:
|
||||||
#cancel chained moves
|
if move.move_dest_id:
|
||||||
if move.propagate:
|
if move.propagate:
|
||||||
self.action_cancel(cr, uid, [move.move_dest_id.id], context=context)
|
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
|
elif move.move_dest_id.state == 'waiting':
|
||||||
# only the last procurement which will go into exception, instead of all procurements
|
#If waiting, the chain will be broken and we are not sure if we can still wait for it (=> could take from stock instead)
|
||||||
# along the chain going into exception. We need to check if there are no split moves not cancelled however
|
self.write(cr, uid, [move.move_dest_id.id], {'state': 'confirmed'}, context=context)
|
||||||
if move.procurement_id:
|
if move.procurement_id:
|
||||||
proc = move.procurement_id
|
# Does the same as procurement check, only eliminating a refresh
|
||||||
if all([x.state == 'cancel' for x in proc.move_ids if x.id != move.id]):
|
proc = move.procurement_id
|
||||||
procurement_obj.write(cr, uid, [proc.id], {'state': 'cancel'})
|
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)
|
|
||||||
return self.write(cr, uid, ids, {'state': 'cancel', 'move_dest_id': False}, context=context)
|
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):
|
def _check_package_from_moves(self, cr, uid, ids, context=None):
|
||||||
|
@ -3934,7 +3932,8 @@ class stock_warehouse_orderpoint(osv.osv):
|
||||||
continue
|
continue
|
||||||
procurement_qty = uom_obj._compute_qty_obj(cr, uid, procurement.product_uom, procurement.product_qty, procurement.product_id.uom_id, context=context)
|
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:
|
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
|
#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
|
procurement_qty -= move.product_qty
|
||||||
qty += procurement_qty
|
qty += procurement_qty
|
||||||
|
@ -3950,7 +3949,6 @@ class stock_warehouse_orderpoint(osv.osv):
|
||||||
for rule in self.browse(cr, uid, ids, context=context):
|
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:
|
if rule.product_id.uom_id.category_id.id != rule.product_uom.category_id.id:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def action_view_proc_to_process(self, cr, uid, ids, context=None):
|
def action_view_proc_to_process(self, cr, uid, ids, context=None):
|
||||||
|
|
Loading…
Reference in New Issue