details:   https://code.tryton.org/tryton/commit/d7abce5cd10c
branch:    default
user:      Cédric Krier <[email protected]>
date:      Thu Jun 11 15:30:11 2026 +0200
description:
        Aggregate product by locations before applying filter when searching by 
quantity

        Closes #14887
diffstat:

 modules/stock/move.py              |  17 ++++++++---------
 modules/stock/tests/test_module.py |  19 +++++++++++++++++++
 2 files changed, 27 insertions(+), 9 deletions(-)

diffs (63 lines):

diff -r e8394b1a0c0f -r d7abce5cd10c modules/stock/move.py
--- a/modules/stock/move.py     Thu Jun 18 14:37:36 2026 +0200
+++ b/modules/stock/move.py     Thu Jun 11 15:30:11 2026 +0200
@@ -151,6 +151,12 @@
             pbl = Product.products_by_location(
                 location_ids, with_childs=with_childs, grouping=grouping)
 
+        quantities = defaultdict(float)
+        for key, quantity in pbl.items():
+            # pbl could return None in some keys
+            if key[position] is not None:
+                quantities[key[position]] += quantity
+
         operator_ = {
             '=': operator.eq,
             '>=': operator.ge,
@@ -161,15 +167,8 @@
             'in': lambda v, l: v in l,
             'not in': lambda v, l: v not in l,
             }.get(operator_, lambda v, l: False)
-        record_ids = []
-        for key, quantity in pbl.items():
-            if (quantity is not None and operand is not None
-                    and operator_(quantity, operand)):
-                # pbl could return None in some keys
-                if key[position] is not None:
-                    record_ids.append(key[position])
-
-        return [('id', 'in', record_ids)]
+        return [('id', 'in',
+                [k for k, v in quantities.items() if operator_(v, operand)])]
 
 
 class Move(Workflow, ModelSQL, ModelView):
diff -r e8394b1a0c0f -r d7abce5cd10c modules/stock/tests/test_module.py
--- a/modules/stock/tests/test_module.py        Thu Jun 18 14:37:36 2026 +0200
+++ b/modules/stock/tests/test_module.py        Thu Jun 11 15:30:11 2026 +0200
@@ -335,6 +335,25 @@
 
             test_products_by_location()
 
+            with transaction.set_context(
+                    locations=[supplier.id, customer.id, storage.id]):
+                for domain, found in [
+                        ([('quantity', '=', 0)], True),
+                        ([('quantity', '>=', 0)], True),
+                        ([('quantity', '>', 0)], False),
+                        ([('quantity', '<=', 0)], True),
+                        ([('quantity', '<', 0)], False),
+                        ([('quantity', '!=', 0)], False),
+                        ([('quantity', 'in', [0])], True),
+                        ([('quantity', 'in', [1])], False),
+                        ([('quantity', 'not in', [0])], False),
+                        ([('quantity', 'not in', [1])], True),
+                        ]:
+                    with self.subTest(domain=domain):
+                        self.assertEqual(
+                            Product.search(domain),
+                            [product] if found else [])
+
             periods = [
                 today + relativedelta(days=-6),
                 today + relativedelta(days=-5),

Reply via email to