From b930a771c05ccb8f790c627b2d8d87bd1c5398c6 Mon Sep 17 00:00:00 2001 From: "Laurent Mignon (ACSONE)" Date: Wed, 5 Aug 2026 13:48:06 +0200 Subject: [PATCH 1/2] [FIX] fs_product_multi_image: Fix main_image_id on product.template Before this commit, the main_image_id field on product.template was not being computed correctly, leading to incorrect image display in the product views. This commit fixes the computation of main_image_id by ensuring that it selects the correct image from the related image_ids. --- fs_product_multi_image/__manifest__.py | 2 +- .../migrations/16.0.1.2.0/post-migrate.py | 23 +++++++++++++++++++ .../models/product_template.py | 20 +++++++++++++--- 3 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 fs_product_multi_image/migrations/16.0.1.2.0/post-migrate.py diff --git a/fs_product_multi_image/__manifest__.py b/fs_product_multi_image/__manifest__.py index a08483aa77..785b8018bf 100644 --- a/fs_product_multi_image/__manifest__.py +++ b/fs_product_multi_image/__manifest__.py @@ -5,7 +5,7 @@ "name": "Fs Product Multi Image", "summary": """ Manage multi images from extenal file system on product""", - "version": "16.0.1.1.6", + "version": "16.0.1.2.0", "license": "AGPL-3", "author": "ACSONE SA/NV,Odoo Community Association (OCA)", "website": "https://github.com/OCA/storage", diff --git a/fs_product_multi_image/migrations/16.0.1.2.0/post-migrate.py b/fs_product_multi_image/migrations/16.0.1.2.0/post-migrate.py new file mode 100644 index 0000000000..6ab8a46737 --- /dev/null +++ b/fs_product_multi_image/migrations/16.0.1.2.0/post-migrate.py @@ -0,0 +1,23 @@ +# Copyright 2026 ACSONE SA/NV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +import logging + +try: + from odoo.upgrade import util +except ImportError as error: + raise ImportError( + "This migration script requires odoo.upgrade.util.\n" + "Please install odoo.upgrade.util to proceed with the migration.\n" + "See https://github.com/odoo/upgrade-util/" + ) from error + + +_logger = logging.getLogger(__name__) + + +def migrate(cr, version): + if not version: + return + _logger.info("Recompute main_image_id for product.template records") + util.recompute_fields(cr, "product.template", ["main_image_id"], logger=_logger) diff --git a/fs_product_multi_image/models/product_template.py b/fs_product_multi_image/models/product_template.py index 3ed6c44de0..eb0cd287e9 100644 --- a/fs_product_multi_image/models/product_template.py +++ b/fs_product_multi_image/models/product_template.py @@ -33,10 +33,24 @@ class ProductTemplate(models.Model): store=False, ) + def _sort_multi_image_id_key(self, img): + # this sort method is required to deal with + # new id when the image is added with when + # clicking on the add image button on the product + # view (not the one into the widget) + key = [img.sequence] + id_ = img.id + if isinstance(id_, models.NewId): + if id_.origin: + key.append(f"a{id_.origin}") + elif id_.ref: + key.append(id_.ref) + else: + key.append(f"a{id_}") + return tuple(key) + @api.depends("image_ids", "image_ids.sequence") def _compute_main_image_id(self): for record in self: - image_ids = record.image_ids.sorted( - key=lambda i: f"{i.sequence},{str(i.id)}" - ) + image_ids = record.image_ids.sorted(key=self._sort_multi_image_id_key) record.main_image_id = image_ids and image_ids[0] or None From fcac3eba4c3eb5c29db1a38cf160edc629b53a41 Mon Sep 17 00:00:00 2001 From: "Laurent Mignon (ACSONE)" Date: Wed, 5 Aug 2026 13:50:07 +0200 Subject: [PATCH 2/2] [IMP] fs_product_multi_image: Add image_128 field to product.product and product.template This field is a compatibility field for standard modules (especially website_sale) that expect to find an image_128 field on product.product and product.template records. The sole purpose of this field is to provide the field image_128 that can be resolved by the web controller when requested to provide the image_128 content of a product.product or product.template record. --- fs_product_multi_image/models/product_product.py | 12 ++++++++++++ fs_product_multi_image/models/product_template.py | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/fs_product_multi_image/models/product_product.py b/fs_product_multi_image/models/product_product.py index fd87e9a7d9..1e66624638 100644 --- a/fs_product_multi_image/models/product_product.py +++ b/fs_product_multi_image/models/product_product.py @@ -37,6 +37,18 @@ class ProductProduct(models.Model): readonly=True, store=False, ) + # Compatibility field for standard modules (especially website_sale) + # The sole purpose of this field is to provide the field image_128 + # that can be resolved by the web controller when requested + # to provide the image_128 content of a product.product record. + # By using a related field of the same type (FSImage) as image_medium, + # we avoid any extra cost of computing the image_128 content + image_128 = FSImage( + string="Image 128", + related="image_medium", + readonly=True, + store=False, + ) @api.depends( "product_tmpl_id.image_ids", diff --git a/fs_product_multi_image/models/product_template.py b/fs_product_multi_image/models/product_template.py index eb0cd287e9..b528732f56 100644 --- a/fs_product_multi_image/models/product_template.py +++ b/fs_product_multi_image/models/product_template.py @@ -32,6 +32,18 @@ class ProductTemplate(models.Model): readonly=True, store=False, ) + # Compatibility field for standard modules (especially website_sale) + # The sole purpose of this field is to provide the field image_128 + # that can be resolved by the web controller when requested + # to provide the image_128 content of a product.product record. + # By using a related field of the same type (FSImage) as image_medium, + # we avoid any extra cost of computing the image_128 content + image_128 = FSImage( + string="Image 128", + related="image_medium", + readonly=True, + store=False, + ) def _sort_multi_image_id_key(self, img): # this sort method is required to deal with