details:   https://code.tryton.org/tryton/commit/75a9ec77dd93
branch:    default
user:      Cédric Krier <[email protected]>
date:      Tue Dec 30 17:42:00 2025 +0100
description:
        Use Many2One field for product and variant on stock reporting

        Closes #14456
diffstat:

 modules/stock/stock_reporting.py                           |  121 +++++++++++-
 modules/stock/stock_reporting.xml                          |    4 +-
 modules/stock/tests/scenario_stock_reporting_inventory.rst |    4 +-
 modules/stock/view/reporting_inventory_daily_form.xml      |    4 +-
 modules/stock/view/reporting_inventory_daily_list.xml      |    2 +
 modules/stock/view/reporting_inventory_form.xml            |    4 +-
 modules/stock/view/reporting_inventory_list.xml            |    2 +
 modules/stock/view/reporting_inventory_move_form.xml       |    4 +-
 modules/stock/view/reporting_inventory_move_list.xml       |    2 +
 modules/stock/view/reporting_inventory_turnover_form.xml   |    4 +-
 modules/stock/view/reporting_inventory_turnover_list.xml   |    2 +
 11 files changed, 125 insertions(+), 28 deletions(-)

diffs (343 lines):

diff -r c6e8d2a3534e -r 75a9ec77dd93 modules/stock/stock_reporting.py
--- a/modules/stock/stock_reporting.py  Sun Jan 18 16:51:32 2026 +0100
+++ b/modules/stock/stock_reporting.py  Tue Dec 30 17:42:00 2025 +0100
@@ -47,10 +47,36 @@
     __slots__ = ()
 
     company = fields.Many2One('company.company', "Company")
-    product = fields.Reference("Product", [
+    product_reference = fields.Reference("Product Reference", [
             ('product.product', "Variant"),
             ('product.template', "Product"),
-            ])
+            ],
+        context={
+            'company': Eval('company', -1),
+            },
+        depends=['company'])
+    product_template = fields.Many2One(
+        'product.template', "Product",
+        states={
+            'invisible': (
+                Eval('context', {}).get('product_type', 'product.template')
+                != 'product.template'),
+            },
+        context={
+            'company': Eval('company', -1),
+            },
+        depends=['company'])
+    product = fields.Many2One(
+        'product.product', "Variant",
+        states={
+            'invisible': (
+                Eval('context', {}).get('product_type', 'product.template')
+                != 'product.product'),
+            },
+        context={
+            'company': Eval('company', -1),
+            },
+        depends=['company'])
     unit = fields.Function(
         fields.Many2One('product.uom', "Unit"),
         'on_change_with_unit')
@@ -108,9 +134,14 @@
             )
 
         if context.get('product_type') == 'product.product':
-            product = Concat('product.product,', move.product)
+            product_reference = Concat('product.product,', move.product)
+            product = move.product
+            product_template = Literal(None)
         else:
-            product = Concat('product.template,', product_table.template)
+            product_reference = Concat(
+                'product.template,', product_table.template)
+            product = Literal(None)
+            product_template = product_table.template
             quantities = (
                 quantities
                 .join(product_table,
@@ -149,6 +180,8 @@
 
         quantities = quantities.select(
             Min(move.id).as_('id'),
+            product_reference.as_('product_reference'),
+            product_template.as_('product_template'),
             product.as_('product'),
             date_column.as_('date'),
             next_date_column.as_('next_date'),
@@ -173,7 +206,7 @@
             + Coalesce(quantities.input_quantity, 0)
             - Coalesce(quantities.output_quantity, 0),
             window=Window(
-                [quantities.product],
+                [quantities.product_reference],
                 order_by=[*cls._quantities_order_by(quantities)]))
 
         if period:
@@ -208,7 +241,8 @@
             query = quantities.join(cache, 'LEFT',
                 condition=(
                     cache.product
-                    == cls.product.sql_id(quantities.product, cls)))
+                    == cls.product_reference.sql_id(
+                        quantities.product_reference, cls)))
             quantity += Coalesce(cache.quantity, 0)
         else:
             query = quantities
@@ -217,6 +251,8 @@
             .select(
                 quantities.id.as_('id'),
                 Literal(company).as_('company'),
+                quantities.product_reference.as_('product_reference'),
+                quantities.product_template.as_('product_template'),
                 quantities.product.as_('product'),
                 quantities.input_quantity.as_('input_quantity'),
                 quantities.output_quantity.as_('output_quantity'),
@@ -261,10 +297,20 @@
             & (move.state.in_(['done', 'assigned', 'draft'])))
         return state_clause
 
-    @fields.depends('product')
+    @fields.depends('product_reference')
     def on_change_with_unit(self, name=None):
-        if self.product:
-            return self.product.default_uom
+        if self.product_reference:
+            return self.product_reference.default_uom
+
+    @classmethod
+    def view_attributes(cls):
+        return super().view_attributes() + [
+            ('/tree/field[@name="product_template"]', 'tree_invisible',
+                Eval('product_type', 'product.template')
+                != 'product.template'),
+            ('/tree/field[@name="product"]', 'tree_invisible',
+                Eval('product_type', 'product.template') != 'product.product'),
+            ]
 
 
 class InventoryContext(_InventoryContextMixin):
@@ -459,10 +505,36 @@
     __name__ = 'stock.reporting.inventory.turnover'
 
     company = fields.Many2One('company.company', "Company")
-    product = fields.Reference("Product", [
+    product_reference = fields.Reference("Product", [
             ('product.product', "Variant"),
             ('product.template', "Product"),
-            ])
+            ],
+        context={
+            'company': Eval('company', -1),
+            },
+        depends=['company'])
+    product_template = fields.Many2One(
+        'product.template', "Product",
+        states={
+            'invisible': (
+                Eval('context', {}).get('product_type', 'product.template')
+                != 'product.template'),
+            },
+        context={
+            'company': Eval('company', -1),
+            },
+        depends=['company'])
+    product = fields.Many2One(
+        'product.product', "Variant",
+        states={
+            'invisible': (
+                Eval('context', {}).get('product_type', 'product.template')
+                != 'product.product'),
+            },
+        context={
+            'company': Eval('company', -1),
+            },
+        depends=['company'])
     unit = fields.Function(
         fields.Many2One('product.uom', "Unit"),
         'on_change_with_unit')
@@ -506,8 +578,11 @@
 
         return (inventory
             .select(
-                cls.product.sql_id(inventory.product, cls).as_('id'),
+                cls.product_reference.sql_id(
+                    inventory.product_reference, cls).as_('id'),
                 Literal(company).as_('company'),
+                inventory.product_reference.as_('product_reference'),
+                inventory.product_template.as_('product_template'),
                 inventory.product.as_('product'),
                 round_sql(
                     output_quantity,
@@ -518,9 +593,23 @@
                 round_sql(
                     output_quantity / NullIf(average_quantity, 0),
                     cls.turnover.digits[1]).as_('turnover'),
-                group_by=[inventory.product]))
+                group_by=[
+                    inventory.product_reference,
+                    inventory.product_template,
+                    inventory.product,
+                    ]))
 
-    @fields.depends('product')
+    @fields.depends('product_reference')
     def on_change_with_unit(self, name=None):
-        if self.product:
-            return self.product.default_uom
+        if self.product_reference:
+            return self.product_reference.default_uom
+
+    @classmethod
+    def view_attributes(cls):
+        return super().view_attributes() + [
+            ('/tree/field[@name="product_template"]', 'tree_invisible',
+                Eval('product_type', 'product.template')
+                != 'product.template'),
+            ('/tree/field[@name="product"]', 'tree_invisible',
+                Eval('product_type', 'product.template') != 'product.product'),
+            ]
diff -r c6e8d2a3534e -r 75a9ec77dd93 modules/stock/stock_reporting.xml
--- a/modules/stock/stock_reporting.xml Sun Jan 18 16:51:32 2026 +0100
+++ b/modules/stock/stock_reporting.xml Tue Dec 30 17:42:00 2025 +0100
@@ -96,7 +96,7 @@
             <field 
name="context_model">stock.reporting.inventory.range.context</field>
             <field
                 name="domain"
-                eval="[('product', '=', (Eval('active_model', 
'product.template'), Eval('active_id', -1)))]"
+                eval="[('product_reference', '=', (Eval('active_model', 
'product.template'), Eval('active_id', -1)))]"
                 pyson="1"/>
             <field
                 name="context"
@@ -177,7 +177,7 @@
             <field 
name="context_model">stock.reporting.inventory.range.context</field>
             <field
                 name="domain"
-                eval="[('product', '=', (Eval('active_model', 
'product.template'), Eval('active_id', -1)))]"
+                eval="[('product_reference', '=', (Eval('active_model', 
'product.template'), Eval('active_id', -1)))]"
                 pyson="1"/>
             <field
                 name="context"
diff -r c6e8d2a3534e -r 75a9ec77dd93 
modules/stock/tests/scenario_stock_reporting_inventory.rst
--- a/modules/stock/tests/scenario_stock_reporting_inventory.rst        Sun Jan 
18 16:51:32 2026 +0100
+++ b/modules/stock/tests/scenario_stock_reporting_inventory.rst        Tue Dec 
30 17:42:00 2025 +0100
@@ -148,7 +148,7 @@
     ...     inventory, = Inventory.find([])
     >>> inventory.quantity
     9.0
-    >>> assertEqual(inventory.product.__class__.__name__, product_type)
+    >>> assertEqual(inventory.product_reference.__class__.__name__, 
product_type)
 
     >>> with config.set_context(
     ...         location=warehouse_loc.id,
@@ -236,4 +236,4 @@
     5.5
     >>> turnover.turnover
     0.045
-    >>> assertEqual(turnover.product.__class__.__name__, product_type)
+    >>> assertEqual(turnover.product_reference.__class__.__name__, 
product_type)
diff -r c6e8d2a3534e -r 75a9ec77dd93 
modules/stock/view/reporting_inventory_daily_form.xml
--- a/modules/stock/view/reporting_inventory_daily_form.xml     Sun Jan 18 
16:51:32 2026 +0100
+++ b/modules/stock/view/reporting_inventory_daily_form.xml     Tue Dec 30 
17:42:00 2025 +0100
@@ -2,8 +2,8 @@
 <!-- This file is part of Tryton.  The COPYRIGHT file at the top level of
 this repository contains the full copyright notices and license terms. -->
 <form>
-    <label name="product"/>
-    <field name="product" colspan="3"/>
+    <label name="product_reference"/>
+    <field name="product_reference" colspan="3"/>
 
     <label name="from_date"/>
     <field name="from_date"/>
diff -r c6e8d2a3534e -r 75a9ec77dd93 
modules/stock/view/reporting_inventory_daily_list.xml
--- a/modules/stock/view/reporting_inventory_daily_list.xml     Sun Jan 18 
16:51:32 2026 +0100
+++ b/modules/stock/view/reporting_inventory_daily_list.xml     Tue Dec 30 
17:42:00 2025 +0100
@@ -2,6 +2,8 @@
 <!-- This file is part of Tryton.  The COPYRIGHT file at the top level of
 this repository contains the full copyright notices and license terms. -->
 <tree>
+    <field name="product_reference" tree_invisible="1"/>
+    <field name="product_template" expand="2"/>
     <field name="product" expand="2"/>
     <field name="from_date" string="Date"/>
     <field name="input_quantity" symbol="unit" optional="1"/>
diff -r c6e8d2a3534e -r 75a9ec77dd93 
modules/stock/view/reporting_inventory_form.xml
--- a/modules/stock/view/reporting_inventory_form.xml   Sun Jan 18 16:51:32 
2026 +0100
+++ b/modules/stock/view/reporting_inventory_form.xml   Tue Dec 30 17:42:00 
2025 +0100
@@ -2,8 +2,8 @@
 <!-- This file is part of Tryton.  The COPYRIGHT file at the top level of
 this repository contains the full copyright notices and license terms. -->
 <form>
-    <label name="product"/>
-    <field name="product" colspan="3"/>
+    <label name="product_reference"/>
+    <field name="product_reference" colspan="3"/>
 
     <label name="quantity"/>
     <field name="quantity" symbol="unit"/>
diff -r c6e8d2a3534e -r 75a9ec77dd93 
modules/stock/view/reporting_inventory_list.xml
--- a/modules/stock/view/reporting_inventory_list.xml   Sun Jan 18 16:51:32 
2026 +0100
+++ b/modules/stock/view/reporting_inventory_list.xml   Tue Dec 30 17:42:00 
2025 +0100
@@ -2,6 +2,8 @@
 <!-- This file is part of Tryton.  The COPYRIGHT file at the top level of
 this repository contains the full copyright notices and license terms. -->
 <tree>
+    <field name="product_reference" tree_invisible="1"/>
+    <field name="product_template" expand="2"/>
     <field name="product" expand="2"/>
     <field name="quantity" symbol="unit"/>
 </tree>
diff -r c6e8d2a3534e -r 75a9ec77dd93 
modules/stock/view/reporting_inventory_move_form.xml
--- a/modules/stock/view/reporting_inventory_move_form.xml      Sun Jan 18 
16:51:32 2026 +0100
+++ b/modules/stock/view/reporting_inventory_move_form.xml      Tue Dec 30 
17:42:00 2025 +0100
@@ -2,8 +2,8 @@
 <!-- This file is part of Tryton.  The COPYRIGHT file at the top level of
 this repository contains the full copyright notices and license terms. -->
 <form>
-    <label name="product"/>
-    <field name="product" colspan="3"/>
+    <label name="product_reference"/>
+    <field name="product_reference" colspan="3"/>
 
     <label name="date"/>
     <field name="date"/>
diff -r c6e8d2a3534e -r 75a9ec77dd93 
modules/stock/view/reporting_inventory_move_list.xml
--- a/modules/stock/view/reporting_inventory_move_list.xml      Sun Jan 18 
16:51:32 2026 +0100
+++ b/modules/stock/view/reporting_inventory_move_list.xml      Tue Dec 30 
17:42:00 2025 +0100
@@ -2,6 +2,8 @@
 <!-- This file is part of Tryton.  The COPYRIGHT file at the top level of
 this repository contains the full copyright notices and license terms. -->
 <tree>
+    <field name="product_reference" tree_invisible="1"/>
+    <field name="product_template" expand="2"/>
     <field name="product" expand="2"/>
     <field name="date"/>
     <field name="origin" expand="1" optional="1"/>
diff -r c6e8d2a3534e -r 75a9ec77dd93 
modules/stock/view/reporting_inventory_turnover_form.xml
--- a/modules/stock/view/reporting_inventory_turnover_form.xml  Sun Jan 18 
16:51:32 2026 +0100
+++ b/modules/stock/view/reporting_inventory_turnover_form.xml  Tue Dec 30 
17:42:00 2025 +0100
@@ -2,8 +2,8 @@
 <!-- This file is part of Tryton.  The COPYRIGHT file at the top level of
 this repository contains the full copyright notices and license terms. -->
 <form>
-    <label name="product"/>
-    <field name="product" colspan="3"/>
+    <label name="product_reference"/>
+    <field name="product_reference" colspan="3"/>
 
     <label name="output_quantity"/>
     <field name="output_quantity" symbol="unit"/>
diff -r c6e8d2a3534e -r 75a9ec77dd93 
modules/stock/view/reporting_inventory_turnover_list.xml
--- a/modules/stock/view/reporting_inventory_turnover_list.xml  Sun Jan 18 
16:51:32 2026 +0100
+++ b/modules/stock/view/reporting_inventory_turnover_list.xml  Tue Dec 30 
17:42:00 2025 +0100
@@ -2,6 +2,8 @@
 <!-- This file is part of Tryton.  The COPYRIGHT file at the top level of
 this repository contains the full copyright notices and license terms. -->
 <tree>
+    <field name="product_reference" tree_invisible="1"/>
+    <field name="product_template" expand="2"/>
     <field name="product" expand="2"/>
     <field name="output_quantity" symbol="unit" optional="0"/>
     <field name="average_quantity" symbol="unit" optional="0"/>

Reply via email to