[FIX] website_sale: fix website_sequence
There was multiple issues with `website_sequence` field, mostly coming from
commits during the last few years (eg 43c544f8a8) that tried to change the
`website_sequence` order to be the same as `sequence` but changing only part of
the code.
Other commits tried to restore that again by changing only some parts.
This commit does:
1. Originally, the `website_sequence` was supposed to be the opposite of
`sequence` field, the greater the first.
This commit ensure this is not the case anymore and it is used as the
regular `sequence` field.
2. `website_sequence` demo data was set between 4 and 1000, no need to use so
big differences. Plus, having a website_sequence set to 4 means the next
product would already have negative value.
3. Add `website_sequence` field in tree view in place of `sequence` for website
product view, and in debug mode on form view.
task-1960777
closes odoo/odoo#32593
Signed-off-by: Jérémy Kersten (jke) <jke@openerp.com>
This commit is contained in:
@@ -12,6 +12,7 @@ Publish your members/association directory publicly.
|
||||
'depends': ['website_partner', 'website_google_map', 'association', 'website_sale'],
|
||||
'data': [
|
||||
'data/membership_data.xml',
|
||||
'views/product_template_views.xml',
|
||||
'views/website_membership_templates.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'security/website_membership.xml',
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="product_template_view_form" model="ir.ui.view">
|
||||
<field name="name">product.template.view.form.inherit.website_membership</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="membership.membership_products_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="active" position="after">
|
||||
<field name="website_sequence" groups="base.group_no_one"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="product_template_view_tree" model="ir.ui.view">
|
||||
<field name="name">product.template.view.tree.inherit.website_membership</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="membership.membership_products_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="name" position="before">
|
||||
<field name="website_sequence" widget="handle"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -157,7 +157,7 @@ class WebsiteSale(http.Controller):
|
||||
def _get_search_order(self, post):
|
||||
# OrderBy will be parsed in orm and so no direct sql injection
|
||||
# id is added to be sure that order is a unique sort key
|
||||
order = post.get('order') or 'website_sequence desc'
|
||||
order = post.get('order') or 'website_sequence ASC'
|
||||
return 'is_published desc, %s, id desc' % order
|
||||
|
||||
def _get_search_domain(self, search, category, attrib_values, search_in_description=True):
|
||||
|
||||
@@ -58,15 +58,13 @@
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="website_size_x">2</field>
|
||||
<field name="website_size_y">2</field>
|
||||
<field name="website_sequence">1000</field>
|
||||
<field name="website_sequence">9950</field>
|
||||
<field name="website_style_ids" eval="[(6,0,[ref('website_sale.image_full')])]"/>
|
||||
</record>
|
||||
|
||||
<record id="product.product_product_6" model="product.product">
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="website_sequence">4</field>
|
||||
<field name="website_style_ids" eval="[(6,0,[ref('website_sale.image_full')])]"/>
|
||||
<field name="website_sequence">4</field>
|
||||
<field name="website_sequence">10010</field>
|
||||
<field name="website_style_ids" eval="[(6,0,[ref('website_sale.image_full')])]"/>
|
||||
</record>
|
||||
|
||||
@@ -639,7 +637,7 @@
|
||||
<field name="name">Warranty</field>
|
||||
<field name="default_code">SERV_125889</field>
|
||||
<field name="list_price">20.0</field>
|
||||
<field name="website_sequence">100</field>
|
||||
<field name="website_sequence">9980</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="type">service</field>
|
||||
<field name="uom_id" ref="uom.product_uom_unit"/>
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
from odoo import api, fields, models, tools, _
|
||||
from odoo.exceptions import ValidationError, UserError
|
||||
from odoo.addons import decimal_precision as dp
|
||||
from odoo.addons.website.models import ir_http
|
||||
from odoo.tools.translate import html_translate
|
||||
from odoo.osv import expression
|
||||
@@ -204,7 +203,7 @@ class ProductTemplate(models.Model):
|
||||
'(upsell strategy).Those product show up on the product page.')
|
||||
accessory_product_ids = fields.Many2many('product.product', 'product_accessory_rel', 'src_id', 'dest_id',
|
||||
string='Accessory Products', help='Accessories show up when the customer'
|
||||
'reviews the cart before payment (cross-sell strategy).')
|
||||
'reviews the cart before payment (cross-sell strategy).')
|
||||
website_size_x = fields.Integer('Size X', default=1)
|
||||
website_size_y = fields.Integer('Size Y', default=1)
|
||||
website_style_ids = fields.Many2many('product.style', string='Styles')
|
||||
@@ -345,27 +344,41 @@ class ProductTemplate(models.Model):
|
||||
return website and website.company_id or res
|
||||
|
||||
def _default_website_sequence(self):
|
||||
self._cr.execute("SELECT MIN(website_sequence) FROM %s" % self._table)
|
||||
min_sequence = self._cr.fetchone()[0]
|
||||
return min_sequence and min_sequence - 1 or 10
|
||||
''' We want new product to be the last (highest seq).
|
||||
Every product should ideally have an unique sequence.
|
||||
Default sequence (10000) should only be used for DB first product.
|
||||
As we don't resequence the whole tree (as `sequence` does), this field
|
||||
might have negative value.
|
||||
'''
|
||||
self._cr.execute("SELECT MAX(website_sequence) FROM %s" % self._table)
|
||||
max_sequence = self._cr.fetchone()[0]
|
||||
if max_sequence is None:
|
||||
return 10000
|
||||
return max_sequence + 5
|
||||
|
||||
def set_sequence_top(self):
|
||||
self.website_sequence = self.sudo().search([], order='website_sequence desc', limit=1).website_sequence + 1
|
||||
min_sequence = self.sudo().search([], order='website_sequence ASC', limit=1)
|
||||
self.website_sequence = min_sequence.website_sequence - 5
|
||||
|
||||
def set_sequence_bottom(self):
|
||||
self.website_sequence = self.sudo().search([], order='website_sequence', limit=1).website_sequence - 1
|
||||
max_sequence = self.sudo().search([], order='website_sequence DESC', limit=1)
|
||||
self.website_sequence = max_sequence.website_sequence + 5
|
||||
|
||||
def set_sequence_up(self):
|
||||
previous_product_tmpl = self.sudo().search(
|
||||
[('website_sequence', '>', self.website_sequence), ('website_published', '=', self.website_published)],
|
||||
order='website_sequence', limit=1)
|
||||
previous_product_tmpl = self.sudo().search([
|
||||
('website_sequence', '<', self.website_sequence),
|
||||
('website_published', '=', self.website_published),
|
||||
], order='website_sequence DESC', limit=1)
|
||||
if previous_product_tmpl:
|
||||
previous_product_tmpl.website_sequence, self.website_sequence = self.website_sequence, previous_product_tmpl.website_sequence
|
||||
else:
|
||||
self.set_sequence_top()
|
||||
|
||||
def set_sequence_down(self):
|
||||
next_prodcut_tmpl = self.search([('website_sequence', '<', self.website_sequence), ('website_published', '=', self.website_published)], order='website_sequence desc', limit=1)
|
||||
next_prodcut_tmpl = self.search([
|
||||
('website_sequence', '>', self.website_sequence),
|
||||
('website_published', '=', self.website_published),
|
||||
], order='website_sequence ASC', limit=1)
|
||||
if next_prodcut_tmpl:
|
||||
next_prodcut_tmpl.website_sequence, self.website_sequence = self.website_sequence, next_prodcut_tmpl.website_sequence
|
||||
else:
|
||||
|
||||
@@ -5,3 +5,4 @@ from . import test_website_sale_mail
|
||||
from . import test_website_sale_pricelist
|
||||
from . import test_website_sale_product_attribute_value_config
|
||||
from . import test_website_sale_image
|
||||
from . import test_website_sequence
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import odoo.tests
|
||||
|
||||
|
||||
@odoo.tests.common.tagged('post_install', '-at_install')
|
||||
class TestWebsiteSequence(odoo.tests.TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestWebsiteSequence, self).setUp()
|
||||
|
||||
ProductTemplate = self.env['product.template']
|
||||
product_templates = ProductTemplate.search([])
|
||||
# if stock is installed we can't archive since there is orderpoints
|
||||
if hasattr(self.env['product.product'], 'orderpoint_ids'):
|
||||
product_templates.mapped('product_variant_ids.orderpoint_ids').write({'active': False})
|
||||
product_templates.write({'active': False})
|
||||
self.p1, self.p2, self.p3, self.p4 = ProductTemplate.create([{
|
||||
'name': 'First Product',
|
||||
'website_sequence': 100,
|
||||
}, {
|
||||
'name': 'Second Product',
|
||||
'website_sequence': 180,
|
||||
}, {
|
||||
'name': 'Third Product',
|
||||
'website_sequence': 225,
|
||||
}, {
|
||||
'name': 'Last Product',
|
||||
'website_sequence': 250,
|
||||
}])
|
||||
|
||||
self._check_correct_order(self.p1 + self.p2 + self.p3 + self.p4)
|
||||
|
||||
def _search_website_sequence_order(self, order='ASC'):
|
||||
'''Helper method to limit the search only to the setUp products'''
|
||||
return self.env['product.template'].search([
|
||||
], order='website_sequence %s' % (order))
|
||||
|
||||
def _check_correct_order(self, products):
|
||||
product_ids = self._search_website_sequence_order().ids
|
||||
self.assertEqual(product_ids, products.ids, "Wrong sequence order")
|
||||
|
||||
def test_01_website_sequence(self):
|
||||
# 100:1, 180:2, 225:3, 250:4
|
||||
self.p2.set_sequence_down()
|
||||
# 100:1, 180:3, 225:2, 250:4
|
||||
self._check_correct_order(self.p1 + self.p3 + self.p2 + self.p4)
|
||||
self.p4.set_sequence_up()
|
||||
# 100:1, 180:3, 225:4, 250:2
|
||||
self._check_correct_order(self.p1 + self.p3 + self.p4 + self.p2)
|
||||
self.p2.set_sequence_top()
|
||||
# 95:2, 100:1, 180:3, 225:4
|
||||
self._check_correct_order(self.p2 + self.p1 + self.p3 + self.p4)
|
||||
self.p1.set_sequence_bottom()
|
||||
# 95:2, 180:3, 225:4, 230:1
|
||||
self._check_correct_order(self.p2 + self.p3 + self.p4 + self.p1)
|
||||
|
||||
current_sequences = self._search_website_sequence_order().mapped('website_sequence')
|
||||
self.assertEqual(current_sequences, [95, 180, 225, 230], "Wrong sequence order (2)")
|
||||
|
||||
self.p2.website_sequence = 1
|
||||
self.p3.set_sequence_top()
|
||||
# -4:3, 1:2, 225:4, 230:1
|
||||
self.assertEqual(self.p3.website_sequence, -4, "`website_sequence` should go below 0")
|
||||
|
||||
new_product = self.env['product.template'].create({
|
||||
'name': 'Last Newly Created Product',
|
||||
})
|
||||
|
||||
self.assertEqual(self._search_website_sequence_order()[-1], new_product, "new product should be last")
|
||||
@@ -12,22 +12,6 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="product_template_action_website" model="ir.actions.act_window">
|
||||
<field name="name">Products</field>
|
||||
<field name="res_model">product.template</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">kanban,tree,form,activity</field>
|
||||
<field name="search_view_id" ref="product_template_search_view_website"/>
|
||||
<field name="context">{'search_default_published': 1, 'default_website_published': True}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Create a new product
|
||||
</p><p>
|
||||
A product can be either a physical product or a service that you sell to your customers.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="product_pricelist_view">
|
||||
<field name="name">product.pricelist.website.form</field>
|
||||
<field name="model">product.pricelist</field>
|
||||
@@ -50,8 +34,9 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="product_template_website_tree_view">
|
||||
<field name="name">product.template.website.tree</field>
|
||||
<!-- We want website_id to be shown outside of website module like other models -->
|
||||
<record model="ir.ui.view" id="product_template_view_tree">
|
||||
<field name="name">product.template.view.tree.inherit.website_sale</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="product.product_template_tree_view"/>
|
||||
<field name="arch" type="xml">
|
||||
@@ -61,6 +46,36 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- only website module template view should use the website_sequence -->
|
||||
<record model="ir.ui.view" id="product_template_view_tree_website_sale">
|
||||
<field name="name">product.template.view.tree.website_sale</field>
|
||||
<field name="mode">primary</field>
|
||||
<field name="model">product.template</field>
|
||||
<field name="inherit_id" ref="website_sale.product_template_view_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="sequence" position="replace">
|
||||
<field name="website_sequence" widget="handle"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="product_template_action_website" model="ir.actions.act_window">
|
||||
<field name="name">Products</field>
|
||||
<field name="res_model">product.template</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">kanban,tree,form,activity</field>
|
||||
<field name="view_id" ref="product_template_view_tree_website_sale"/>
|
||||
<field name="search_view_id" ref="product_template_search_view_website"/>
|
||||
<field name="context">{'search_default_published': 1, 'default_website_published': True}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Create a new product
|
||||
</p><p>
|
||||
A product can be either a physical product or a service that you sell to your customers.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="product_template_form_view_invoice_policy">
|
||||
<field name="name">product.template.invoice.policy</field>
|
||||
<field name="model">product.template</field>
|
||||
@@ -96,6 +111,7 @@
|
||||
<group string="Shop">
|
||||
<field name="website_url" invisible="1"/>
|
||||
<field name="website_id" options="{'no_create': True}" groups="website.group_multi_website"/>
|
||||
<field name="website_sequence" groups="base.group_no_one"/>
|
||||
<field name="public_categ_ids" widget="many2many_tags" string="Categories"/>
|
||||
<field name="alternative_product_ids" widget="many2many_tags"/>
|
||||
<field name="accessory_product_ids" widget="many2many_tags"/>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo noupdate="1">
|
||||
<record id="sale_product_configurator.product_product_1_product_template" model="product.template">
|
||||
<field name="website_sequence">100</field>
|
||||
<field name="website_sequence">9980</field>
|
||||
<field name="website_published" eval="True"/>
|
||||
</record>
|
||||
<record id="product.product_product_4_product_template" model="product.template">
|
||||
|
||||
Reference in New Issue
Block a user