From 03cc9da743e7796a716b0200918ed0408be8391f Mon Sep 17 00:00:00 2001 From: Akim Juillerat Date: Mon, 2 Sep 2019 15:47:51 +0200 Subject: [PATCH 1/8] 12.0 create module stock picking completion info --- stock_picking_completion_info/__init__.py | 1 + stock_picking_completion_info/__manifest__.py | 21 ++ .../models/__init__.py | 1 + .../models/stock_picking.py | 64 +++++ .../readme/CONTRIBUTORS.rst | 1 + .../readme/DESCRIPTION.rst | 9 + .../tests/__init__.py | 1 + .../test_stock_picking_completion_info.py | 255 ++++++++++++++++++ .../views/stock_picking.xml | 30 +++ 9 files changed, 383 insertions(+) create mode 100644 stock_picking_completion_info/__init__.py create mode 100644 stock_picking_completion_info/__manifest__.py create mode 100644 stock_picking_completion_info/models/__init__.py create mode 100644 stock_picking_completion_info/models/stock_picking.py create mode 100644 stock_picking_completion_info/readme/CONTRIBUTORS.rst create mode 100644 stock_picking_completion_info/readme/DESCRIPTION.rst create mode 100644 stock_picking_completion_info/tests/__init__.py create mode 100644 stock_picking_completion_info/tests/test_stock_picking_completion_info.py create mode 100644 stock_picking_completion_info/views/stock_picking.xml diff --git a/stock_picking_completion_info/__init__.py b/stock_picking_completion_info/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/stock_picking_completion_info/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/stock_picking_completion_info/__manifest__.py b/stock_picking_completion_info/__manifest__.py new file mode 100644 index 000000000..35a2b282e --- /dev/null +++ b/stock_picking_completion_info/__manifest__.py @@ -0,0 +1,21 @@ +# Copyright 2019 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +{ + "name": "Stock Picking Completion Info", + "summary": "Display on current document completion information according " + "to next operations", + "version": "12.0.1.0.0", + "development_status": "Alpha", + "category": "Warehouse Management", + "website": "https://github.com/OCA/stock-logistics-warehouse", + "author": "Camptocamp, Odoo Community Association (OCA)", + "license": "AGPL-3", + "application": False, + "installable": True, + "depends": [ + "stock", + ], + "data": [ + "views/stock_picking.xml", + ], +} diff --git a/stock_picking_completion_info/models/__init__.py b/stock_picking_completion_info/models/__init__.py new file mode 100644 index 000000000..ae4c27227 --- /dev/null +++ b/stock_picking_completion_info/models/__init__.py @@ -0,0 +1 @@ +from . import stock_picking diff --git a/stock_picking_completion_info/models/stock_picking.py b/stock_picking_completion_info/models/stock_picking.py new file mode 100644 index 000000000..17af8b474 --- /dev/null +++ b/stock_picking_completion_info/models/stock_picking.py @@ -0,0 +1,64 @@ +# Copyright 2019 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +from odoo import api, models, fields + + +class PickingType(models.Model): + + _inherit = 'stock.picking.type' + + display_completion_info = fields.Boolean( + help='Inform operator of a completed operation at processing and at' + ' completion' + ) + + +class StockPicking(models.Model): + + _inherit = 'stock.picking' + + completion_info = fields.Selection( + [ + ('no', 'No'), + ( + 'last_picking', + 'Completion of this operation allows next operations to be ' + 'processed.', + ), + ( + 'next_picking_ready', + 'Next operations are ready to be processed.', + ), + ], + compute='_compute_completion_info', + ) + + @api.depends( + 'picking_type_id.display_completion_info', + 'move_lines.move_dest_ids.move_orig_ids.state', + ) + def _compute_completion_info(self): + for picking in self: + if ( + picking.state == 'draft' + or not picking.picking_type_id.display_completion_info + ): + picking.completion_info = 'no' + continue + # Depending moves are all the origin moves linked to the + # destination pickings' moves + depending_moves = picking.move_lines.mapped( + 'move_dest_ids.picking_id.move_lines.move_orig_ids' + ) + if all(m.state in ('done', 'cancel') for m in depending_moves): + picking.completion_info = 'next_picking_ready' + continue + # If there aren't any depending move from another picking that is + # not done, then actual picking is the last to process + other_depending_moves = ( + depending_moves - picking.move_lines + ).filtered(lambda m: m.state not in ('done', 'cancel')) + if not other_depending_moves: + picking.completion_info = 'last_picking' + continue + picking.completion_info = 'no' diff --git a/stock_picking_completion_info/readme/CONTRIBUTORS.rst b/stock_picking_completion_info/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..e31e2f0c4 --- /dev/null +++ b/stock_picking_completion_info/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Akim Juillerat diff --git a/stock_picking_completion_info/readme/DESCRIPTION.rst b/stock_picking_completion_info/readme/DESCRIPTION.rst new file mode 100644 index 000000000..70b915d0c --- /dev/null +++ b/stock_picking_completion_info/readme/DESCRIPTION.rst @@ -0,0 +1,9 @@ +This module adds completion information on stock picking. + +If activated on the picking type, completion information is computed according +to the next chained pickings related to the stock moves of the actual picking. + +In other words, if all the previous moves linked to the destination pickings +moves are done, the completion of the actual picking allows the destination +pickings to be processed. In such case, a ribbon will appear on the stock +picking form view, to inform the stock operator. diff --git a/stock_picking_completion_info/tests/__init__.py b/stock_picking_completion_info/tests/__init__.py new file mode 100644 index 000000000..c496adc0f --- /dev/null +++ b/stock_picking_completion_info/tests/__init__.py @@ -0,0 +1 @@ +from . import test_stock_picking_completion_info diff --git a/stock_picking_completion_info/tests/test_stock_picking_completion_info.py b/stock_picking_completion_info/tests/test_stock_picking_completion_info.py new file mode 100644 index 000000000..6f51c412b --- /dev/null +++ b/stock_picking_completion_info/tests/test_stock_picking_completion_info.py @@ -0,0 +1,255 @@ +# Copyright 2019 Camptocamp SA +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) +from odoo.tests import SavepointCase + + +class TestStockPickingCompletionInfo(SavepointCase): + + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + cls.partner_delta = cls.env.ref('base.res_partner_4') + cls.warehouse = cls.env.ref('stock.warehouse0') + cls.warehouse.write({ + 'outgoing_shipments': 'pick_pack_ship', + }) + cls.customers_location = cls.env.ref('stock.stock_location_customers') + cls.output_location = cls.env.ref('stock.stock_location_output') + cls.packing_location = cls.env.ref('stock.location_pack_zone') + cls.stock_shelf_location = cls.env.ref( + 'stock.stock_location_components' + ) + cls.stock_shelf_2_location = cls.env.ref('stock.stock_location_14') + + cls.out_type = cls.warehouse.out_type_id + cls.pack_type = cls.warehouse.pack_type_id + cls.pick_type = cls.warehouse.pick_type_id + cls.pick_type.write({'display_completion_info': True}) + + cls.product_1 = cls.env['product.product'].create({ + 'name': 'Product 1', 'type': 'product', + }) + cls.product_2 = cls.env['product.product'].create({ + 'name': 'Product 2', 'type': 'product', + }) + + def _init_inventory(self, same_location=True): + # Product 1 on shelf 1 + # Product 2 on shelf 2 + inventory = self.env['stock.inventory'].create({ + 'name': 'Test init', + 'filter': 'partial', + }) + inventory.action_start() + if not same_location: + product_location_list = [ + (self.product_1, self.stock_shelf_location), + (self.product_2, self.stock_shelf_2_location), + ] + else: + product_location_list = [ + (self.product_1, self.stock_shelf_location), + (self.product_2, self.stock_shelf_location), + ] + lines_vals = list() + for product, location in product_location_list: + lines_vals.append((0, 0, { + 'product_id': product.id, + 'product_uom_id': product.uom_id.id, + 'product_qty': 10.0, + 'location_id': location.id, + })) + inventory.write({ + 'line_ids': lines_vals + }) + inventory.action_validate() + + def _create_pickings(self, same_pick_location=True): + # Create delivery order + ship_order = self.env['stock.picking'].create({ + 'partner_id': self.partner_delta.id, + 'location_id': self.output_location.id, + 'location_dest_id': self.customers_location.id, + 'picking_type_id': self.out_type.id, + }) + pack_order = self.env['stock.picking'].create({ + 'partner_id': self.partner_delta.id, + 'location_id': self.packing_location.id, + 'location_dest_id': self.output_location.id, + 'picking_type_id': self.pack_type.id, + }) + pick_order = self.env['stock.picking'].create({ + 'partner_id': self.partner_delta.id, + 'location_id': self.stock_shelf_location.id, + 'location_dest_id': self.packing_location.id, + 'picking_type_id': self.pick_type.id, + }) + if same_pick_location: + return ship_order, pack_order, pick_order + pick_order_2 = self.env['stock.picking'].create({ + 'partner_id': self.partner_delta.id, + 'location_id': self.stock_shelf_2_location.id, + 'location_dest_id': self.packing_location.id, + 'picking_type_id': self.pick_type.id, + }) + return ship_order, pack_order, pick_order, pick_order_2 + + def _create_move(self, picking, product, state='waiting', + procure_method='make_to_order', move_dest=None): + move_vals = { + 'name': product.name, + 'product_id': product.id, + 'product_uom_qty': 2.0, + 'product_uom': product.uom_id.id, + 'picking_id': picking.id, + 'location_id': picking.location_id.id, + 'location_dest_id': picking.location_dest_id.id, + 'state': state, + 'procure_method': procure_method, + } + if move_dest: + move_vals['move_dest_ids'] = [(4, move_dest.id, False)] + return self.env['stock.move'].create(move_vals) + + def test_picking_all_at_once(self): + self._init_inventory() + ship_order, pack_order, pick_order = self._create_pickings() + ship_move_1 = self._create_move(ship_order, self.product_1) + pack_move_1 = self._create_move( + pack_order, self.product_1, move_dest=ship_move_1 + ) + pick_move_1 = self._create_move( + pick_order, self.product_1, state='confirmed', + procure_method='make_to_stock', move_dest=pack_move_1 + ) + ship_move_2 = self._create_move(ship_order, self.product_2) + pack_move_2 = self._create_move( + pack_order, self.product_2, move_dest=ship_move_2 + ) + pick_move_2 = self._create_move( + pick_order, self.product_2, state='confirmed', + procure_method='make_to_stock', move_dest=pack_move_2 + ) + self.assertEqual(pick_move_1.state, 'confirmed') + self.assertEqual(pick_move_2.state, 'confirmed') + self.assertEqual(pick_order.state, 'confirmed') + self.assertEqual(pick_order.completion_info, 'last_picking') + pick_order.action_assign() + self.assertEqual(pick_move_1.state, 'assigned') + self.assertEqual(pick_move_2.state, 'assigned') + self.assertEqual(pick_order.state, 'assigned') + self.assertEqual(pick_order.completion_info, 'last_picking') + wiz = self.env['stock.immediate.transfer'].create( + {'pick_ids': [(4, pick_order.id)]} + ) + wiz.process() + self.assertEqual(pick_move_1.state, 'done') + self.assertEqual(pick_move_2.state, 'done') + self.assertEqual(pick_order.state, 'done') + self.assertEqual(pick_order.completion_info, 'next_picking_ready') + + def test_picking_from_different_locations(self): + self._init_inventory(same_location=False) + ship_order, pack_order, pick_order_1, pick_order_2 = \ + self._create_pickings(same_pick_location=False) + ship_move_1 = self._create_move(ship_order, self.product_1) + pack_move_1 = self._create_move( + pack_order, self.product_1, move_dest=ship_move_1 + ) + pick_move_1 = self._create_move( + pick_order_1, self.product_1, state='confirmed', + procure_method='make_to_stock', move_dest=pack_move_1 + ) + ship_move_2 = self._create_move(ship_order, self.product_2) + pack_move_2 = self._create_move( + pack_order, self.product_2, move_dest=ship_move_2 + ) + pick_move_2 = self._create_move( + pick_order_2, self.product_2, state='confirmed', + procure_method='make_to_stock', move_dest=pack_move_2 + ) + self.assertEqual(pick_move_1.state, 'confirmed') + self.assertEqual(pick_move_2.state, 'confirmed') + self.assertEqual(pick_order_1.state, 'confirmed') + self.assertEqual(pick_order_1.completion_info, 'no') + self.assertEqual(pick_order_2.state, 'confirmed') + self.assertEqual(pick_order_2.completion_info, 'no') + pick_order_1.action_assign() + self.assertEqual(pick_move_1.state, 'assigned') + self.assertEqual(pick_order_1.state, 'assigned') + self.assertEqual(pick_order_1.completion_info, 'no') + pick_order_2.action_assign() + self.assertEqual(pick_move_2.state, 'assigned') + self.assertEqual(pick_order_2.state, 'assigned') + self.assertEqual(pick_order_2.completion_info, 'no') + wiz = self.env['stock.immediate.transfer'].create( + {'pick_ids': [(4, pick_order_1.id)]} + ) + wiz.process() + self.assertEqual(pick_move_1.state, 'done') + self.assertEqual(pick_order_1.state, 'done') + self.assertEqual(pick_order_1.completion_info, 'no') + self.assertNotEqual(pick_move_2.state, 'done') + self.assertNotEqual(pick_order_2.state, 'done') + self.assertEqual(pick_order_2.completion_info, 'last_picking') + wiz = self.env['stock.immediate.transfer'].create( + {'pick_ids': [(4, pick_order_2.id)]}) + wiz.process() + self.assertEqual(pick_move_2.state, 'done') + self.assertEqual(pick_order_2.state, 'done') + self.assertEqual(pick_order_2.completion_info, 'next_picking_ready') + self.assertEqual(pick_order_1.completion_info, 'next_picking_ready') + + def test_picking_with_backorder(self): + self._init_inventory() + ship_order, pack_order, pick_order = self._create_pickings() + ship_move_1 = self._create_move(ship_order, self.product_1) + pack_move_1 = self._create_move( + pack_order, self.product_1, move_dest=ship_move_1 + ) + pick_move_1 = self._create_move( + pick_order, self.product_1, state='confirmed', + procure_method='make_to_stock', move_dest=pack_move_1 + ) + ship_move_2 = self._create_move(ship_order, self.product_2) + pack_move_2 = self._create_move( + pack_order, self.product_2, move_dest=ship_move_2 + ) + pick_move_2 = self._create_move( + pick_order, self.product_2, state='confirmed', + procure_method='make_to_stock', move_dest=pack_move_2 + ) + self.assertEqual(pick_move_1.state, 'confirmed') + self.assertEqual(pick_move_2.state, 'confirmed') + self.assertEqual(pick_order.state, 'confirmed') + self.assertEqual(pick_order.completion_info, 'last_picking') + pick_order.action_assign() + self.assertEqual(pick_move_1.state, 'assigned') + self.assertEqual(pick_move_2.state, 'assigned') + self.assertEqual(pick_order.state, 'assigned') + self.assertEqual(pick_order.completion_info, 'last_picking') + # Process partially to create backorder + pick_move_1.move_line_ids.qty_done = 1.0 + pick_move_2.move_line_ids.qty_done = \ + pick_move_2.move_line_ids.product_uom_qty + pick_order.action_done() + pick_backorder = self.env['stock.picking'].search( + [('backorder_id', '=', pick_order.id)] + ) + pick_backorder_move = pick_backorder.move_lines + self.assertEqual(pick_move_1.state, 'done') + self.assertEqual(pick_move_2.state, 'done') + self.assertEqual(pick_order.state, 'done') + self.assertEqual(pick_backorder_move.state, 'assigned') + self.assertEqual(pick_backorder.state, 'assigned') + self.assertEqual(pick_order.completion_info, 'no') + self.assertEqual(pick_backorder.completion_info, 'last_picking') + # Process backorder + pick_backorder_move.move_line_ids.qty_done = \ + pick_backorder_move.move_line_ids.product_uom_qty + pick_backorder.action_done() + self.assertEqual(pick_backorder_move.state, 'done') + self.assertEqual(pick_backorder.state, 'done') + self.assertEqual(pick_order.completion_info, 'next_picking_ready') + self.assertEqual(pick_backorder.completion_info, 'next_picking_ready') diff --git a/stock_picking_completion_info/views/stock_picking.xml b/stock_picking_completion_info/views/stock_picking.xml new file mode 100644 index 000000000..46066c94d --- /dev/null +++ b/stock_picking_completion_info/views/stock_picking.xml @@ -0,0 +1,30 @@ + + + + Operation Types inherit + + stock.picking.type + + + + + + + + + stock.picking.form.inherit + + stock.picking + + + + + + From 6c2d9f348b947530c540a43db6ceedab89ef6b71 Mon Sep 17 00:00:00 2001 From: Akim Juillerat Date: Tue, 10 Sep 2019 13:11:23 +0200 Subject: [PATCH 2/8] Distinct full order picking --- .../models/stock_picking.py | 16 ++++++++++++++-- .../tests/test_stock_picking_completion_info.py | 8 ++++---- .../views/stock_picking.xml | 3 +++ 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/stock_picking_completion_info/models/stock_picking.py b/stock_picking_completion_info/models/stock_picking.py index 17af8b474..666d7a22a 100644 --- a/stock_picking_completion_info/models/stock_picking.py +++ b/stock_picking_completion_info/models/stock_picking.py @@ -22,13 +22,18 @@ class StockPicking(models.Model): ('no', 'No'), ( 'last_picking', - 'Completion of this operation allows next operations to be ' - 'processed.', + 'Last picking: Completion of this operation allows next ' + 'operations to be processed.', ), ( 'next_picking_ready', 'Next operations are ready to be processed.', ), + ( + 'full_order_picking', + 'Full order picking: You are processing a full order picking ' + 'that will allow next operation to be processed' + ) ], compute='_compute_completion_info', ) @@ -50,9 +55,16 @@ class StockPicking(models.Model): depending_moves = picking.move_lines.mapped( 'move_dest_ids.picking_id.move_lines.move_orig_ids' ) + # If all the depending moves are done or canceled then next picking + # is ready to be processed if all(m.state in ('done', 'cancel') for m in depending_moves): picking.completion_info = 'next_picking_ready' continue + # If all the depending moves are the moves on the actual picking + # then it's a full order and next picking is ready to be processed + if depending_moves == picking.move_lines: + picking.completion_info = 'full_order_picking' + continue # If there aren't any depending move from another picking that is # not done, then actual picking is the last to process other_depending_moves = ( diff --git a/stock_picking_completion_info/tests/test_stock_picking_completion_info.py b/stock_picking_completion_info/tests/test_stock_picking_completion_info.py index 6f51c412b..437f72e66 100644 --- a/stock_picking_completion_info/tests/test_stock_picking_completion_info.py +++ b/stock_picking_completion_info/tests/test_stock_picking_completion_info.py @@ -134,12 +134,12 @@ class TestStockPickingCompletionInfo(SavepointCase): self.assertEqual(pick_move_1.state, 'confirmed') self.assertEqual(pick_move_2.state, 'confirmed') self.assertEqual(pick_order.state, 'confirmed') - self.assertEqual(pick_order.completion_info, 'last_picking') + self.assertEqual(pick_order.completion_info, 'full_order_picking') pick_order.action_assign() self.assertEqual(pick_move_1.state, 'assigned') self.assertEqual(pick_move_2.state, 'assigned') self.assertEqual(pick_order.state, 'assigned') - self.assertEqual(pick_order.completion_info, 'last_picking') + self.assertEqual(pick_order.completion_info, 'full_order_picking') wiz = self.env['stock.immediate.transfer'].create( {'pick_ids': [(4, pick_order.id)]} ) @@ -223,12 +223,12 @@ class TestStockPickingCompletionInfo(SavepointCase): self.assertEqual(pick_move_1.state, 'confirmed') self.assertEqual(pick_move_2.state, 'confirmed') self.assertEqual(pick_order.state, 'confirmed') - self.assertEqual(pick_order.completion_info, 'last_picking') + self.assertEqual(pick_order.completion_info, 'full_order_picking') pick_order.action_assign() self.assertEqual(pick_move_1.state, 'assigned') self.assertEqual(pick_move_2.state, 'assigned') self.assertEqual(pick_order.state, 'assigned') - self.assertEqual(pick_order.completion_info, 'last_picking') + self.assertEqual(pick_order.completion_info, 'full_order_picking') # Process partially to create backorder pick_move_1.move_line_ids.qty_done = 1.0 pick_move_2.move_line_ids.qty_done = \ diff --git a/stock_picking_completion_info/views/stock_picking.xml b/stock_picking_completion_info/views/stock_picking.xml index 46066c94d..762740717 100644 --- a/stock_picking_completion_info/views/stock_picking.xml +++ b/stock_picking_completion_info/views/stock_picking.xml @@ -24,6 +24,9 @@ + From 071d70ef31a138c17228a1e060ce886a2791758b Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Thu, 19 Dec 2019 14:39:27 +0100 Subject: [PATCH 3/8] [IMP] stock_picking_completion_info: black, isort --- stock_picking_completion_info/__manifest__.py | 10 +- .../models/stock_picking.py | 57 ++- .../test_stock_picking_completion_info.py | 328 ++++++++++-------- 3 files changed, 210 insertions(+), 185 deletions(-) diff --git a/stock_picking_completion_info/__manifest__.py b/stock_picking_completion_info/__manifest__.py index 35a2b282e..c7419b27d 100644 --- a/stock_picking_completion_info/__manifest__.py +++ b/stock_picking_completion_info/__manifest__.py @@ -3,7 +3,7 @@ { "name": "Stock Picking Completion Info", "summary": "Display on current document completion information according " - "to next operations", + "to next operations", "version": "12.0.1.0.0", "development_status": "Alpha", "category": "Warehouse Management", @@ -12,10 +12,6 @@ "license": "AGPL-3", "application": False, "installable": True, - "depends": [ - "stock", - ], - "data": [ - "views/stock_picking.xml", - ], + "depends": ["stock"], + "data": ["views/stock_picking.xml"], } diff --git a/stock_picking_completion_info/models/stock_picking.py b/stock_picking_completion_info/models/stock_picking.py index 666d7a22a..c9ef59061 100644 --- a/stock_picking_completion_info/models/stock_picking.py +++ b/stock_picking_completion_info/models/stock_picking.py @@ -1,76 +1,73 @@ # Copyright 2019 Camptocamp SA # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl) -from odoo import api, models, fields +from odoo import api, fields, models class PickingType(models.Model): - _inherit = 'stock.picking.type' + _inherit = "stock.picking.type" display_completion_info = fields.Boolean( - help='Inform operator of a completed operation at processing and at' - ' completion' + help="Inform operator of a completed operation at processing and at" + " completion" ) class StockPicking(models.Model): - _inherit = 'stock.picking' + _inherit = "stock.picking" completion_info = fields.Selection( [ - ('no', 'No'), + ("no", "No"), ( - 'last_picking', - 'Last picking: Completion of this operation allows next ' - 'operations to be processed.', + "last_picking", + "Last picking: Completion of this operation allows next " + "operations to be processed.", ), + ("next_picking_ready", "Next operations are ready to be processed."), ( - 'next_picking_ready', - 'Next operations are ready to be processed.', + "full_order_picking", + "Full order picking: You are processing a full order picking " + "that will allow next operation to be processed", ), - ( - 'full_order_picking', - 'Full order picking: You are processing a full order picking ' - 'that will allow next operation to be processed' - ) ], - compute='_compute_completion_info', + compute="_compute_completion_info", ) @api.depends( - 'picking_type_id.display_completion_info', - 'move_lines.move_dest_ids.move_orig_ids.state', + "picking_type_id.display_completion_info", + "move_lines.move_dest_ids.move_orig_ids.state", ) def _compute_completion_info(self): for picking in self: if ( - picking.state == 'draft' + picking.state == "draft" or not picking.picking_type_id.display_completion_info ): - picking.completion_info = 'no' + picking.completion_info = "no" continue # Depending moves are all the origin moves linked to the # destination pickings' moves depending_moves = picking.move_lines.mapped( - 'move_dest_ids.picking_id.move_lines.move_orig_ids' + "move_dest_ids.picking_id.move_lines.move_orig_ids" ) # If all the depending moves are done or canceled then next picking # is ready to be processed - if all(m.state in ('done', 'cancel') for m in depending_moves): - picking.completion_info = 'next_picking_ready' + if all(m.state in ("done", "cancel") for m in depending_moves): + picking.completion_info = "next_picking_ready" continue # If all the depending moves are the moves on the actual picking # then it's a full order and next picking is ready to be processed if depending_moves == picking.move_lines: - picking.completion_info = 'full_order_picking' + picking.completion_info = "full_order_picking" continue # If there aren't any depending move from another picking that is # not done, then actual picking is the last to process - other_depending_moves = ( - depending_moves - picking.move_lines - ).filtered(lambda m: m.state not in ('done', 'cancel')) + other_depending_moves = (depending_moves - picking.move_lines).filtered( + lambda m: m.state not in ("done", "cancel") + ) if not other_depending_moves: - picking.completion_info = 'last_picking' + picking.completion_info = "last_picking" continue - picking.completion_info = 'no' + picking.completion_info = "no" diff --git a/stock_picking_completion_info/tests/test_stock_picking_completion_info.py b/stock_picking_completion_info/tests/test_stock_picking_completion_info.py index 437f72e66..e70b76ec9 100644 --- a/stock_picking_completion_info/tests/test_stock_picking_completion_info.py +++ b/stock_picking_completion_info/tests/test_stock_picking_completion_info.py @@ -4,43 +4,37 @@ from odoo.tests import SavepointCase class TestStockPickingCompletionInfo(SavepointCase): - @classmethod def setUpClass(cls): super().setUpClass() cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) - cls.partner_delta = cls.env.ref('base.res_partner_4') - cls.warehouse = cls.env.ref('stock.warehouse0') - cls.warehouse.write({ - 'outgoing_shipments': 'pick_pack_ship', - }) - cls.customers_location = cls.env.ref('stock.stock_location_customers') - cls.output_location = cls.env.ref('stock.stock_location_output') - cls.packing_location = cls.env.ref('stock.location_pack_zone') - cls.stock_shelf_location = cls.env.ref( - 'stock.stock_location_components' - ) - cls.stock_shelf_2_location = cls.env.ref('stock.stock_location_14') + cls.partner_delta = cls.env.ref("base.res_partner_4") + cls.warehouse = cls.env.ref("stock.warehouse0") + cls.warehouse.write({"outgoing_shipments": "pick_pack_ship"}) + cls.customers_location = cls.env.ref("stock.stock_location_customers") + cls.output_location = cls.env.ref("stock.stock_location_output") + cls.packing_location = cls.env.ref("stock.location_pack_zone") + cls.stock_shelf_location = cls.env.ref("stock.stock_location_components") + cls.stock_shelf_2_location = cls.env.ref("stock.stock_location_14") cls.out_type = cls.warehouse.out_type_id cls.pack_type = cls.warehouse.pack_type_id cls.pick_type = cls.warehouse.pick_type_id - cls.pick_type.write({'display_completion_info': True}) + cls.pick_type.write({"display_completion_info": True}) - cls.product_1 = cls.env['product.product'].create({ - 'name': 'Product 1', 'type': 'product', - }) - cls.product_2 = cls.env['product.product'].create({ - 'name': 'Product 2', 'type': 'product', - }) + cls.product_1 = cls.env["product.product"].create( + {"name": "Product 1", "type": "product"} + ) + cls.product_2 = cls.env["product.product"].create( + {"name": "Product 2", "type": "product"} + ) def _init_inventory(self, same_location=True): # Product 1 on shelf 1 # Product 2 on shelf 2 - inventory = self.env['stock.inventory'].create({ - 'name': 'Test init', - 'filter': 'partial', - }) + inventory = self.env["stock.inventory"].create( + {"name": "Test init", "filter": "partial"} + ) inventory.action_start() if not same_location: product_location_list = [ @@ -54,63 +48,81 @@ class TestStockPickingCompletionInfo(SavepointCase): ] lines_vals = list() for product, location in product_location_list: - lines_vals.append((0, 0, { - 'product_id': product.id, - 'product_uom_id': product.uom_id.id, - 'product_qty': 10.0, - 'location_id': location.id, - })) - inventory.write({ - 'line_ids': lines_vals - }) + lines_vals.append( + ( + 0, + 0, + { + "product_id": product.id, + "product_uom_id": product.uom_id.id, + "product_qty": 10.0, + "location_id": location.id, + }, + ) + ) + inventory.write({"line_ids": lines_vals}) inventory.action_validate() def _create_pickings(self, same_pick_location=True): # Create delivery order - ship_order = self.env['stock.picking'].create({ - 'partner_id': self.partner_delta.id, - 'location_id': self.output_location.id, - 'location_dest_id': self.customers_location.id, - 'picking_type_id': self.out_type.id, - }) - pack_order = self.env['stock.picking'].create({ - 'partner_id': self.partner_delta.id, - 'location_id': self.packing_location.id, - 'location_dest_id': self.output_location.id, - 'picking_type_id': self.pack_type.id, - }) - pick_order = self.env['stock.picking'].create({ - 'partner_id': self.partner_delta.id, - 'location_id': self.stock_shelf_location.id, - 'location_dest_id': self.packing_location.id, - 'picking_type_id': self.pick_type.id, - }) + ship_order = self.env["stock.picking"].create( + { + "partner_id": self.partner_delta.id, + "location_id": self.output_location.id, + "location_dest_id": self.customers_location.id, + "picking_type_id": self.out_type.id, + } + ) + pack_order = self.env["stock.picking"].create( + { + "partner_id": self.partner_delta.id, + "location_id": self.packing_location.id, + "location_dest_id": self.output_location.id, + "picking_type_id": self.pack_type.id, + } + ) + pick_order = self.env["stock.picking"].create( + { + "partner_id": self.partner_delta.id, + "location_id": self.stock_shelf_location.id, + "location_dest_id": self.packing_location.id, + "picking_type_id": self.pick_type.id, + } + ) if same_pick_location: return ship_order, pack_order, pick_order - pick_order_2 = self.env['stock.picking'].create({ - 'partner_id': self.partner_delta.id, - 'location_id': self.stock_shelf_2_location.id, - 'location_dest_id': self.packing_location.id, - 'picking_type_id': self.pick_type.id, - }) + pick_order_2 = self.env["stock.picking"].create( + { + "partner_id": self.partner_delta.id, + "location_id": self.stock_shelf_2_location.id, + "location_dest_id": self.packing_location.id, + "picking_type_id": self.pick_type.id, + } + ) return ship_order, pack_order, pick_order, pick_order_2 - def _create_move(self, picking, product, state='waiting', - procure_method='make_to_order', move_dest=None): + def _create_move( + self, + picking, + product, + state="waiting", + procure_method="make_to_order", + move_dest=None, + ): move_vals = { - 'name': product.name, - 'product_id': product.id, - 'product_uom_qty': 2.0, - 'product_uom': product.uom_id.id, - 'picking_id': picking.id, - 'location_id': picking.location_id.id, - 'location_dest_id': picking.location_dest_id.id, - 'state': state, - 'procure_method': procure_method, + "name": product.name, + "product_id": product.id, + "product_uom_qty": 2.0, + "product_uom": product.uom_id.id, + "picking_id": picking.id, + "location_id": picking.location_id.id, + "location_dest_id": picking.location_dest_id.id, + "state": state, + "procure_method": procure_method, } if move_dest: - move_vals['move_dest_ids'] = [(4, move_dest.id, False)] - return self.env['stock.move'].create(move_vals) + move_vals["move_dest_ids"] = [(4, move_dest.id, False)] + return self.env["stock.move"].create(move_vals) def test_picking_all_at_once(self): self._init_inventory() @@ -120,86 +132,100 @@ class TestStockPickingCompletionInfo(SavepointCase): pack_order, self.product_1, move_dest=ship_move_1 ) pick_move_1 = self._create_move( - pick_order, self.product_1, state='confirmed', - procure_method='make_to_stock', move_dest=pack_move_1 + pick_order, + self.product_1, + state="confirmed", + procure_method="make_to_stock", + move_dest=pack_move_1, ) ship_move_2 = self._create_move(ship_order, self.product_2) pack_move_2 = self._create_move( pack_order, self.product_2, move_dest=ship_move_2 ) pick_move_2 = self._create_move( - pick_order, self.product_2, state='confirmed', - procure_method='make_to_stock', move_dest=pack_move_2 + pick_order, + self.product_2, + state="confirmed", + procure_method="make_to_stock", + move_dest=pack_move_2, ) - self.assertEqual(pick_move_1.state, 'confirmed') - self.assertEqual(pick_move_2.state, 'confirmed') - self.assertEqual(pick_order.state, 'confirmed') - self.assertEqual(pick_order.completion_info, 'full_order_picking') + self.assertEqual(pick_move_1.state, "confirmed") + self.assertEqual(pick_move_2.state, "confirmed") + self.assertEqual(pick_order.state, "confirmed") + self.assertEqual(pick_order.completion_info, "full_order_picking") pick_order.action_assign() - self.assertEqual(pick_move_1.state, 'assigned') - self.assertEqual(pick_move_2.state, 'assigned') - self.assertEqual(pick_order.state, 'assigned') - self.assertEqual(pick_order.completion_info, 'full_order_picking') - wiz = self.env['stock.immediate.transfer'].create( - {'pick_ids': [(4, pick_order.id)]} + self.assertEqual(pick_move_1.state, "assigned") + self.assertEqual(pick_move_2.state, "assigned") + self.assertEqual(pick_order.state, "assigned") + self.assertEqual(pick_order.completion_info, "full_order_picking") + wiz = self.env["stock.immediate.transfer"].create( + {"pick_ids": [(4, pick_order.id)]} ) wiz.process() - self.assertEqual(pick_move_1.state, 'done') - self.assertEqual(pick_move_2.state, 'done') - self.assertEqual(pick_order.state, 'done') - self.assertEqual(pick_order.completion_info, 'next_picking_ready') + self.assertEqual(pick_move_1.state, "done") + self.assertEqual(pick_move_2.state, "done") + self.assertEqual(pick_order.state, "done") + self.assertEqual(pick_order.completion_info, "next_picking_ready") def test_picking_from_different_locations(self): self._init_inventory(same_location=False) - ship_order, pack_order, pick_order_1, pick_order_2 = \ - self._create_pickings(same_pick_location=False) + ship_order, pack_order, pick_order_1, pick_order_2 = self._create_pickings( + same_pick_location=False + ) ship_move_1 = self._create_move(ship_order, self.product_1) pack_move_1 = self._create_move( pack_order, self.product_1, move_dest=ship_move_1 ) pick_move_1 = self._create_move( - pick_order_1, self.product_1, state='confirmed', - procure_method='make_to_stock', move_dest=pack_move_1 + pick_order_1, + self.product_1, + state="confirmed", + procure_method="make_to_stock", + move_dest=pack_move_1, ) ship_move_2 = self._create_move(ship_order, self.product_2) pack_move_2 = self._create_move( pack_order, self.product_2, move_dest=ship_move_2 ) pick_move_2 = self._create_move( - pick_order_2, self.product_2, state='confirmed', - procure_method='make_to_stock', move_dest=pack_move_2 + pick_order_2, + self.product_2, + state="confirmed", + procure_method="make_to_stock", + move_dest=pack_move_2, ) - self.assertEqual(pick_move_1.state, 'confirmed') - self.assertEqual(pick_move_2.state, 'confirmed') - self.assertEqual(pick_order_1.state, 'confirmed') - self.assertEqual(pick_order_1.completion_info, 'no') - self.assertEqual(pick_order_2.state, 'confirmed') - self.assertEqual(pick_order_2.completion_info, 'no') + self.assertEqual(pick_move_1.state, "confirmed") + self.assertEqual(pick_move_2.state, "confirmed") + self.assertEqual(pick_order_1.state, "confirmed") + self.assertEqual(pick_order_1.completion_info, "no") + self.assertEqual(pick_order_2.state, "confirmed") + self.assertEqual(pick_order_2.completion_info, "no") pick_order_1.action_assign() - self.assertEqual(pick_move_1.state, 'assigned') - self.assertEqual(pick_order_1.state, 'assigned') - self.assertEqual(pick_order_1.completion_info, 'no') + self.assertEqual(pick_move_1.state, "assigned") + self.assertEqual(pick_order_1.state, "assigned") + self.assertEqual(pick_order_1.completion_info, "no") pick_order_2.action_assign() - self.assertEqual(pick_move_2.state, 'assigned') - self.assertEqual(pick_order_2.state, 'assigned') - self.assertEqual(pick_order_2.completion_info, 'no') - wiz = self.env['stock.immediate.transfer'].create( - {'pick_ids': [(4, pick_order_1.id)]} + self.assertEqual(pick_move_2.state, "assigned") + self.assertEqual(pick_order_2.state, "assigned") + self.assertEqual(pick_order_2.completion_info, "no") + wiz = self.env["stock.immediate.transfer"].create( + {"pick_ids": [(4, pick_order_1.id)]} ) wiz.process() - self.assertEqual(pick_move_1.state, 'done') - self.assertEqual(pick_order_1.state, 'done') - self.assertEqual(pick_order_1.completion_info, 'no') - self.assertNotEqual(pick_move_2.state, 'done') - self.assertNotEqual(pick_order_2.state, 'done') - self.assertEqual(pick_order_2.completion_info, 'last_picking') - wiz = self.env['stock.immediate.transfer'].create( - {'pick_ids': [(4, pick_order_2.id)]}) + self.assertEqual(pick_move_1.state, "done") + self.assertEqual(pick_order_1.state, "done") + self.assertEqual(pick_order_1.completion_info, "no") + self.assertNotEqual(pick_move_2.state, "done") + self.assertNotEqual(pick_order_2.state, "done") + self.assertEqual(pick_order_2.completion_info, "last_picking") + wiz = self.env["stock.immediate.transfer"].create( + {"pick_ids": [(4, pick_order_2.id)]} + ) wiz.process() - self.assertEqual(pick_move_2.state, 'done') - self.assertEqual(pick_order_2.state, 'done') - self.assertEqual(pick_order_2.completion_info, 'next_picking_ready') - self.assertEqual(pick_order_1.completion_info, 'next_picking_ready') + self.assertEqual(pick_move_2.state, "done") + self.assertEqual(pick_order_2.state, "done") + self.assertEqual(pick_order_2.completion_info, "next_picking_ready") + self.assertEqual(pick_order_1.completion_info, "next_picking_ready") def test_picking_with_backorder(self): self._init_inventory() @@ -209,47 +235,53 @@ class TestStockPickingCompletionInfo(SavepointCase): pack_order, self.product_1, move_dest=ship_move_1 ) pick_move_1 = self._create_move( - pick_order, self.product_1, state='confirmed', - procure_method='make_to_stock', move_dest=pack_move_1 + pick_order, + self.product_1, + state="confirmed", + procure_method="make_to_stock", + move_dest=pack_move_1, ) ship_move_2 = self._create_move(ship_order, self.product_2) pack_move_2 = self._create_move( pack_order, self.product_2, move_dest=ship_move_2 ) pick_move_2 = self._create_move( - pick_order, self.product_2, state='confirmed', - procure_method='make_to_stock', move_dest=pack_move_2 + pick_order, + self.product_2, + state="confirmed", + procure_method="make_to_stock", + move_dest=pack_move_2, ) - self.assertEqual(pick_move_1.state, 'confirmed') - self.assertEqual(pick_move_2.state, 'confirmed') - self.assertEqual(pick_order.state, 'confirmed') - self.assertEqual(pick_order.completion_info, 'full_order_picking') + self.assertEqual(pick_move_1.state, "confirmed") + self.assertEqual(pick_move_2.state, "confirmed") + self.assertEqual(pick_order.state, "confirmed") + self.assertEqual(pick_order.completion_info, "full_order_picking") pick_order.action_assign() - self.assertEqual(pick_move_1.state, 'assigned') - self.assertEqual(pick_move_2.state, 'assigned') - self.assertEqual(pick_order.state, 'assigned') - self.assertEqual(pick_order.completion_info, 'full_order_picking') + self.assertEqual(pick_move_1.state, "assigned") + self.assertEqual(pick_move_2.state, "assigned") + self.assertEqual(pick_order.state, "assigned") + self.assertEqual(pick_order.completion_info, "full_order_picking") # Process partially to create backorder pick_move_1.move_line_ids.qty_done = 1.0 - pick_move_2.move_line_ids.qty_done = \ - pick_move_2.move_line_ids.product_uom_qty + pick_move_2.move_line_ids.qty_done = pick_move_2.move_line_ids.product_uom_qty pick_order.action_done() - pick_backorder = self.env['stock.picking'].search( - [('backorder_id', '=', pick_order.id)] + pick_backorder = self.env["stock.picking"].search( + [("backorder_id", "=", pick_order.id)] ) pick_backorder_move = pick_backorder.move_lines - self.assertEqual(pick_move_1.state, 'done') - self.assertEqual(pick_move_2.state, 'done') - self.assertEqual(pick_order.state, 'done') - self.assertEqual(pick_backorder_move.state, 'assigned') - self.assertEqual(pick_backorder.state, 'assigned') - self.assertEqual(pick_order.completion_info, 'no') - self.assertEqual(pick_backorder.completion_info, 'last_picking') + self.assertEqual(pick_move_1.state, "done") + self.assertEqual(pick_move_2.state, "done") + self.assertEqual(pick_order.state, "done") + self.assertEqual(pick_backorder_move.state, "assigned") + self.assertEqual(pick_backorder.state, "assigned") + self.assertEqual(pick_order.completion_info, "no") + self.assertEqual(pick_backorder.completion_info, "last_picking") # Process backorder - pick_backorder_move.move_line_ids.qty_done = \ + pick_backorder_move.move_line_ids.qty_done = ( pick_backorder_move.move_line_ids.product_uom_qty + ) pick_backorder.action_done() - self.assertEqual(pick_backorder_move.state, 'done') - self.assertEqual(pick_backorder.state, 'done') - self.assertEqual(pick_order.completion_info, 'next_picking_ready') - self.assertEqual(pick_backorder.completion_info, 'next_picking_ready') + self.assertEqual(pick_backorder_move.state, "done") + self.assertEqual(pick_backorder.state, "done") + self.assertEqual(pick_order.completion_info, "next_picking_ready") + self.assertEqual(pick_backorder.completion_info, "next_picking_ready") From 0289d74b88ecb3bc85a92523f5011e2b599ccbf7 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Thu, 19 Dec 2019 15:04:01 +0100 Subject: [PATCH 4/8] [MIG] stock_picking_completion_info: Migration to 13.0 --- .../odoo/addons/stock_picking_completion_info | 1 + setup/stock_picking_completion_info/setup.py | 6 ++++++ stock_picking_completion_info/__manifest__.py | 2 +- .../models/stock_picking.py | 13 +++++++++++++ .../tests/test_stock_picking_completion_info.py | 6 ++---- 5 files changed, 23 insertions(+), 5 deletions(-) create mode 120000 setup/stock_picking_completion_info/odoo/addons/stock_picking_completion_info create mode 100644 setup/stock_picking_completion_info/setup.py diff --git a/setup/stock_picking_completion_info/odoo/addons/stock_picking_completion_info b/setup/stock_picking_completion_info/odoo/addons/stock_picking_completion_info new file mode 120000 index 000000000..9c5a54b68 --- /dev/null +++ b/setup/stock_picking_completion_info/odoo/addons/stock_picking_completion_info @@ -0,0 +1 @@ +../../../../stock_picking_completion_info \ No newline at end of file diff --git a/setup/stock_picking_completion_info/setup.py b/setup/stock_picking_completion_info/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/stock_picking_completion_info/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/stock_picking_completion_info/__manifest__.py b/stock_picking_completion_info/__manifest__.py index c7419b27d..29d05d60a 100644 --- a/stock_picking_completion_info/__manifest__.py +++ b/stock_picking_completion_info/__manifest__.py @@ -4,7 +4,7 @@ "name": "Stock Picking Completion Info", "summary": "Display on current document completion information according " "to next operations", - "version": "12.0.1.0.0", + "version": "13.0.1.0.0", "development_status": "Alpha", "category": "Warehouse Management", "website": "https://github.com/OCA/stock-logistics-warehouse", diff --git a/stock_picking_completion_info/models/stock_picking.py b/stock_picking_completion_info/models/stock_picking.py index c9ef59061..80c509604 100644 --- a/stock_picking_completion_info/models/stock_picking.py +++ b/stock_picking_completion_info/models/stock_picking.py @@ -71,3 +71,16 @@ class StockPicking(models.Model): picking.completion_info = "last_picking" continue picking.completion_info = "no" + + +class StockMove(models.Model): + + _inherit = "stock.move" + + def write(self, vals): + super().write(vals) + if "state" in vals: + # invalidate cache, the api.depends do not allow to find all + # the conditions to invalidate the field + self.env["stock.picking"].invalidate_cache(fnames=["completion_info"]) + return True diff --git a/stock_picking_completion_info/tests/test_stock_picking_completion_info.py b/stock_picking_completion_info/tests/test_stock_picking_completion_info.py index e70b76ec9..dac491e90 100644 --- a/stock_picking_completion_info/tests/test_stock_picking_completion_info.py +++ b/stock_picking_completion_info/tests/test_stock_picking_completion_info.py @@ -10,7 +10,7 @@ class TestStockPickingCompletionInfo(SavepointCase): cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) cls.partner_delta = cls.env.ref("base.res_partner_4") cls.warehouse = cls.env.ref("stock.warehouse0") - cls.warehouse.write({"outgoing_shipments": "pick_pack_ship"}) + cls.warehouse.write({"delivery_steps": "pick_pack_ship"}) cls.customers_location = cls.env.ref("stock.stock_location_customers") cls.output_location = cls.env.ref("stock.stock_location_output") cls.packing_location = cls.env.ref("stock.location_pack_zone") @@ -32,9 +32,7 @@ class TestStockPickingCompletionInfo(SavepointCase): def _init_inventory(self, same_location=True): # Product 1 on shelf 1 # Product 2 on shelf 2 - inventory = self.env["stock.inventory"].create( - {"name": "Test init", "filter": "partial"} - ) + inventory = self.env["stock.inventory"].create({"name": "Test init"}) inventory.action_start() if not same_location: product_location_list = [ From a7dcbec3258443e9963cd84b37c1a9055b10d288 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Tue, 17 Mar 2020 12:04:02 +0100 Subject: [PATCH 5/8] run pre-commit with new prettiers --- .../views/stock_picking.xml | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/stock_picking_completion_info/views/stock_picking.xml b/stock_picking_completion_info/views/stock_picking.xml index 762740717..318dbf62f 100644 --- a/stock_picking_completion_info/views/stock_picking.xml +++ b/stock_picking_completion_info/views/stock_picking.xml @@ -1,4 +1,4 @@ - + Operation Types inherit @@ -10,7 +10,6 @@ - stock.picking.form.inherit @@ -18,13 +17,25 @@