diff --git a/addons/procurement/procurement.py b/addons/procurement/procurement.py index df8534e703d..2848858a088 100644 --- a/addons/procurement/procurement.py +++ b/addons/procurement/procurement.py @@ -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) diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 6cd6e815974..bdb87ad61f4 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -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' diff --git a/addons/sale/sale_workflow.xml b/addons/sale/sale_workflow.xml index 831a0a9d12c..1dd093f4e1d 100644 --- a/addons/sale/sale_workflow.xml +++ b/addons/sale/sale_workflow.xml @@ -262,9 +262,7 @@ - procurement.order - procurement_lines_get() - test_state('finished') + ship_end diff --git a/addons/sale_stock/sale_stock.py b/addons/sale_stock/sale_stock.py index 20bac48c562..ffe96dd6cb3 100644 --- a/addons/sale_stock/sale_stock.py +++ b/addons/sale_stock/sale_stock.py @@ -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: diff --git a/addons/stock/procurement.py b/addons/stock/procurement.py index b8d7a03c9f2..2024b016ef3 100644 --- a/addons/stock/procurement.py +++ b/addons/stock/procurement.py @@ -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) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 938a759da46..2a316fa3461 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -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):