From 55588eba5791e5d3dcd0f2e43654a4a95f9c9ed5 Mon Sep 17 00:00:00 2001 From: Arnold Moyaux Date: Fri, 8 Jan 2021 13:54:23 +0000 Subject: [PATCH] [IMP] stock: reserve on return to select delivered lot Since return moves are linked to their original move, it's easy to manage reservation in those case. We just don't bypass the reservation when it comes from a reservation outside our stock in order to manage the computation on linked moves and reserve pieces that were not yet return. Also add a return picking type for 2 reasons: - Allow to select existing lot. - Display reserved move line for an incoming picking. Note that this picking type is just a default configuration and could be modify by the user without any issue. closes odoo/odoo#64384 Related: odoo/enterprise#20605 Signed-off-by: Arnold Moyaux --- addons/mrp/models/stock_move.py | 5 +- addons/mrp_subcontracting/models/__init__.py | 1 - .../mrp_subcontracting/models/stock_move.py | 4 +- .../models/stock_move_line.py | 15 --- addons/stock/models/stock_move.py | 109 +++++++++++------- addons/stock/models/stock_move_line.py | 18 ++- addons/stock/models/stock_warehouse.py | 22 +++- addons/stock/tests/test_move2.py | 69 +++++++++++ addons/stock/tests/test_packing.py | 2 +- 9 files changed, 169 insertions(+), 76 deletions(-) delete mode 100644 addons/mrp_subcontracting/models/stock_move_line.py diff --git a/addons/mrp/models/stock_move.py b/addons/mrp/models/stock_move.py index d8ab7a42284..8b3bb5401cf 100644 --- a/addons/mrp/models/stock_move.py +++ b/addons/mrp/models/stock_move.py @@ -367,8 +367,9 @@ class StockMove(models.Model): return True return False - def _should_bypass_reservation(self): - res = super(StockMove, self)._should_bypass_reservation() + def _should_bypass_reservation(self, forced_location=False): + res = super(StockMove, self)._should_bypass_reservation( + forced_location=forced_location) return bool(res and not self.production_id) def _key_assign_picking(self): diff --git a/addons/mrp_subcontracting/models/__init__.py b/addons/mrp_subcontracting/models/__init__.py index fc944c6b955..7e7c3993a14 100644 --- a/addons/mrp_subcontracting/models/__init__.py +++ b/addons/mrp_subcontracting/models/__init__.py @@ -5,7 +5,6 @@ from . import product from . import res_company from . import res_partner from . import stock_move -from . import stock_move_line from . import stock_picking from . import stock_quant from . import stock_rule diff --git a/addons/mrp_subcontracting/models/stock_move.py b/addons/mrp_subcontracting/models/stock_move.py index 59cb6108465..c7ed7d7112b 100644 --- a/addons/mrp_subcontracting/models/stock_move.py +++ b/addons/mrp_subcontracting/models/stock_move.py @@ -197,9 +197,9 @@ class StockMove(models.Model): return False return super()._should_bypass_set_qty_producing() - def _should_bypass_reservation(self): + def _should_bypass_reservation(self, forced_location=False): """ If the move is subcontracted then ignore the reservation. """ - should_bypass_reservation = super(StockMove, self)._should_bypass_reservation() + should_bypass_reservation = super()._should_bypass_reservation(forced_location=forced_location) if not should_bypass_reservation and self.is_subcontract: return True return should_bypass_reservation diff --git a/addons/mrp_subcontracting/models/stock_move_line.py b/addons/mrp_subcontracting/models/stock_move_line.py deleted file mode 100644 index 6dfb789b041..00000000000 --- a/addons/mrp_subcontracting/models/stock_move_line.py +++ /dev/null @@ -1,15 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import models, fields - - -class StockMoveLine(models.Model): - _inherit = 'stock.move.line' - - def _should_bypass_reservation(self, location): - """ If the move line is subcontracted then ignore the reservation. """ - should_bypass_reservation = super(StockMoveLine, self)._should_bypass_reservation(location) - if not should_bypass_reservation and self.move_id.is_subcontract: - return True - return should_bypass_reservation diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py index 663d2b83acd..a2b87d0996f 100644 --- a/addons/stock/models/stock_move.py +++ b/addons/stock/models/stock_move.py @@ -1320,13 +1320,16 @@ class StockMove(models.Model): self.env['stock.move.line'].create(self._prepare_move_line_vals(quantity=quantity, reserved_quant=reserved_quant)) return taken_quantity - def _should_bypass_reservation(self): + def _should_bypass_reservation(self, forced_location=False): self.ensure_one() - return self.location_id.should_bypass_reservation() or self.product_id.type != 'product' + location = forced_location or self.location_id + return location.should_bypass_reservation() or self.product_id.type != 'product' # necessary hook to be able to override move reservation to a restrict lot, owner, pack, location... def _get_available_quantity(self, location_id, lot_id=None, package_id=None, owner_id=None, strict=False, allow_negative=False): self.ensure_one() + if location_id.should_bypass_reservation(): + return self.product_qty return self.env['stock.quant']._get_available_quantity(self.product_id, location_id, lot_id=lot_id, package_id=package_id, owner_id=owner_id, strict=strict, allow_negative=allow_negative) def _action_assign(self): @@ -1335,6 +1338,46 @@ class StockMove(models.Model): equal to its `product_qty`. If it is less, the stock move is considered partially available. """ + + def _get_available_move_lines(move): + move_lines_in = move.move_orig_ids.filtered(lambda m: m.state == 'done').mapped('move_line_ids') + keys_in_groupby = ['location_dest_id', 'lot_id', 'result_package_id', 'owner_id'] + + def _keys_in_sorted(ml): + return (ml.location_dest_id.id, ml.lot_id.id, ml.result_package_id.id, ml.owner_id.id) + + grouped_move_lines_in = {} + for k, g in groupby(sorted(move_lines_in, key=_keys_in_sorted), key=itemgetter(*keys_in_groupby)): + qty_done = 0 + for ml in g: + qty_done += ml.product_uom_id._compute_quantity(ml.qty_done, ml.product_id.uom_id) + grouped_move_lines_in[k] = qty_done + move_lines_out_done = (move.move_orig_ids.mapped('move_dest_ids') - move)\ + .filtered(lambda m: m.state in ['done'])\ + .mapped('move_line_ids') + # As we defer the write on the stock.move's state at the end of the loop, there + # could be moves to consider in what our siblings already took. + moves_out_siblings = move.move_orig_ids.mapped('move_dest_ids') - move + moves_out_siblings_to_consider = moves_out_siblings & (StockMove.browse(assigned_moves_ids) + StockMove.browse(partially_available_moves_ids)) + reserved_moves_out_siblings = moves_out_siblings.filtered(lambda m: m.state in ['partially_available', 'assigned']) + move_lines_out_reserved = (reserved_moves_out_siblings | moves_out_siblings_to_consider).mapped('move_line_ids') + keys_out_groupby = ['location_id', 'lot_id', 'package_id', 'owner_id'] + + def _keys_out_sorted(ml): + return (ml.location_id.id, ml.lot_id.id, ml.package_id.id, ml.owner_id.id) + + grouped_move_lines_out = {} + for k, g in groupby(sorted(move_lines_out_done, key=_keys_out_sorted), key=itemgetter(*keys_out_groupby)): + qty_done = 0 + for ml in g: + qty_done += ml.product_uom_id._compute_quantity(ml.qty_done, ml.product_id.uom_id) + grouped_move_lines_out[k] = qty_done + for k, g in groupby(sorted(move_lines_out_reserved, key=_keys_out_sorted), key=itemgetter(*keys_out_groupby)): + grouped_move_lines_out[k] = sum(self.env['stock.move.line'].concat(*list(g)).mapped('product_qty')) + available_move_lines = {key: grouped_move_lines_in[key] - grouped_move_lines_out.get(key, 0) for key in grouped_move_lines_in} + # pop key if the quantity available amount to 0 + return dict((k, v) for k, v in available_move_lines.items() if v) + StockMove = self.env['stock.move'] assigned_moves_ids = OrderedSet() partially_available_moves_ids = OrderedSet() @@ -1349,10 +1392,26 @@ class StockMove(models.Model): missing_reserved_quantity = move.product_uom._compute_quantity(missing_reserved_uom_quantity, move.product_id.uom_id, rounding_method='HALF-UP') if move._should_bypass_reservation(): # create the move line(s) but do not impact quants - if move.product_id.tracking == 'serial' and (move.picking_type_id.use_create_lots or move.picking_type_id.use_existing_lots): + if move.move_orig_ids: + available_move_lines = _get_available_move_lines(move) + for (location_id, lot_id, package_id, owner_id), quantity in available_move_lines.items(): + qty_added = min(missing_reserved_quantity, quantity) + move_line_vals = move._prepare_move_line_vals(qty_added) + move_line_vals.update({ + 'location_id': location_id.id, + 'lot_id': lot_id.id, + 'lot_name': lot_id.name, + 'owner_id': owner_id.id, + }) + move_line_vals_list.append(move_line_vals) + missing_reserved_quantity -= qty_added + if float_is_zero(missing_reserved_quantity, precision_rounding=move.product_id.uom_id.rounding): + break + + if missing_reserved_quantity and move.product_id.tracking == 'serial' and (move.picking_type_id.use_create_lots or move.picking_type_id.use_existing_lots): for i in range(0, int(missing_reserved_quantity)): move_line_vals_list.append(move._prepare_move_line_vals(quantity=1)) - else: + elif missing_reserved_quantity: to_update = move.move_line_ids.filtered(lambda ml: ml.product_uom_id == move.product_uom and ml.location_id == move.location_id and ml.location_dest_id == move.location_dest_id and @@ -1361,7 +1420,8 @@ class StockMove(models.Model): not ml.package_id and not ml.owner_id) if to_update: - to_update[0].product_uom_qty += missing_reserved_uom_quantity + to_update[0].product_uom_qty += move.product_id.uom_id._compute_quantity( + missing_reserved_quantity, move.product_uom, rounding_method='HALF-UP') else: move_line_vals_list.append(move._prepare_move_line_vals(quantity=missing_reserved_quantity)) assigned_moves_ids.add(move.id) @@ -1394,44 +1454,7 @@ class StockMove(models.Model): # `qty_done` is in `ml.product_uom_id` and, as we will later increase # the reserved quantity on the quants, convert it here in # `product_id.uom_id` (the UOM of the quants is the UOM of the product). - move_lines_in = move.move_orig_ids.filtered(lambda m: m.state == 'done').mapped('move_line_ids') - keys_in_groupby = ['location_dest_id', 'lot_id', 'result_package_id', 'owner_id'] - - def _keys_in_sorted(ml): - return (ml.location_dest_id.id, ml.lot_id.id, ml.result_package_id.id, ml.owner_id.id) - - grouped_move_lines_in = {} - for k, g in groupby(sorted(move_lines_in, key=_keys_in_sorted), key=itemgetter(*keys_in_groupby)): - qty_done = 0 - for ml in g: - qty_done += ml.product_uom_id._compute_quantity(ml.qty_done, ml.product_id.uom_id) - grouped_move_lines_in[k] = qty_done - move_lines_out_done = (move.move_orig_ids.mapped('move_dest_ids') - move)\ - .filtered(lambda m: m.state in ['done'])\ - .mapped('move_line_ids') - # As we defer the write on the stock.move's state at the end of the loop, there - # could be moves to consider in what our siblings already took. - moves_out_siblings = move.move_orig_ids.mapped('move_dest_ids') - move - moves_out_siblings_to_consider = moves_out_siblings & (StockMove.browse(assigned_moves_ids) + StockMove.browse(partially_available_moves_ids)) - reserved_moves_out_siblings = moves_out_siblings.filtered(lambda m: m.state in ['partially_available', 'assigned']) - move_lines_out_reserved = (reserved_moves_out_siblings | moves_out_siblings_to_consider).mapped('move_line_ids') - keys_out_groupby = ['location_id', 'lot_id', 'package_id', 'owner_id'] - - def _keys_out_sorted(ml): - return (ml.location_id.id, ml.lot_id.id, ml.package_id.id, ml.owner_id.id) - - grouped_move_lines_out = {} - for k, g in groupby(sorted(move_lines_out_done, key=_keys_out_sorted), key=itemgetter(*keys_out_groupby)): - qty_done = 0 - for ml in g: - qty_done += ml.product_uom_id._compute_quantity(ml.qty_done, ml.product_id.uom_id) - grouped_move_lines_out[k] = qty_done - for k, g in groupby(sorted(move_lines_out_reserved, key=_keys_out_sorted), key=itemgetter(*keys_out_groupby)): - grouped_move_lines_out[k] = sum(self.env['stock.move.line'].concat(*list(g)).mapped('product_qty')) - available_move_lines = {key: grouped_move_lines_in[key] - grouped_move_lines_out.get(key, 0) for key in grouped_move_lines_in.keys()} - # pop key if the quantity available amount to 0 - available_move_lines = dict((k, v) for k, v in available_move_lines.items() if v) - + available_move_lines = _get_available_move_lines(move) if not available_move_lines: continue for move_line in move.move_line_ids.filtered(lambda m: m.product_qty): diff --git a/addons/stock/models/stock_move_line.py b/addons/stock/models/stock_move_line.py index 1ecd487bc1b..a0b287fccc6 100644 --- a/addons/stock/models/stock_move_line.py +++ b/addons/stock/models/stock_move_line.py @@ -340,7 +340,7 @@ class StockMoveLine(models.Model): new_product_uom_qty = ml.product_qty # Unreserve the old charateristics of the move line. - if not ml._should_bypass_reservation(ml.location_id): + if not ml.move_id._should_bypass_reservation(ml.location_id): try: Quant._update_reserved_quantity(ml.product_id, ml.location_id, -ml.product_qty, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id, strict=True) except UserError: @@ -351,7 +351,7 @@ class StockMoveLine(models.Model): raise # Reserve the maximum available of the new charateristics of the move line. - if not ml._should_bypass_reservation(updates.get('location_id', ml.location_id)): + if not ml.move_id._should_bypass_reservation(updates.get('location_id', ml.location_id)): reserved_qty = 0 try: q = Quant._update_reserved_quantity(ml.product_id, updates.get('location_id', ml.location_id), new_product_uom_qty, lot_id=updates.get('lot_id', ml.lot_id), @@ -395,7 +395,7 @@ class StockMoveLine(models.Model): result_package_id = updates.get('result_package_id', ml.result_package_id) owner_id = updates.get('owner_id', ml.owner_id) quantity = ml.move_id.product_uom._compute_quantity(qty_done, ml.move_id.product_id.uom_id, rounding_method='HALF-UP') - if not ml._should_bypass_reservation(location_id): + if not ml.move_id._should_bypass_reservation(location_id): ml._free_reservation(product_id, location_id, quantity, lot_id=lot_id, package_id=package_id, owner_id=owner_id) if not float_is_zero(quantity, precision_digits=precision): available_qty, in_date = Quant._update_available_quantity(product_id, location_id, -quantity, lot_id=lot_id, package_id=package_id, owner_id=owner_id) @@ -406,7 +406,7 @@ class StockMoveLine(models.Model): taken_from_untracked_qty = min(untracked_qty, abs(available_qty)) Quant._update_available_quantity(product_id, location_id, -taken_from_untracked_qty, lot_id=False, package_id=package_id, owner_id=owner_id) Quant._update_available_quantity(product_id, location_id, taken_from_untracked_qty, lot_id=lot_id, package_id=package_id, owner_id=owner_id) - if not ml._should_bypass_reservation(location_id): + if not ml.move_id._should_bypass_reservation(location_id): ml._free_reservation(ml.product_id, location_id, untracked_qty, lot_id=False, package_id=package_id, owner_id=owner_id) Quant._update_available_quantity(product_id, location_dest_id, quantity, lot_id=lot_id, package_id=result_package_id, owner_id=owner_id, in_date=in_date) @@ -451,7 +451,7 @@ class StockMoveLine(models.Model): precision = self.env['decimal.precision'].precision_get('Product Unit of Measure') for ml in self: # Unlinking a move line should unreserve. - if ml.product_id.type == 'product' and not ml._should_bypass_reservation(ml.location_id) and not float_is_zero(ml.product_qty, precision_digits=precision): + if ml.product_id.type == 'product' and not ml.move_id._should_bypass_reservation(ml.location_id) and not float_is_zero(ml.product_qty, precision_digits=precision): try: self.env['stock.quant']._update_reserved_quantity(ml.product_id, ml.location_id, -ml.product_qty, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id, strict=True) except UserError: @@ -553,12 +553,12 @@ class StockMoveLine(models.Model): rounding = ml.product_uom_id.rounding # if this move line is force assigned, unreserve elsewhere if needed - if not ml._should_bypass_reservation(ml.location_id) and float_compare(ml.qty_done, ml.product_uom_qty, precision_rounding=rounding) > 0: + if not ml.move_id._should_bypass_reservation(ml.location_id) and float_compare(ml.qty_done, ml.product_uom_qty, precision_rounding=rounding) > 0: qty_done_product_uom = ml.product_uom_id._compute_quantity(ml.qty_done, ml.product_id.uom_id, rounding_method='HALF-UP') extra_qty = qty_done_product_uom - ml.product_qty ml._free_reservation(ml.product_id, ml.location_id, extra_qty, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id, ml_ids_to_ignore=ml_ids_to_ignore) # unreserve what's been reserved - if not ml._should_bypass_reservation(ml.location_id) and ml.product_id.type == 'product' and ml.product_qty: + if not ml.move_id._should_bypass_reservation(ml.location_id) and ml.product_id.type == 'product' and ml.product_qty: try: Quant._update_reserved_quantity(ml.product_id, ml.location_id, -ml.product_qty, lot_id=ml.lot_id, package_id=ml.package_id, owner_id=ml.owner_id, strict=True) except UserError: @@ -702,10 +702,6 @@ class StockMoveLine(models.Model): self.env['stock.move.line'].browse(to_unlink_candidate_ids).unlink() move_to_recompute_state._recompute_state() - def _should_bypass_reservation(self, location): - self.ensure_one() - return location.should_bypass_reservation() or self.product_id.type != 'product' - def _get_aggregated_product_quantities(self, **kwargs): """ Returns a dictionary of products (key = id+name+description+uom) and corresponding values of interest. diff --git a/addons/stock/models/stock_warehouse.py b/addons/stock/models/stock_warehouse.py index e1afcfb4e22..beb4343e0b0 100644 --- a/addons/stock/models/stock_warehouse.py +++ b/addons/stock/models/stock_warehouse.py @@ -76,6 +76,7 @@ class Warehouse(models.Model): out_type_id = fields.Many2one('stock.picking.type', 'Out Type', check_company=True) in_type_id = fields.Many2one('stock.picking.type', 'In Type', check_company=True) int_type_id = fields.Many2one('stock.picking.type', 'Internal Type', check_company=True) + return_type_id = fields.Many2one('stock.picking.type', 'Return Type', check_company=True) crossdock_route_id = fields.Many2one('stock.location.route', 'Crossdock Route', ondelete='restrict') reception_route_id = fields.Many2one('stock.location.route', 'Receipt Route', ondelete='restrict') delivery_route_id = fields.Many2one('stock.location.route', 'Delivery Route', ondelete='restrict') @@ -332,7 +333,7 @@ class Warehouse(models.Model): warehouse_data[picking_type] = PickingType.create(values).id if 'out_type_id' in warehouse_data: - PickingType.browse(warehouse_data['out_type_id']).write({'return_picking_type_id': warehouse_data.get('in_type_id', False)}) + PickingType.browse(warehouse_data['out_type_id']).write({'return_picking_type_id': warehouse_data.get('return_type_id', False)}) if 'in_type_id' in warehouse_data: PickingType.browse(warehouse_data['in_type_id']).write({'return_picking_type_id': warehouse_data.get('out_type_id', False)}) return warehouse_data @@ -915,6 +916,10 @@ class Warehouse(models.Model): 'int_type_id': { 'barcode': self.code.replace(" ", "").upper() + "-INTERNAL", }, + 'return_type_id': { + 'default_location_dest_id': output_loc.id, + 'barcode': self.code.replace(" ", "").upper() + "-RETURNS", + }, } def _get_picking_type_create_values(self, max_sequence): @@ -976,6 +981,16 @@ class Warehouse(models.Model): 'sequence': max_sequence + 2, 'sequence_code': 'INT', 'company_id': self.company_id.id, + }, 'return_type_id': { + 'name': _('Returns'), + 'code': 'incoming', + 'use_create_lots': False, + 'use_existing_lots': True, + 'default_location_src_id': False, + 'sequence': max_sequence + 6, + 'show_reserved': True, + 'sequence_code': 'IN', + 'company_id': self.company_id.id, }, }, max_sequence + 6 @@ -1009,6 +1024,11 @@ class Warehouse(models.Model): 'prefix': self.code + '/INT/', 'padding': 5, 'company_id': self.company_id.id, }, + 'return_type_id': { + 'name': self.name + ' ' + _('Sequence return'), + 'prefix': self.code + '/RET/', 'padding': 5, + 'company_id': self.company_id.id, + }, } def _format_rulename(self, from_loc, dest_loc, suffix): diff --git a/addons/stock/tests/test_move2.py b/addons/stock/tests/test_move2.py index 64fe4f08691..1cecf9c7076 100644 --- a/addons/stock/tests/test_move2.py +++ b/addons/stock/tests/test_move2.py @@ -740,6 +740,75 @@ class TestPickShip(TestStockCommon): self.assertEqual(self.env['stock.quant']._get_available_quantity(self.productA, return_location), 5.0) self.assertEqual(len(self.env['stock.quant'].search([('product_id', '=', self.productA.id), ('quantity', '!=', 0)])), 2) + def test_return_lot(self): + """ With two distinct deliveries for the same product tracked by lot, ensure that the + return of the second picking suggest the lot from the picking returned. + """ + self.productA.tracking = 'lot' + lot1 = self.env['stock.production.lot'].create({ + 'name': 'lot1', + 'product_id': self.productA.id, + 'company_id': self.env.company.id, + }) + lot2 = self.env['stock.production.lot'].create({ + 'name': 'lot2', + 'product_id': self.productA.id, + 'company_id': self.env.company.id, + }) + lot3 = self.env['stock.production.lot'].create({ + 'name': 'lot3', + 'product_id': self.productA.id, + 'company_id': self.env.company.id, + }) + stock_location = self.env['stock.location'].browse(self.stock_location) + + self.env['stock.quant']._update_available_quantity(self.productA, stock_location, 7.0, lot_id=lot1) + self.env['stock.quant']._update_available_quantity(self.productA, stock_location, 7.0, lot_id=lot2) + self.env['stock.quant']._update_available_quantity(self.productA, stock_location, 7.0, lot_id=lot3) + + picking_pick, picking_client = self.create_pick_ship() + picking_pick.action_confirm() + picking_pick.action_assign() + for move_line in picking_pick.move_line_ids: + move_line.qty_done = move_line.product_uom_qty + picking_pick._action_done() + picking_client.action_confirm() + picking_client.action_assign() + for move_line in picking_client.move_line_ids: + move_line.qty_done = move_line.product_uom_qty + picking_client._action_done() + + picking_pick, picking_client = self.create_pick_ship() + picking_pick.action_confirm() + picking_pick.action_assign() + for move_line in picking_pick.move_line_ids: + move_line.qty_done = move_line.product_uom_qty + picking_pick._action_done() + picking_client.action_confirm() + picking_client.action_assign() + for move_line in picking_client.move_line_ids: + move_line.qty_done = move_line.product_uom_qty + picking_client._action_done() + + # Following FIFO strategy, First picking should have empty lot1 and took 3 of lot2. + # So the second picking contains 4 lot2 and 6 lot3 + self.assertEqual(picking_client.move_line_ids[0].lot_id, lot2) + self.assertEqual(picking_client.move_line_ids[0].qty_done, 4) + self.assertEqual(picking_client.move_line_ids[1].lot_id, lot3) + self.assertEqual(picking_client.move_line_ids[1].qty_done, 6) + + stock_return_picking_form = Form(self.env['stock.return.picking'].with_context( + active_ids=picking_client.ids, active_id=picking_client.ids[0], active_model='stock.picking')) + stock_return_picking = stock_return_picking_form.save() + stock_return_picking.product_return_moves.quantity = 10.0 + return_pick = self.env['stock.picking'].browse(stock_return_picking.create_returns()['res_id']) + + self.assertEqual(len(return_pick.move_line_ids), 2) + self.assertEqual(return_pick.move_line_ids[0].lot_id, lot2) + self.assertEqual(return_pick.move_line_ids[0].product_uom_qty, 4) + self.assertEqual(return_pick.move_line_ids[1].lot_id, lot3) + self.assertEqual(return_pick.move_line_ids[1].product_uom_qty, 6) + self.assertEqual(return_pick.picking_type_id, picking_client.location_id.warehouse_id.return_type_id) class TestSinglePicking(TestStockCommon): def test_backorder_1(self): diff --git a/addons/stock/tests/test_packing.py b/addons/stock/tests/test_packing.py index 599d1bd9f32..becf1eb792c 100644 --- a/addons/stock/tests/test_packing.py +++ b/addons/stock/tests/test_packing.py @@ -218,7 +218,7 @@ class TestPacking(TestPackingCommon): }) picking = self.env['stock.picking'].create({ 'picking_type_id': self.warehouse.in_type_id.id, - 'location_id': self.stock_location.id, + 'location_id': self.customer_location.id, 'location_dest_id': self.stock_location.id, 'state': 'draft', })