From ff7df19bd3bb80470a8a3b58b061b89405ffc2c2 Mon Sep 17 00:00:00 2001 From: William Henrotin Date: Tue, 18 Sep 2018 09:32:37 +0200 Subject: [PATCH] [IMP] stock: choose destination wizard Purpose : during a transfer, if some move lines have different destination locations and the user click on 'put in pack' button, an warning is triggered. This message, saying : 'You cannot put in the same pack move lines having different destination location', is replaced by a wizard allowing the user to choose a unique destination location. This location will be set as location_dest_id on all transfer's stock move lines. After that, the wizard calls again the _put_in_pack() method where all the logic remains the same. Task : 1872345 --- addons/delivery/models/stock_picking.py | 11 ++- .../wizard/choose_delivery_package.py | 4 - addons/stock/__manifest__.py | 1 + addons/stock/models/stock_picking.py | 25 ++++++- addons/stock/tests/test_packing.py | 73 +++++++++++++++++++ addons/stock/wizard/__init__.py | 1 + .../stock/wizard/stock_package_destination.py | 29 ++++++++ .../stock_package_destination_views.xml | 35 +++++++++ 8 files changed, 171 insertions(+), 8 deletions(-) create mode 100644 addons/stock/wizard/stock_package_destination.py create mode 100644 addons/stock/wizard/stock_package_destination_views.xml diff --git a/addons/delivery/models/stock_picking.py b/addons/delivery/models/stock_picking.py index e3cc5f55ed9..a9afc258004 100644 --- a/addons/delivery/models/stock_picking.py +++ b/addons/delivery/models/stock_picking.py @@ -113,6 +113,9 @@ class StockPicking(models.Model): @api.multi def put_in_pack(self): + res = super(StockPicking, self).put_in_pack() + if isinstance(res, dict) and res.get('type'): + return res if self.carrier_id and self.carrier_id.delivery_type not in ['base_on_rule', 'fixed']: view_id = self.env.ref('delivery.choose_delivery_package_view_form').id return { @@ -123,10 +126,14 @@ class StockPicking(models.Model): 'view_id': view_id, 'views': [(view_id, 'form')], 'target': 'new', - 'context': dict(self.env.context, current_package_carrier_type=self.carrier_id.delivery_type), + 'context': dict( + self.env.context, + current_package_carrier_type=self.carrier_id.delivery_type, + default_stock_quant_package_id=res.id + ), } else: - return self._put_in_pack() + return res @api.multi def action_send_confirmation_email(self): diff --git a/addons/delivery/wizard/choose_delivery_package.py b/addons/delivery/wizard/choose_delivery_package.py index ed10e8727aa..70c06f06dac 100644 --- a/addons/delivery/wizard/choose_delivery_package.py +++ b/addons/delivery/wizard/choose_delivery_package.py @@ -55,10 +55,6 @@ class ChooseDeliveryPackage(models.TransientModel): return {'warning': warning_mess} def put_in_pack(self): - picking_id = self.env['stock.picking'].browse(self.env.context['active_id']) - if not self.stock_quant_package_id: - stock_quant_package = picking_id._put_in_pack() - self.stock_quant_package_id = stock_quant_package # write shipping weight and product_packaging on 'stock_quant_package' if needed if self.delivery_packaging_id: self.stock_quant_package_id.packaging_id = self.delivery_packaging_id diff --git a/addons/stock/__manifest__.py b/addons/stock/__manifest__.py index a9010b63ac2..a6c42ce4111 100644 --- a/addons/stock/__manifest__.py +++ b/addons/stock/__manifest__.py @@ -47,6 +47,7 @@ 'wizard/stock_warn_insufficient_qty_views.xml', 'wizard/product_replenish_views.xml', 'wizard/stock_track_confirmation_views.xml', + 'wizard/stock_package_destination_views.xml', 'views/res_partner_views.xml', 'views/product_strategy_views.xml', diff --git a/addons/stock/models/stock_picking.py b/addons/stock/models/stock_picking.py index a798926bfdb..888d2637830 100644 --- a/addons/stock/models/stock_picking.py +++ b/addons/stock/models/stock_picking.py @@ -986,6 +986,26 @@ class Picking(models.Model): return _explore(self.env['stock.picking'], self.env['stock.move'], moves) + def check_destinations(self): + if len(self.move_line_ids.filtered(lambda l: l.qty_done > 0 and not l.result_package_id).mapped('location_dest_id')) > 1: + view_id = self.env.ref('stock.stock_package_destination_form_view').id + wiz = self.env['stock.package.destination'].create({ + 'picking_id': self.id, + 'location_dest_id': self.move_line_ids[0].location_dest_id.id, + }) + return { + 'name': _('Choose destination location'), + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'stock.package.destination', + 'view_id': view_id, + 'views': [(view_id, 'form')], + 'type': 'ir.actions.act_window', + 'res_id': wiz.id, + 'target': 'new' + } + else: + return {} def _put_in_pack(self): package = False @@ -994,8 +1014,6 @@ class Picking(models.Model): if move_line_ids: move_lines_to_pack = self.env['stock.move.line'] package = self.env['stock.quant.package'].create({}) - if len(move_line_ids.mapped('location_dest_id')) > 1: - raise UserError('You cannot put in the same pack move lines having different destination locations') for ml in move_line_ids: if float_compare(ml.qty_done, ml.product_uom_qty, precision_rounding=ml.product_uom_id.rounding) >= 0: @@ -1027,6 +1045,9 @@ class Picking(models.Model): return package def put_in_pack(self): + res = self.check_destinations() + if res.get('type'): + return res return self._put_in_pack() def button_scrap(self): diff --git a/addons/stock/tests/test_packing.py b/addons/stock/tests/test_packing.py index c119bd18d10..c67d001247b 100644 --- a/addons/stock/tests/test_packing.py +++ b/addons/stock/tests/test_packing.py @@ -180,3 +180,76 @@ class TestPacking(TransactionCase): self.assertEqual(package_level_reserved.location_id.id, shelf1_location.id, 'The reserved package level must be reserved in shelf1') self.assertEqual(package_level_confirmed.location_id.id, self.stock_location.id, 'The not reserved package should keep its location') self.assertEqual(picking.package_level_ids.mapped('is_done'), [True, True], 'Both package should still done') + + def test_put_in_pack_to_different_location(self): + """ Hitting 'Put in pack' button while some move lines go to different + location should trigger a wizard. This wizard applies the same destination + location to all the move lines + """ + shelf1_location = self.env['stock.location'].create({ + 'name': 'shelf1', + 'usage': 'internal', + 'location_id': self.stock_location.id, + }) + shelf2_location = self.env['stock.location'].create({ + 'name': 'shelf2', + 'usage': 'internal', + 'location_id': self.stock_location.id, + }) + picking = self.env['stock.picking'].create({ + 'picking_type_id': self.warehouse.in_type_id.id, + 'location_id': self.stock_location.id, + 'location_dest_id': self.stock_location.id, + 'state': 'draft', + }) + ship_move_a = self.env['stock.move'].create({ + 'name': 'move 1', + 'product_id': self.productA.id, + 'product_uom_qty': 5.0, + 'product_uom': self.productA.uom_id.id, + 'location_id': self.customer_location.id, + 'location_dest_id': shelf1_location.id, + 'picking_id': picking.id, + 'state': 'draft', + }) + picking.action_confirm() + picking.action_assign() + picking.move_line_ids.filtered(lambda ml: ml.product_id == self.productA).qty_done = 5.0 + picking.put_in_pack() + pack1 = self.env['stock.quant.package'].search([])[-1] + picking.write({ + 'move_line_ids': [(0, 0, { + 'product_id': self.productB.id, + 'product_uom_qty': 7.0, + 'qty_done': 7.0, + 'product_uom_id': self.productB.uom_id.id, + 'location_id': self.customer_location.id, + 'location_dest_id': shelf2_location.id, + 'picking_id': picking.id, + 'state': 'confirmed', + })] + }) + picking.write({ + 'move_line_ids': [(0, 0, { + 'product_id': self.productA.id, + 'product_uom_qty': 5.0, + 'qty_done': 5.0, + 'product_uom_id': self.productA.uom_id.id, + 'location_id': self.customer_location.id, + 'location_dest_id': shelf1_location.id, + 'picking_id': picking.id, + 'state': 'confirmed', + })] + }) + wizard_values = picking.put_in_pack() + wizard = self.env[(wizard_values.get('res_model'))].browse(wizard_values.get('res_id')) + wizard.location_dest_id = shelf2_location.id + wizard.action_done() + picking.action_done() + pack2 = self.env['stock.quant.package'].search([])[-1] + self.assertEqual(pack2.location_id.id, shelf2_location.id, 'The package must be stored in shelf2') + self.assertEqual(pack1.location_id.id, shelf1_location.id, 'The package must be stored in shelf1') + qp1 = pack2.quant_ids[0] + qp2 = pack2.quant_ids[1] + self.assertEqual(qp1.quantity, 5, 'The quant has not the good quantity') + self.assertEqual(qp2.quantity, 7, 'The quant has not the good quantity') diff --git a/addons/stock/wizard/__init__.py b/addons/stock/wizard/__init__.py index 34ec326e15a..fd4115a1bfc 100644 --- a/addons/stock/wizard/__init__.py +++ b/addons/stock/wizard/__init__.py @@ -12,3 +12,4 @@ from . import stock_rules_report from . import stock_warn_insufficient_qty from . import product_replenish from . import stock_track_confirmation +from . import stock_package_destination diff --git a/addons/stock/wizard/stock_package_destination.py b/addons/stock/wizard/stock_package_destination.py new file mode 100644 index 00000000000..9dbab24c6b5 --- /dev/null +++ b/addons/stock/wizard/stock_package_destination.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, fields, models + + +class ChooseDestinationLocation(models.TransientModel): + _name = 'stock.package.destination' + + picking_id = fields.Many2one('stock.picking', required=True) + move_line_ids = fields.Many2many('stock.move.line', 'Products', compute='_compute_move_line_ids', required=True) + location_dest_id = fields.Many2one('stock.location', 'Destination location', required=True) + filtered_location = fields.One2many(comodel_name='stock.location', compute='_filter_location') + + @api.one + @api.depends('picking_id') + def _compute_move_line_ids(self): + self.move_line_ids = self.picking_id.move_line_ids.filtered(lambda l: l.qty_done > 0 and not l.result_package_id) + + @api.one + @api.depends('move_line_ids') + def _filter_location(self): + self.filtered_location = self.move_line_ids.mapped('location_dest_id') + + def action_done(self): + # set the same location on each move line and pass again in _put_in_pack + for line in self.move_line_ids: + line.location_dest_id = self.location_dest_id + return self.picking_id.put_in_pack() diff --git a/addons/stock/wizard/stock_package_destination_views.xml b/addons/stock/wizard/stock_package_destination_views.xml new file mode 100644 index 00000000000..783468d9988 --- /dev/null +++ b/addons/stock/wizard/stock_package_destination_views.xml @@ -0,0 +1,35 @@ + + + + stock.package.destination.view + stock.package.destination + +
+
+ You are trying to put products going to different locations into the same package +
+
+ + + + + + + + +
+
+ Where do you want to send the products ? +
+
+ + +
+ +
+
+
+