# Part of Odoo. See LICENSE file for full copyright and licensing details. from datetime import datetime, timedelta from freezegun import freeze_time from odoo.api import Environment from odoo.tests import tagged from odoo.tools import SQL from odoo.addons.base.tests.common import BaseCommon from odoo.addons.http_routing.tests.common import MockRequest @tagged('post_install', '-at_install') class TestWebsiteSequence(BaseCommon): @classmethod def setUpClass(cls): super().setUpClass() cls.website = cls.env.ref('website.default_website') cls.public_user = cls.env.ref('base.public_user') ProductTemplate = cls.env['product.template'] product_templates = ProductTemplate.search([]) # if stock is installed we can't archive since there is orderpoints if 'orderpoint_ids' in cls.env['product.product']: product_templates.mapped('product_variant_ids.orderpoint_ids').write({'active': False}) # if pos loyalty is installed we can't archive since there are loyalty rules and rewards if 'loyalty.program' in cls.env: programs = cls.env['loyalty.program'].search([]) programs.active = False programs.coupon_ids.unlink() programs.unlink() # The "Service on Timesheet" product cannot be archived nor deleted via ORM if time_product := cls.env.ref('sale_timesheet.time_product', raise_if_not_found=False): product_templates -= time_product.product_tmpl_id cls.env.cr.execute(SQL( 'UPDATE product_template SET active = false WHERE id = %s', time_product.product_tmpl_id.id, )) product_templates.write({'active': False}) cls.product_tmpls = cls.p1, cls.p2, cls.p3, cls.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, }]) def get_product_sort_mapping(self, label): context = dict(self.env.context, website_id=self.website.id, lang='en_US') env = Environment(self.env.cr, self.public_user.id, context) with MockRequest(env, website=self.website.with_env(env)) as req: product_sort_mapping = req.env['website']._get_product_sort_mapping() return next(k for k, v in product_sort_mapping if v == label) def get_sorted_products(self, order, products=None): products = products or self.product_tmpls return products.search( [('id', 'in', products.ids)], order=order, ) def assertProductOrdering(self, products, order): """Assert `products` are sorted by `order`. :param records products: The products or product templates to check. :param str order: Expect ordering, in the same format as used by `search`. """ expected = self.get_sorted_products(order, products=products) self.assertSequenceEqual(products, expected, f"Products should be ordered on '{order}'") def test_01_website_sequence(self): sequence_order = self.get_product_sort_mapping("Featured") self.assertProductOrdering(self.p1 + self.p2 + self.p3 + self.p4, sequence_order) # 100:1, 180:2, 225:3, 250:4 self.p2.set_sequence_down() # 100:1, 180:3, 225:2, 250:4 self.assertProductOrdering(self.p1 + self.p3 + self.p2 + self.p4, sequence_order) self.p4.set_sequence_up() # 100:1, 180:3, 225:4, 250:2 self.assertProductOrdering(self.p1 + self.p3 + self.p4 + self.p2, sequence_order) self.p2.set_sequence_top() # 95:2, 100:1, 180:3, 225:4 self.assertProductOrdering(self.p2 + self.p1 + self.p3 + self.p4, sequence_order) self.p1.set_sequence_bottom() # 95:2, 180:3, 225:4, 230:1 self.assertProductOrdering(self.p2 + self.p3 + self.p4 + self.p1, sequence_order) current_products = self.get_sorted_products(sequence_order) current_sequences = current_products.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', }) current_products += new_product self.assertEqual( self.get_sorted_products(sequence_order, current_products)[-1], new_product, "New product should be last", ) def test_02_newest_arrivals(self): def toggle_publish(products, delta=timedelta(seconds=5)): publish_date = datetime.now() for product in products: publish_date += delta with freeze_time(publish_date): product.website_publish_button() product.flush_recordset() # force computations newest_arrival_order = self.get_product_sort_mapping("Newest Arrivals") toggle_publish(self.product_tmpls) # Products were published sequentially, # so first product is "oldest" arrival & last product is "newest" arrival target = self.product_tmpls[::-1] self.assertTrue(all(self.product_tmpls.mapped('is_published'))) self.assertProductOrdering(target, newest_arrival_order) publish_dates = self.product_tmpls.mapped('publish_date') toggle_publish(self.product_tmpls) self.assertFalse(any(self.product_tmpls.mapped('is_published'))) self.assertSequenceEqual( self.product_tmpls.mapped('publish_date'), publish_dates, "Unpublishing should not affect publishing date", ) toggle_publish(self.p2, delta=timedelta(days=1)) self.assertEqual( self.get_sorted_products(newest_arrival_order)[0], self.p2, "Most recently published product should appear first", )