From 32fcd4da06c38e021e975c78870669ed4184b5df Mon Sep 17 00:00:00 2001 From: Jacques-Etienne Baudoux Date: Wed, 22 May 2024 14:14:56 +0200 Subject: [PATCH 1/2] stock_picking_restrict_cancel_printed: move cancel Prevent cancellation of stock move is picking is printed --- .../__manifest__.py | 2 ++ .../models/__init__.py | 1 + .../models/stock_move.py | 20 +++++++++++++ ...t_stock_picking_restrict_cancel_printed.py | 28 ++++++++++++++++++- 4 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 stock_picking_restrict_cancel_printed/models/stock_move.py diff --git a/stock_picking_restrict_cancel_printed/__manifest__.py b/stock_picking_restrict_cancel_printed/__manifest__.py index 320a9b3d6a69..aec2bf99331a 100644 --- a/stock_picking_restrict_cancel_printed/__manifest__.py +++ b/stock_picking_restrict_cancel_printed/__manifest__.py @@ -1,4 +1,5 @@ # Copyright 2024 Camptocamp (). +# Copyright 2024 Jacques-Etienne Baudoux (BCIM) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { @@ -8,6 +9,7 @@ "category": "Inventory", "summary": "Prevent canceling a stock transfer if printed.", "author": "Camptocamp, BCIM, Odoo Community Association (OCA)", + "maintainers": ["jbaudoux"], "website": "https://github.com/OCA/stock-logistics-workflow", "license": "AGPL-3", "depends": ["stock"], diff --git a/stock_picking_restrict_cancel_printed/models/__init__.py b/stock_picking_restrict_cancel_printed/models/__init__.py index dae0bb2efe14..127b9daf7c18 100644 --- a/stock_picking_restrict_cancel_printed/models/__init__.py +++ b/stock_picking_restrict_cancel_printed/models/__init__.py @@ -1,2 +1,3 @@ +from . import stock_move from . import stock_picking from . import stock_picking_type diff --git a/stock_picking_restrict_cancel_printed/models/stock_move.py b/stock_picking_restrict_cancel_printed/models/stock_move.py new file mode 100644 index 000000000000..709168b7bd6c --- /dev/null +++ b/stock_picking_restrict_cancel_printed/models/stock_move.py @@ -0,0 +1,20 @@ +# Copyright 2024 Jacques-Etienne Baudoux (BCIM) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import _, models +from odoo.exceptions import UserError + + +class StockMove(models.Model): + _inherit = "stock.move" + + def _action_cancel(self): + for move in self: + if ( + move.picking_id.printed + and move.picking_type_id.restrict_cancel_if_printed + ): + raise UserError( + _("You cannot cancel a transfer that is already printed.") + ) + return super()._action_cancel() diff --git a/stock_picking_restrict_cancel_printed/tests/test_stock_picking_restrict_cancel_printed.py b/stock_picking_restrict_cancel_printed/tests/test_stock_picking_restrict_cancel_printed.py index 6f47af7846da..40f272398835 100644 --- a/stock_picking_restrict_cancel_printed/tests/test_stock_picking_restrict_cancel_printed.py +++ b/stock_picking_restrict_cancel_printed/tests/test_stock_picking_restrict_cancel_printed.py @@ -1,23 +1,39 @@ # Copyright 2024 Camptocamp (). +# Copyright 2024 Jacques-Etienne Baudoux (BCIM) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from odoo import Command from odoo.exceptions import UserError from odoo.tests.common import SavepointCase -class TestResPartnerGetAddress(SavepointCase): +class TestPickingRestrictCancel(SavepointCase): @classmethod def setUpClass(cls): super().setUpClass() cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) cls.partner_1 = cls.env.ref("base.res_partner_1") cls.picking_type = cls.env.ref("stock.picking_type_out") + cls.product = cls.env.ref("product.product_product_5") cls.picking = cls.env["stock.picking"].create( { "partner_id": cls.partner_1.id, "picking_type_id": cls.picking_type.id, "location_id": cls.env.ref("stock.stock_location_stock").id, "location_dest_id": cls.env.ref("stock.stock_location_customers").id, + "move_ids": [ + Command.create( + { + "name": "Test move", + "product_id": cls.product.id, + "product_uom_qty": 1, + "location_id": cls.env.ref("stock.stock_location_stock").id, + "location_dest_id": cls.env.ref( + "stock.stock_location_customers" + ).id, + } + ) + ], } ) @@ -30,3 +46,13 @@ def test_stock_picking_restrict_cancel_printed_disabled(self): self.picking_type.restrict_cancel_if_printed = False self.picking.printed = True self.picking.action_cancel() + + def test_stock_move_restrict_cancel_printed_enabled(self): + self.picking.printed = True + with self.assertRaises(UserError): + self.picking.move_ids._action_cancel() + + def test_stock_move_restrict_cancel_printed_disabled(self): + self.picking_type.restrict_cancel_if_printed = False + self.picking.printed = True + self.picking.move_ids._action_cancel() From c1c68d3e3518c7a01c4925563893647631042fa5 Mon Sep 17 00:00:00 2001 From: Jacques-Etienne Baudoux Date: Fri, 3 Jan 2025 12:35:23 +0100 Subject: [PATCH 2/2] stock_picking_restrict_cancel_printed: fix no backorder Allow to validate a partial picking when the backorder policy is set to never --- .../models/stock_move.py | 3 +++ .../tests/test_stock_picking_restrict_cancel_printed.py | 9 ++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/stock_picking_restrict_cancel_printed/models/stock_move.py b/stock_picking_restrict_cancel_printed/models/stock_move.py index 709168b7bd6c..070c99778fe0 100644 --- a/stock_picking_restrict_cancel_printed/models/stock_move.py +++ b/stock_picking_restrict_cancel_printed/models/stock_move.py @@ -9,6 +9,9 @@ class StockMove(models.Model): _inherit = "stock.move" def _action_cancel(self): + # if picking_type create_backorder is never, then move is canceled on action_done + if self.env.context.get("cancel_backorder"): + return super()._action_cancel() for move in self: if ( move.picking_id.printed diff --git a/stock_picking_restrict_cancel_printed/tests/test_stock_picking_restrict_cancel_printed.py b/stock_picking_restrict_cancel_printed/tests/test_stock_picking_restrict_cancel_printed.py index 40f272398835..57e5d083c085 100644 --- a/stock_picking_restrict_cancel_printed/tests/test_stock_picking_restrict_cancel_printed.py +++ b/stock_picking_restrict_cancel_printed/tests/test_stock_picking_restrict_cancel_printed.py @@ -26,7 +26,7 @@ def setUpClass(cls): { "name": "Test move", "product_id": cls.product.id, - "product_uom_qty": 1, + "product_uom_qty": 3, "location_id": cls.env.ref("stock.stock_location_stock").id, "location_dest_id": cls.env.ref( "stock.stock_location_customers" @@ -56,3 +56,10 @@ def test_stock_move_restrict_cancel_printed_disabled(self): self.picking_type.restrict_cancel_if_printed = False self.picking.printed = True self.picking.move_ids._action_cancel() + + def test_stock_move_restrict_cancel_printed_enabled_nobackorder(self): + """Check a picking partially processed can be validated when no backorder are created""" + self.picking.printed = True + self.picking.move_ids.quantity_done = 1 + self.picking_type.create_backorder = "never" + self.picking.button_validate()