From 51ac0092f2fe51a6dfcbbe955c075c75149e0725 Mon Sep 17 00:00:00 2001
From: stcc-odoo <stcc@odoo.com>
Date: Mon, 10 Jul 2023 15:18:13 +0000
Subject: [PATCH] [FIX] website_sale: make website_sequence unique

Steps to reproduce:

- Install website_sale
- Go to website shop in the frontend
	- Login, Edit
	- Click on 3rd or 4th kanban item
	- Push down

Issue:

The item goes to the end of the list instead of moving one element to
the right/down. Pushing an element down searches for the next element
with a higher `website_sequence` and places the current element right
after, by switching their sequence values.

Normally the logic works fine, but the problem arises when the
`website_sale` module is installed, when records with
`website_sequence = NULL` are set to the same default value, namely
10000. Then, when an item with `website_sequence = 10000` is pushed
down, it will skip many items, before finding one with a higher
`website_sequence`.

Solution:

When updating records with `website_sequence = NULL`, assign an unique
sequence to each one. We can set the `website_sequence` relative to the
inverse of the id to ensure that the order on the shopping page remains
the same as it was prior to the fix.

opw-3318867

closes odoo/odoo#127982

Signed-off-by: Stefan-Calin Crainiciuc (stcc) <stcc@odoo.com>
---
 addons/website_sale/data/demo.xml     |  2 +-
 addons/website_sale/models/product.py | 23 +++++++++++++++++++++++
 2 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/addons/website_sale/data/demo.xml b/addons/website_sale/data/demo.xml
index 568c0f968b85..c55f9e1224bb 100644
--- a/addons/website_sale/data/demo.xml
+++ b/addons/website_sale/data/demo.xml
@@ -63,7 +63,6 @@
 
         <record id="product.product_product_6" model="product.product">
             <field name="is_published" eval="True"/>
-            <field name="website_sequence">10010</field>
         </record>
 
         <record id="product.product_product_7" model="product.product">
@@ -183,6 +182,7 @@
             <field name="public_categ_ids" eval="[(6,0,[ref('public_category_cabinets')])]"/>
         </record>
         <record id="product.product_product_11_product_template" model="product.template">
+            <field name="website_sequence">9990</field>
             <field name="public_categ_ids" eval="[(6,0,[ref('public_category_furnitures_chairs')])]"/>
         </record>
         <record id="product.product_product_12_product_template" model="product.template">
diff --git a/addons/website_sale/models/product.py b/addons/website_sale/models/product.py
index fd7430790055..4cfb9878536f 100644
--- a/addons/website_sale/models/product.py
+++ b/addons/website_sale/models/product.py
@@ -1,5 +1,6 @@
 # -*- coding: utf-8 -*-
 # Part of Odoo. See LICENSE file for full copyright and licensing details.
+import logging
 
 from odoo import api, fields, models, tools, _
 from odoo.exceptions import ValidationError, UserError
@@ -7,6 +8,9 @@ from odoo.addons.http_routing.models.ir_http import slug
 from odoo.addons.website.models import ir_http
 from odoo.tools.translate import html_translate
 from odoo.osv import expression
+from psycopg2.extras import execute_values
+
+_logger = logging.getLogger(__name__)
 
 
 class ProductRibbon(models.Model):
@@ -339,6 +343,25 @@ class ProductTemplate(models.Model):
         website = self.website_id or kwargs.get('website')
         return website and website.company_id or res
 
+    def _init_column(self, column_name):
+        # to avoid generating a single default website_sequence when installing the module,
+        # we need to set the default row by row for this column
+        if column_name == "website_sequence":
+            _logger.debug("Table '%s': setting default value of new column %s to unique values for each row", self._table, column_name)
+            self.env.cr.execute("SELECT id FROM %s WHERE website_sequence IS NULL" % self._table)
+            prod_tmpl_ids = self.env.cr.dictfetchall()
+            max_seq = self._default_website_sequence()
+            query = """
+                UPDATE {table}
+                SET website_sequence = p.web_seq
+                FROM (VALUES %s) AS p(p_id, web_seq)
+                WHERE id = p.p_id
+            """.format(table=self._table)
+            values_args = [(prod_tmpl['id'], max_seq + i * 5) for i, prod_tmpl in enumerate(prod_tmpl_ids)]
+            execute_values(self.env.cr._obj, query, values_args)
+        else:
+            super(ProductTemplate, self)._init_column(column_name)
+
     def _default_website_sequence(self):
         ''' We want new product to be the last (highest seq).
         Every product should ideally have an unique sequence.
-- 
GitLab