From 6bbe1da12613f926e368cde35e9ff296a50f04ee Mon Sep 17 00:00:00 2001 From: Fernando Date: Sat, 18 Apr 2026 21:40:45 -0400 Subject: [PATCH 1/2] [FIX] purchase_request: handle move_dest_ids as False in Odoo 18 In Odoo 18, procurement.values.get('move_dest_ids') can return False (boolean) instead of an empty list when no destination moves exist. The previous code used get('move_dest_ids', []) which only uses the default when the key doesn't exist, not when it's explicitly False. This causes: TypeError: 'bool' object is not iterable Fix: use 'or []' to ensure we always have an iterable list. Closes: https://github.com/OCA/purchase-workflow/issues/2927 --- purchase_request/models/stock_rule.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/purchase_request/models/stock_rule.py b/purchase_request/models/stock_rule.py index 6ff8ae717b1..98609132104 100644 --- a/purchase_request/models/stock_rule.py +++ b/purchase_request/models/stock_rule.py @@ -22,7 +22,7 @@ def _prepare_purchase_request_line(self, request_id, procurement): "product_qty": procurement_uom_po_qty, "request_id": request_id.id, "move_dest_ids": [ - (4, x.id) for x in procurement.values.get("move_dest_ids", []) + (4, x.id) for x in (procurement.values.get("move_dest_ids") or []) ], "orderpoint_id": procurement.values.get("orderpoint_id", False) and procurement.values.get("orderpoint_id").id, From a2b9f8efbf5e5dd2977963da3b1025a0d9b6e497 Mon Sep 17 00:00:00 2001 From: Fernando Date: Sat, 18 Apr 2026 21:50:58 -0400 Subject: [PATCH 2/2] [TEST] purchase_request: add test for move_dest_ids edge case Add test for issue #2927 to ensure procurement works when move_dest_ids is False/None, which can happen in Odoo 18. This test prevents regression of TypeError: 'bool' object is not iterable --- .../test_purchase_request_procurement.py | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/purchase_request/tests/test_purchase_request_procurement.py b/purchase_request/tests/test_purchase_request_procurement.py index 44c0271e920..7aa71df82ac 100644 --- a/purchase_request/tests/test_purchase_request_procurement.py +++ b/purchase_request/tests/test_purchase_request_procurement.py @@ -167,3 +167,30 @@ def test_origin(self): move4 = self._procurement_group_run("Split", self.product_1, 10) self.assertEqual(move4.created_purchase_request_line_id.request_id, pr) self.assertEqual(pr.origin, "Test Origin, Test, Split") + + def test_procurement_without_move_dest_ids(self): + """Test that procurement works when move_dest_ids is False/None. + + This tests the fix for issue #2927 where move_dest_ids could be + a boolean False in Odoo 18 instead of an empty list, causing + TypeError: 'bool' object is not iterable + """ + move = self.env["stock.move"].create( + { + "reservation_date": fields.Datetime.now(), + "location_dest_id": self.customer_location.id, + "location_id": self.location.id, + "name": self.product_1.name, + "origin": "Test Move Dest Ids", + "procure_method": "make_to_order", + "product_id": self.product_1.id, + "product_uom": self.product_1.uom_id.id, + "product_uom_qty": 5, + "route_ids": [(4, self.route_buy.id)], + # Explicitly set move_dest_ids to False (simulating Odoo 18 edge case) + "move_dest_ids": [(5, 0, 0)], + } + ) + # This should not raise TypeError: 'bool' object is not iterable + move._action_confirm() + self.assertTrue(move.created_purchase_request_line_id)