From 9d426ff17623d512025b4fa66f46a026e9c56f79 Mon Sep 17 00:00:00 2001 From: luis Date: Wed, 10 Dec 2025 12:57:05 +0100 Subject: [PATCH] Criptomart/red-supermercados-coop#6 stock_valuation_layer_category_groupby: add group_by parent categories --- .../README.md | 44 ++++++++++----- .../i18n/es.po | 28 +++++++++- ...stock_valuation_layer_category_groupby.pot | 28 +++++++++- .../models/stock_valuation_layer.py | 54 ++++++++++++++++++- .../views/stock_valuation_layer_views.xml | 20 +++++++ 5 files changed, 157 insertions(+), 17 deletions(-) diff --git a/stock_valuation_layer_category_groupby/README.md b/stock_valuation_layer_category_groupby/README.md index 786397b..505fcc3 100644 --- a/stock_valuation_layer_category_groupby/README.md +++ b/stock_valuation_layer_category_groupby/README.md @@ -2,40 +2,60 @@ ## Description -This module allows grouping by product category in the stock valuation layer reports. +This module allows grouping by product category at different hierarchy levels in the stock valuation layer reports. ### Problem By default, the `categ_id` field in `stock.valuation.layer` is a related field without storage (`store=False`). This means it cannot be used for grouping in reports and pivot views, even though it can be used for filtering and searching. +Additionally, when dealing with hierarchical category structures (e.g., Food > Dairy > Cheese > Cheddar), it's often useful to group by different levels of the hierarchy. + ### Solution -This module makes the `categ_id` field stored (`store=True`) and indexed, which enables: -- Grouping by product category in list views -- Grouping by product category in pivot views -- Grouping by product category in graph views +This module makes the `categ_id` field stored (`store=True`) and indexed, and adds computed fields for each category hierarchy level (up to 4 levels), enabling: +- Grouping by product category (full path) in list, pivot, and graph views +- Grouping by category level 1 (top-level category) +- Grouping by category level 2 (second-level category) +- Grouping by category level 3 (third-level category) +- Grouping by category level 4 (fourth-level category) - Better performance when filtering by category ## Installation 1. Install the module from the Apps menu -2. The field will be automatically populated for existing records +2. The fields will be automatically populated for existing records ## Usage After installation: 1. Go to Inventory > Reporting > Inventory Valuation 2. Switch to Pivot or Graph view -3. Click on "Measures" or group options -4. You will now see "Product Category" available for grouping +3. Click on "Group By" options +4. You will now see multiple category grouping options: + - **Product Category**: Groups by the full category (leaf node) + - **Category Level 1**: Groups by top-level category + - **Category Level 2**: Groups by second-level category + - **Category Level 3**: Groups by third-level category + - **Category Level 4**: Groups by fourth-level category + +### Example + +If you have a category structure like: +- Food (Level 1) + - Dairy (Level 2) + - Cheese (Level 3) + - Cheddar (Level 4) + +You can now group your inventory valuation by "Food" (Level 1), "Dairy" (Level 2), "Cheese" (Level 3), or "Cheddar" (Level 4) as needed. ## Technical Details -The module extends `stock.valuation.layer` model and modifies the `categ_id` field to: -- `store=True`: Store the value in the database -- `index=True`: Add database index for better performance +The module extends `stock.valuation.layer` model and: +- Modifies the `categ_id` field to: `store=True` and `index=True` +- Adds computed stored fields: `categ_level_1_id`, `categ_level_2_id`, `categ_level_3_id`, `categ_level_4_id` +- Uses the `parent_path` field from `product.category` to determine hierarchy levels -The field remains a related field, so it will automatically update when the product category changes. +All fields are indexed for better performance and are automatically updated when product categories change. ## Bug Tracker diff --git a/stock_valuation_layer_category_groupby/i18n/es.po b/stock_valuation_layer_category_groupby/i18n/es.po index ed1f2fd..052e6e8 100644 --- a/stock_valuation_layer_category_groupby/i18n/es.po +++ b/stock_valuation_layer_category_groupby/i18n/es.po @@ -6,8 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 16.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-12-04 17:24+0000\n" -"PO-Revision-Date: 2025-12-04 17:24+0000\n" +"POT-Creation-Date: 2025-12-10 17:24+0000\n" +"PO-Revision-Date: 2025-12-10 17:24+0000\n" "Last-Translator: \n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -21,6 +21,30 @@ msgstr "" msgid "Product Category" msgstr "Categoría de producto" +#. module: stock_valuation_layer_category_groupby +#: model:ir.model.fields,field_description:stock_valuation_layer_category_groupby.field_stock_valuation_layer__categ_level_1_id +#: model_terms:ir.ui.view,arch_db:stock_valuation_layer_category_groupby.view_inventory_valuation_search_category +msgid "Category Level 1" +msgstr "Categoría Nivel 1" + +#. module: stock_valuation_layer_category_groupby +#: model:ir.model.fields,field_description:stock_valuation_layer_category_groupby.field_stock_valuation_layer__categ_level_2_id +#: model_terms:ir.ui.view,arch_db:stock_valuation_layer_category_groupby.view_inventory_valuation_search_category +msgid "Category Level 2" +msgstr "Categoría Nivel 2" + +#. module: stock_valuation_layer_category_groupby +#: model:ir.model.fields,field_description:stock_valuation_layer_category_groupby.field_stock_valuation_layer__categ_level_3_id +#: model_terms:ir.ui.view,arch_db:stock_valuation_layer_category_groupby.view_inventory_valuation_search_category +msgid "Category Level 3" +msgstr "Categoría Nivel 3" + +#. module: stock_valuation_layer_category_groupby +#: model:ir.model.fields,field_description:stock_valuation_layer_category_groupby.field_stock_valuation_layer__categ_level_4_id +#: model_terms:ir.ui.view,arch_db:stock_valuation_layer_category_groupby.view_inventory_valuation_search_category +msgid "Category Level 4" +msgstr "Categoría Nivel 4" + #. module: stock_valuation_layer_category_groupby #: model:ir.model.fields,field_description:stock_valuation_layer_category_groupby.field_stock_valuation_layer__smart_search msgid "Smart Search" diff --git a/stock_valuation_layer_category_groupby/i18n/stock_valuation_layer_category_groupby.pot b/stock_valuation_layer_category_groupby/i18n/stock_valuation_layer_category_groupby.pot index 3cb8e99..c057852 100644 --- a/stock_valuation_layer_category_groupby/i18n/stock_valuation_layer_category_groupby.pot +++ b/stock_valuation_layer_category_groupby/i18n/stock_valuation_layer_category_groupby.pot @@ -6,8 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: Odoo Server 16.0\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-12-04 17:24+0000\n" -"PO-Revision-Date: 2025-12-04 17:24+0000\n" +"POT-Creation-Date: 2025-12-10 17:24+0000\n" +"PO-Revision-Date: 2025-12-10 17:24+0000\n" "Last-Translator: \n" "Language-Team: \n" "MIME-Version: 1.0\n" @@ -21,6 +21,30 @@ msgstr "" msgid "Product Category" msgstr "" +#. module: stock_valuation_layer_category_groupby +#: model:ir.model.fields,field_description:stock_valuation_layer_category_groupby.field_stock_valuation_layer__categ_level_1_id +#: model_terms:ir.ui.view,arch_db:stock_valuation_layer_category_groupby.view_inventory_valuation_search_category +msgid "Category Level 1" +msgstr "" + +#. module: stock_valuation_layer_category_groupby +#: model:ir.model.fields,field_description:stock_valuation_layer_category_groupby.field_stock_valuation_layer__categ_level_2_id +#: model_terms:ir.ui.view,arch_db:stock_valuation_layer_category_groupby.view_inventory_valuation_search_category +msgid "Category Level 2" +msgstr "" + +#. module: stock_valuation_layer_category_groupby +#: model:ir.model.fields,field_description:stock_valuation_layer_category_groupby.field_stock_valuation_layer__categ_level_3_id +#: model_terms:ir.ui.view,arch_db:stock_valuation_layer_category_groupby.view_inventory_valuation_search_category +msgid "Category Level 3" +msgstr "" + +#. module: stock_valuation_layer_category_groupby +#: model:ir.model.fields,field_description:stock_valuation_layer_category_groupby.field_stock_valuation_layer__categ_level_4_id +#: model_terms:ir.ui.view,arch_db:stock_valuation_layer_category_groupby.view_inventory_valuation_search_category +msgid "Category Level 4" +msgstr "" + #. module: stock_valuation_layer_category_groupby #: model:ir.model.fields,field_description:stock_valuation_layer_category_groupby.field_stock_valuation_layer__smart_search msgid "Smart Search" diff --git a/stock_valuation_layer_category_groupby/models/stock_valuation_layer.py b/stock_valuation_layer_category_groupby/models/stock_valuation_layer.py index bba7017..d7c4aec 100644 --- a/stock_valuation_layer_category_groupby/models/stock_valuation_layer.py +++ b/stock_valuation_layer_category_groupby/models/stock_valuation_layer.py @@ -1,7 +1,7 @@ # Copyright 2025 Criptomart # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import fields, models +from odoo import api, fields, models class StockValuationLayer(models.Model): @@ -9,3 +9,55 @@ class StockValuationLayer(models.Model): # Make categ_id stored to allow grouping in reports categ_id = fields.Many2one(store=True, index=True) + + # Category hierarchy levels for grouping + categ_level_1_id = fields.Many2one( + comodel_name="product.category", + string="Category Level 1", + compute="_compute_category_levels", + store=True, + index=True, + ) + categ_level_2_id = fields.Many2one( + comodel_name="product.category", + string="Category Level 2", + compute="_compute_category_levels", + store=True, + index=True, + ) + categ_level_3_id = fields.Many2one( + comodel_name="product.category", + string="Category Level 3", + compute="_compute_category_levels", + store=True, + index=True, + ) + categ_level_4_id = fields.Many2one( + comodel_name="product.category", + string="Category Level 4", + compute="_compute_category_levels", + store=True, + index=True, + ) + + @api.depends("categ_id", "categ_id.parent_path") + def _compute_category_levels(self): + """Compute category hierarchy levels for better grouping options.""" + for layer in self: + if not layer.categ_id or not layer.categ_id.parent_path: + layer.categ_level_1_id = False + layer.categ_level_2_id = False + layer.categ_level_3_id = False + layer.categ_level_4_id = False + continue + + # parent_path format: "1/2/3/" where each number is a category ID + path_ids = [ + int(x) for x in layer.categ_id.parent_path.split("/") if x.isdigit() + ] + + # Assign levels based on hierarchy + layer.categ_level_1_id = path_ids[0] if len(path_ids) >= 1 else False + layer.categ_level_2_id = path_ids[1] if len(path_ids) >= 2 else False + layer.categ_level_3_id = path_ids[2] if len(path_ids) >= 3 else False + layer.categ_level_4_id = path_ids[3] if len(path_ids) >= 4 else False diff --git a/stock_valuation_layer_category_groupby/views/stock_valuation_layer_views.xml b/stock_valuation_layer_category_groupby/views/stock_valuation_layer_views.xml index c202e4b..680c5b6 100644 --- a/stock_valuation_layer_category_groupby/views/stock_valuation_layer_views.xml +++ b/stock_valuation_layer_category_groupby/views/stock_valuation_layer_views.xml @@ -13,6 +13,26 @@ name="group_by_categ_id" context="{'group_by': 'categ_id'}" /> + + + +