diff --git a/addons/stock/stock.py b/addons/stock/stock.py index a79ded69f9a..b3a0ac57049 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -993,11 +993,11 @@ class stock_picking(osv.osv): moves_to_unreserve = [] pack_line_to_unreserve = [] for picking in self.browse(cr, uid, picking_ids, context=context): - moves_to_unreserve += [m.id for m in picking.move_lines] + moves_to_unreserve += [m.id for m in picking.move_lines if m.state not in ('done', 'cancel')] pack_line_to_unreserve += [p.id for p in picking.pack_operation_ids] - if pack_line_to_unreserve: - self.pool.get('stock.pack.operation').unlink(cr, uid, pack_line_to_unreserve, context=context) if moves_to_unreserve: + if pack_line_to_unreserve: + self.pool.get('stock.pack.operation').unlink(cr, uid, pack_line_to_unreserve, context=context) self.pool.get('stock.move').do_unreserve(cr, uid, moves_to_unreserve, context=context) def do_recompute_remaining_quantities(self, cr, uid, picking_ids, context=None): @@ -1487,6 +1487,8 @@ class stock_move(osv.osv): def do_unreserve(self, cr, uid, move_ids, context=None): quant_obj = self.pool.get("stock.quant") for move in self.browse(cr, uid, move_ids, context=context): + if move.state in ('done', 'cancel'): + raise osv.except_osv(_('Operation Forbidden!'), _('Cannot unreserve a done move')) quant_obj.quants_unreserve(cr, uid, move, context=context) putaway_values = [] for putaway_rec in move.putaway_ids: @@ -1521,7 +1523,10 @@ class stock_move(osv.osv): def _push_apply(self, cr, uid, moves, context=None): push_obj = self.pool.get("stock.location.path") for move in moves: - if not move.move_dest_id: + #1) if the move is already chained, there is no need to check push rules + #2) if the move is a returned move, we don't want to check push rules, as returning a returned move is the only decent way + # to receive goods without triggering the push rules again (which would duplicate chained operations) + if not move.move_dest_id and not move.origin_returned_move_id: domain = [('location_from_id', '=', move.location_dest_id.id)] if move.warehouse_id: domain += ['|', ('warehouse_id', '=', move.warehouse_id.id), ('warehouse_id', '=', False)] diff --git a/addons/stock/wizard/stock_return_picking.py b/addons/stock/wizard/stock_return_picking.py index 95703679ccb..112b233bf1f 100644 --- a/addons/stock/wizard/stock_return_picking.py +++ b/addons/stock/wizard/stock_return_picking.py @@ -19,8 +19,6 @@ # ############################################################################## -import time - from openerp.osv import osv, fields from openerp.tools.translate import _ import openerp.addons.decimal_precision as dp @@ -34,7 +32,7 @@ class stock_return_picking_line(osv.osv_memory): 'quantity': fields.float("Quantity", digits_compute=dp.get_precision('Product Unit of Measure'), required=True), 'wizard_id': fields.many2one('stock.return.picking', string="Wizard"), 'move_id': fields.many2one('stock.move', "Move"), - 'lot_id': fields.related('move_id', 'quant_ids', 'lot_id', type="many2one", relation='stock.production.lot', string='Serial Number', readonly=True), + 'lot_id': fields.many2one('stock.production.lot', 'Serial Number', help="Used to choose the lot/serial number of the product returned"), } @@ -43,12 +41,7 @@ class stock_return_picking(osv.osv_memory): _description = 'Return Picking' _columns = { 'product_return_moves': fields.one2many('stock.return.picking.line', 'wizard_id', 'Moves'), - 'move_dest_exists': fields.boolean('Chained Move Exists', readonly=True, help="Technical field used to hide 'move_dest_method' and help tooltip if not needed"), - 'move_dest_method': fields.selection([('cancel', 'Cancel'), ('create', 'Recreate'), ('nothing', 'Fix Manually')], string="Chained Picking Resolution"), - } - - _defaults = { - 'move_dest_method': 'nothing', + 'move_dest_exists': fields.boolean('Chained Move Exists', readonly=True, help="Technical field used to hide help tooltip if not needed"), } def default_get(self, cr, uid, fields, context=None): @@ -66,10 +59,10 @@ class stock_return_picking(osv.osv_memory): context = {} res = super(stock_return_picking, self).default_get(cr, uid, fields, context=context) record_id = context and context.get('active_id', False) or False + uom_obj = self.pool.get('product.uom') pick_obj = self.pool.get('stock.picking') pick = pick_obj.browse(cr, uid, record_id, context=context) quant_obj = self.pool.get("stock.quant") - move_obj = self.pool.get("stock.move") chained_move_exist = False if pick: if pick.state != 'done': @@ -78,13 +71,13 @@ class stock_return_picking(osv.osv_memory): for move in pick.move_lines: if move.move_dest_id: chained_move_exist = True - #Sum the quants in that location that can be returned (they must belong to the moves that were included in the returned picking) + #Sum the quants in that location that can be returned (they should have been moved by the moves that were included in the returned picking) qty = 0 - quant_search = quant_obj.search(cr, uid, [('history_ids', 'in', move.id), ('qty', '>', 0.0), - ('location_id', 'child_of', move.location_dest_id.id)], context=context) + quant_search = quant_obj.search(cr, uid, [('history_ids', 'in', move.id), ('qty', '>', 0.0), ('location_id', 'child_of', move.location_dest_id.id)], context=context) for quant in quant_obj.browse(cr, uid, quant_search, context=context): if not quant.reservation_id or quant.reservation_id.origin_returned_move_id.id != move.id: qty += quant.qty + qty = uom_obj._compute_qty(cr, uid, move.product_id.uom_id.id, qty, move.product_uom.id) result1.append({'product_id': move.product_id.id, 'quantity': qty, 'move_id': move.id}) if len(result1) == 0: @@ -106,11 +99,23 @@ class stock_return_picking(osv.osv_memory): pick = pick_obj.browse(cr, uid, record_id, context=context) data = self.read(cr, uid, ids[0], context=context) returned_lines = 0 - + # Cancel assignment of existing chained assigned moves - moves_to_unreserve = [x.move_dest_id.id for x in pick.move_lines if x.move_dest_id and x.move_dest_id.quant_ids] + moves_to_unreserve = [] + for move in pick.move_lines: + to_check_moves = [move.move_dest_id] + while to_check_moves: + current_move = to_check_moves.pop() + if current_move.state not in ('done', 'cancel') and current_move.reserved_quant_ids: + moves_to_unreserve.append(current_move.id) + split_move_ids = move_obj.search(cr, uid, [('split_from', '=', current_move.id)], context=context) + if split_move_ids: + to_check_moves += move_obj.browse(cr, uid, split_move_ids, context=context) + if moves_to_unreserve: move_obj.do_unreserve(cr, uid, moves_to_unreserve, context=context) + #break the link between moves in order to be able to fix them later if needed + move_obj.write(cr, uid, moves_to_unreserve, {'move_orig_ids': False}, context=context) #Create new picking for returned products pick_type_id = pick.picking_type_id.return_picking_type_id and pick.picking_type_id.return_picking_type_id.id or pick.picking_type_id.id @@ -119,8 +124,8 @@ class stock_return_picking(osv.osv_memory): 'picking_type_id': pick_type_id, 'state': 'draft', 'origin': pick.name, - },context=context) - + }, context=context) + for data_get in data_obj.browse(cr, uid, data['product_return_moves'], context=context): move = data_get.move_id if not move: @@ -138,25 +143,9 @@ class stock_return_picking(osv.osv_memory): 'location_dest_id': move.location_id.id, 'origin_returned_move_id': move.id, 'procure_method': 'make_to_stock', + 'restrict_lot_id': data_get.lot_id.id, }) - if data['move_dest_method'] == 'cancel': - if move.move_dest_id and move.move_dest_id.state not in ['done', 'cancel']: - res = move_obj.split(cr, uid, move.move_dest_id, new_qty, context=context) - move_obj.action_cancel(cr, uid, [res], context=context) - if data['move_dest_method'] == 'create': - if move.move_dest_id and move.move_dest_id.state not in ['done', 'cancel']: - res = move_obj.split(cr, uid, move.move_dest_id, new_qty, context=context) - new_move = move_obj.copy(cr, uid, move.id, { - 'location_id': move.location_id.id, - 'location_dest_id': move.location_dest_id.id, - 'product_uom_qty': new_qty, - 'product_uos_qty': uom_obj._compute_qty(cr, uid, move.product_uom.id, new_qty, move.product_uos.id), - 'move_dest_id': res, - 'picking_id': False, - }) - move_obj.action_confirm(cr, uid, [new_move], context=context) - if not returned_lines: raise osv.except_osv(_('Warning!'), _("Please specify at least one non-zero quantity.")) @@ -177,13 +166,13 @@ class stock_return_picking(osv.osv_memory): new_picking_id, pick_type_id = self._create_returns(cr, uid, ids, context=context) # Override the context to disable all the potential filters that could have been set previously ctx = { - 'search_default_picking_type_id': pick_type_id, - 'search_default_draft': False, - 'search_default_assigned': False, - 'search_default_confirmed': False, - 'search_default_ready': False, - 'search_default_late': False, - 'search_default_available': False, + 'search_default_picking_type_id': pick_type_id, + 'search_default_draft': False, + 'search_default_assigned': False, + 'search_default_confirmed': False, + 'search_default_ready': False, + 'search_default_late': False, + 'search_default_available': False, } return { 'domain': "[('id', 'in', [" + str(new_picking_id) + "])]", diff --git a/addons/stock/wizard/stock_return_picking_view.xml b/addons/stock/wizard/stock_return_picking_view.xml index e383ba3c9aa..b26918746a1 100644 --- a/addons/stock/wizard/stock_return_picking_view.xml +++ b/addons/stock/wizard/stock_return_picking_view.xml @@ -43,18 +43,16 @@
-
-

This picking is chained with at least another operation. Choose what you want to do with it:

-
    -
  • Cancel: use this option if you're not going to receive the goods you're returning. The next picking will be split and the returned part will be cancelled.
  • -
  • Recreate: use this option if you plan to receive the returned goods later on. The next picking will be split and linked to another incoming picking for the same quantity
  • -
  • Fix manually: use this option if none of the 2 other suits you or if you want to process the next picking in a single time.
  • -
+
+

This picking appears to be chained with another operation. Later, if you receive the goods you are returning now, make sure to reverse the returned picking in order to avoid logistic rules to be applied again (which would create duplicated operations)

- - - - + + + + + + +