You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							385 lines
						
					
					
						
							15 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							385 lines
						
					
					
						
							15 KiB
						
					
					
				
								# -*- coding: utf-8 -*-
							 | 
						|
								#############################################################################
							 | 
						|
								#
							 | 
						|
								#    Cybrosys Technologies Pvt. Ltd.
							 | 
						|
								#
							 | 
						|
								#    Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
							 | 
						|
								#    Author: Ayisha Sumayya K (odoo@cybrosys.com)
							 | 
						|
								#
							 | 
						|
								#    You can modify it under the terms of the GNU LESSER
							 | 
						|
								#    GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
							 | 
						|
								#
							 | 
						|
								#    This program is distributed in the hope that it will be useful,
							 | 
						|
								#    but WITHOUT ANY WARRANTY; without even the implied warranty of
							 | 
						|
								#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
							 | 
						|
								#    GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
							 | 
						|
								#
							 | 
						|
								#    You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
							 | 
						|
								#    (LGPL v3) along with this program.
							 | 
						|
								#    If not, see <http://www.gnu.org/licenses/>.
							 | 
						|
								#
							 | 
						|
								#############################################################################
							 | 
						|
								
							 | 
						|
								from collections import defaultdict
							 | 
						|
								from itertools import product as cartesian_product
							 | 
						|
								from datetime import datetime
							 | 
						|
								from werkzeug.exceptions import NotFound
							 | 
						|
								
							 | 
						|
								from odoo import fields, http, tools, _
							 | 
						|
								from odoo.http import request
							 | 
						|
								from odoo.addons.http_routing.models.ir_http import slug
							 | 
						|
								from odoo.addons.website.controllers.main import QueryURL
							 | 
						|
								from odoo.addons.website.models.ir_http import sitemap_qs2dom
							 | 
						|
								
							 | 
						|
								from odoo.osv import expression
							 | 
						|
								from odoo.tools import lazy
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								class TableComputeCoffee(object):
							 | 
						|
								    """ Compute data related to coffee shop theme """
							 | 
						|
								
							 | 
						|
								    def __init__(self):
							 | 
						|
								        self.table = {}
							 | 
						|
								
							 | 
						|
								    def _check_place(self, posx, posy, sizex, sizey, ppr):
							 | 
						|
								        res = True
							 | 
						|
								        for yaxix in range(sizey):
							 | 
						|
								            for xaxix in range(sizex):
							 | 
						|
								                if posx + xaxix >= ppr:
							 | 
						|
								                    res = False
							 | 
						|
								                    break
							 | 
						|
								                row = self.table.setdefault(posy + yaxix, {})
							 | 
						|
								                if row.setdefault(posx + xaxix) is not None:
							 | 
						|
								                    res = False
							 | 
						|
								                    break
							 | 
						|
								            for x in range(ppr):
							 | 
						|
								                self.table[posy + yaxix].setdefault(xaxix, None)
							 | 
						|
								        return res
							 | 
						|
								
							 | 
						|
								    def process(self, products, ppg=20, ppr=4):
							 | 
						|
								        """Compute products positions on the grid"""
							 | 
						|
								        minpos = 0
							 | 
						|
								        index = 0
							 | 
						|
								        maxy = 0
							 | 
						|
								        rec = 0
							 | 
						|
								        for pdct in products:
							 | 
						|
								            rec = min(max(pdct.website_size_x, 1), ppr)
							 | 
						|
								            res = min(max(pdct.website_size_y, 1), ppr)
							 | 
						|
								            if index >= ppg:
							 | 
						|
								                rec = res = 1
							 | 
						|
								
							 | 
						|
								            pos = minpos
							 | 
						|
								            while not self._check_place(pos % ppr, pos // ppr, rec, res, ppr):
							 | 
						|
								                pos += 1
							 | 
						|
								            if index >= ppg and ((pos + 1.0) // ppr) > maxy:
							 | 
						|
								                break
							 | 
						|
								
							 | 
						|
								            if rec == 1 and res == 1:
							 | 
						|
								                minpos = pos // ppr
							 | 
						|
								
							 | 
						|
								            for y2 in range(res):
							 | 
						|
								                for x2 in range(rec):
							 | 
						|
								                    self.table[(pos // ppr) + y2][(pos % ppr) + x2] = False
							 | 
						|
								            self.table[pos // ppr][pos % ppr] = {
							 | 
						|
								                'product': pdct, 'x': rec, 'y': res,
							 | 
						|
								                'ribbon': pdct._get_website_ribbon(),
							 | 
						|
								            }
							 | 
						|
								            if index <= ppg:
							 | 
						|
								                maxy = max(maxy, res + (pos // ppr))
							 | 
						|
								            index += 1
							 | 
						|
								
							 | 
						|
								        rows = sorted(self.table.items())
							 | 
						|
								        rows = [r[1] for r in rows]
							 | 
						|
								        for col in range(len(rows)):
							 | 
						|
								            cols = sorted(rows[col].items())
							 | 
						|
								            rec += len(cols)
							 | 
						|
								            rows[col] = [r[1] for r in cols if r[1]]
							 | 
						|
								
							 | 
						|
								        return rows
							 | 
						|
								
							 | 
						|
								
							 | 
						|
								class ThemeCoffeeMenu(http.Controller):
							 | 
						|
								    """ controller for rendering datas to menu page """
							 | 
						|
								
							 | 
						|
								    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 \
							 | 
						|
								                request.env['website'].get_current_website().shop_default_sort
							 | 
						|
								        return 'is_published desc, %s, id desc' % order
							 | 
						|
								
							 | 
						|
								    def _get_search_domain(self, search, category, attrib_values,
							 | 
						|
								                           search_in_description=True):
							 | 
						|
								        domains = [request.website.sale_product_domain()]
							 | 
						|
								        if search:
							 | 
						|
								            for srch in search.split(" "):
							 | 
						|
								                subdomains = [
							 | 
						|
								                    [('name', 'ilike', srch)],
							 | 
						|
								                    [('product_variant_ids.default_code', 'ilike', srch)]
							 | 
						|
								                ]
							 | 
						|
								                if search_in_description:
							 | 
						|
								                    subdomains.append([('website_description', 'ilike', srch)])
							 | 
						|
								                    subdomains.append([('description_sale', 'ilike', srch)])
							 | 
						|
								                domains.append(expression.OR(subdomains))
							 | 
						|
								
							 | 
						|
								        if category:
							 | 
						|
								            domains.append([('public_categ_ids', 'child_of', int(category))])
							 | 
						|
								
							 | 
						|
								        if attrib_values:
							 | 
						|
								            attrib = None
							 | 
						|
								            ids = []
							 | 
						|
								            for value in attrib_values:
							 | 
						|
								                if not attrib:
							 | 
						|
								                    attrib = value[0]
							 | 
						|
								                    ids.append(value[1])
							 | 
						|
								                elif value[0] == attrib:
							 | 
						|
								                    ids.append(value[1])
							 | 
						|
								                else:
							 | 
						|
								                    domains.append([('attribute_line_ids.value_ids', 'in', ids)])
							 | 
						|
								                    attrib = value[0]
							 | 
						|
								                    ids = [value[1]]
							 | 
						|
								            if attrib:
							 | 
						|
								                domains.append([('attribute_line_ids.value_ids', 'in', ids)])
							 | 
						|
								
							 | 
						|
								        return expression.AND(domains)
							 | 
						|
								
							 | 
						|
								    def sitemap_shop(env, rule, qs):
							 | 
						|
								        if not qs or qs.lower() in '/menu':
							 | 
						|
								            yield {'loc': '/menu'}
							 | 
						|
								
							 | 
						|
								        Category = env['product.public.category']
							 | 
						|
								        dom = sitemap_qs2dom(qs, '/menu/category', Category._rec_name)
							 | 
						|
								        dom += env['website'].get_current_website().website_domain()
							 | 
						|
								        for cat in Category.search(dom):
							 | 
						|
								            loc = '/menu/category/%s' % slug(cat)
							 | 
						|
								            if not qs or qs.lower() in loc:
							 | 
						|
								                yield {'loc': loc}
							 | 
						|
								
							 | 
						|
								    def _get_search_options(
							 | 
						|
								            self, category=None, attrib_values=None, pricelist=None, **post):
							 | 
						|
								        return {
							 | 
						|
								            'displayDescription': True,
							 | 
						|
								            'displayDetail': True,
							 | 
						|
								            'displayExtraDetail': True,
							 | 
						|
								            'displayExtraLink': True,
							 | 
						|
								            'displayImage': True,
							 | 
						|
								            'allowFuzzy': not post.get('noFuzzy'),
							 | 
						|
								            'category': str(category.id) if category else None,
							 | 
						|
								            'attrib_values': attrib_values,
							 | 
						|
								            'display_currency': pricelist.currency_id,
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								    def _shop_lookup_products(self, attrib_set, options, post, search, website):
							 | 
						|
								        """ No limit because attributes are obtained from complete
							 | 
						|
								            product list"""
							 | 
						|
								
							 | 
						|
								        product_count, details, fuzzy_search_term = website._search_with_fuzzy(
							 | 
						|
								            "products_only", search, limit=None,
							 | 
						|
								            order=self._get_search_order(post), options=options
							 | 
						|
								        )
							 | 
						|
								        search_result = details[0].get(
							 | 
						|
								            'results', request.env['product.template']
							 | 
						|
								        ).with_context(bin_size=True)
							 | 
						|
								        if attrib_set:
							 | 
						|
								            attribute_values = request.env['product.attribute.value'].browse(attrib_set)
							 | 
						|
								            values_per_attribute = defaultdict(
							 | 
						|
								                lambda: request.env['product.attribute.value'])
							 | 
						|
								
							 | 
						|
								            multi_value_attribute = False
							 | 
						|
								            for value in attribute_values:
							 | 
						|
								                values_per_attribute[value.attribute_id] |= value
							 | 
						|
								                if len(values_per_attribute[value.attribute_id]) > 1:
							 | 
						|
								                    multi_value_attribute = True
							 | 
						|
								
							 | 
						|
								            def filter_template(template, attribute_values_list):
							 | 
						|
								                """Transform product.attribute.value to
							 | 
						|
								                    product.template.attribute.value """
							 | 
						|
								                attribute_value_to_ptav = dict()
							 | 
						|
								                for ptav in template.attribute_line_ids.product_template_value_ids:
							 | 
						|
								                    attribute_value_to_ptav[ptav.product_attribute_value_id] = ptav.id
							 | 
						|
								                possible_combinations = False
							 | 
						|
								                for attribute_values in attribute_values_list:
							 | 
						|
								                    ptavs = request.env['product.template.attribute.value'].browse(
							 | 
						|
								                        [attribute_value_to_ptav[val]
							 | 
						|
								                         for val in attribute_values
							 | 
						|
								                         if val in attribute_value_to_ptav]
							 | 
						|
								                    )
							 | 
						|
								                    if len(ptavs) < len(attribute_values):
							 | 
						|
								                        continue
							 | 
						|
								                    if len(ptavs) == len(template.attribute_line_ids):
							 | 
						|
								                        if template._is_combination_possible(ptavs):
							 | 
						|
								                            return True
							 | 
						|
								                    elif len(ptavs) < len(template.attribute_line_ids):
							 | 
						|
								                        if len(attribute_values_list) == 1:
							 | 
						|
								                            if any(template._get_possible_combinations(
							 | 
						|
								                                    necessary_values=ptavs)):
							 | 
						|
								                                return True
							 | 
						|
								                        if not possible_combinations:
							 | 
						|
								                            possible_combinations = template._get_possible_combinations()
							 | 
						|
								                        if any(len(ptavs & combination) == len(ptavs)
							 | 
						|
								                               for combination in possible_combinations):
							 | 
						|
								                            return True
							 | 
						|
								                return False
							 | 
						|
								
							 | 
						|
								            if not multi_value_attribute:
							 | 
						|
								                possible_attrib_values_list = [attribute_values]
							 | 
						|
								            else:
							 | 
						|
								                possible_attrib_values_list = [
							 | 
						|
								                    request.env['product.attribute.value'].browse(
							 | 
						|
								                        [v.id for v in values]) for values in cartesian_product(
							 | 
						|
								                        *values_per_attribute.values())
							 | 
						|
								                ]
							 | 
						|
								
							 | 
						|
								            search_result = search_result.filtered(
							 | 
						|
								                lambda tmpl: filter_template(tmpl, possible_attrib_values_list)
							 | 
						|
								            )
							 | 
						|
								        return fuzzy_search_term, product_count, search_result
							 | 
						|
								
							 | 
						|
								    def _menu_get_query_url_kwargs(self, category, search, attrib=None,
							 | 
						|
								                                   order=None, **post):
							 | 
						|
								        return {
							 | 
						|
								            'category': category,
							 | 
						|
								            'search': search,
							 | 
						|
								            'attrib': attrib,
							 | 
						|
								            'order': order,
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								    def _get_additional_shop_values(self, values):
							 | 
						|
								        """ Hook to update values used for rendering website_sale.products template """
							 | 
						|
								        return {}
							 | 
						|
								
							 | 
						|
								    @http.route([
							 | 
						|
								        '/menu',
							 | 
						|
								        '/menu/page/<int:page>',
							 | 
						|
								        '/menu/category/<model("product.public.category"):category>',
							 | 
						|
								        '/menu/category/<model("product.public.category"):category>/page/<int:page>',
							 | 
						|
								    ], type='http', auth="public", website=True, sitemap=sitemap_shop)
							 | 
						|
								    def menu_page(self, page=0, category=None, search='',
							 | 
						|
								                  min_price=0.0, max_price=0.0, ppg=False, **post):
							 | 
						|
								        add_qty = int(post.get('add_qty', 1))
							 | 
						|
								
							 | 
						|
								        Category = request.env['product.public.category']
							 | 
						|
								        if category:
							 | 
						|
								            category = Category.search([('id', '=', int(category))], limit=1)
							 | 
						|
								            if not category or not category.can_access_from_current_website():
							 | 
						|
								                raise NotFound()
							 | 
						|
								        else:
							 | 
						|
								            category = Category
							 | 
						|
								
							 | 
						|
								        website = request.env['website'].get_current_website()
							 | 
						|
								        if ppg:
							 | 
						|
								            try:
							 | 
						|
								                ppg = int(ppg)
							 | 
						|
								                post['ppg'] = ppg
							 | 
						|
								            except ValueError:
							 | 
						|
								                ppg = False
							 | 
						|
								        if not ppg:
							 | 
						|
								            ppg = website.shop_ppg or 20
							 | 
						|
								
							 | 
						|
								        ppr = website.shop_ppr or 4
							 | 
						|
								
							 | 
						|
								        attrib_list = request.httprequest.args.getlist('attrib')
							 | 
						|
								        attrib_values = [[int(x) for x in v.split("-")]
							 | 
						|
								                         for v in attrib_list if v
							 | 
						|
								                         ]
							 | 
						|
								        attributes_ids = {v[0] for v in attrib_values}
							 | 
						|
								        attrib_set = {v[1] for v in attrib_values}
							 | 
						|
								
							 | 
						|
								        keep = QueryURL('/menu', **self._menu_get_query_url_kwargs(category
							 | 
						|
								                    and int(category), search, min_price, max_price, **post))
							 | 
						|
								
							 | 
						|
								        now = datetime.timestamp(datetime.now())
							 | 
						|
								        pricelist = request.env['product.pricelist'].\
							 | 
						|
								            browse(request.session.get('website_sale_current_pl'))
							 | 
						|
								        if not pricelist or request.session.\
							 | 
						|
								                get('website_sale_pricelist_time', 0) < now - 60*60:
							 | 
						|
								            pricelist = website.get_current_pricelist()
							 | 
						|
								            request.session['website_sale_pricelist_time'] = now
							 | 
						|
								            request.session['website_sale_current_pl'] = pricelist.id
							 | 
						|
								
							 | 
						|
								        request.update_context(pricelist=pricelist.id,
							 | 
						|
								                               partner=request.env.user.partner_id)
							 | 
						|
								
							 | 
						|
								        url = "/menu"
							 | 
						|
								        if search:
							 | 
						|
								            post["search"] = search
							 | 
						|
								        if attrib_list:
							 | 
						|
								            post['attrib'] = attrib_list
							 | 
						|
								
							 | 
						|
								        options = self._get_search_options(
							 | 
						|
								            category=category,
							 | 
						|
								            attrib_values=attrib_values,
							 | 
						|
								            pricelist=pricelist,
							 | 
						|
								            **post
							 | 
						|
								        )
							 | 
						|
								        fuzzy_search_term, product_count, search_product = \
							 | 
						|
								            self._shop_lookup_products(attrib_set, options, post, search, website)
							 | 
						|
								
							 | 
						|
								        website_domain = website.website_domain()
							 | 
						|
								        categs_domain = [('parent_id', '=', False)] + website_domain
							 | 
						|
								
							 | 
						|
								        if search:
							 | 
						|
								            search_categories = Category.search(
							 | 
						|
								                [('product_tmpl_ids', 'in', search_product.ids)] + website_domain
							 | 
						|
								            ).parents_and_self
							 | 
						|
								            categs_domain.append(('id', 'in', search_categories.ids))
							 | 
						|
								        else:
							 | 
						|
								            search_categories = Category
							 | 
						|
								        categs = lazy(lambda: Category.search(categs_domain))
							 | 
						|
								
							 | 
						|
								        if category:
							 | 
						|
								            url = "/menu/category/%s" % slug(category)
							 | 
						|
								
							 | 
						|
								        pager = website.pager(url=url, total=product_count, page=page,
							 | 
						|
								                              step=ppg, scope=7, url_args=post)
							 | 
						|
								        offset = pager['offset']
							 | 
						|
								        products = search_product[offset:offset + ppg]
							 | 
						|
								
							 | 
						|
								        ProductAttribute = request.env['product.attribute']
							 | 
						|
								        if products:
							 | 
						|
								            attributes = lazy(lambda: ProductAttribute.search([
							 | 
						|
								                ('product_tmpl_ids', 'in', search_product.ids),
							 | 
						|
								                ('visibility', '=', 'visible'),
							 | 
						|
								            ]))
							 | 
						|
								        else:
							 | 
						|
								            attributes = lazy(lambda: ProductAttribute.browse(attributes_ids))
							 | 
						|
								
							 | 
						|
								        layout_mode = request.session.get('website_sale_shop_layout_mode')
							 | 
						|
								        if not layout_mode:
							 | 
						|
								            if website.viewref('website_sale.products_list_view').active:
							 | 
						|
								                layout_mode = 'list'
							 | 
						|
								            else:
							 | 
						|
								                layout_mode = 'grid'
							 | 
						|
								            request.session['website_sale_shop_layout_mode'] = layout_mode
							 | 
						|
								
							 | 
						|
								        products_prices = lazy(lambda: products._get_sales_prices(pricelist))
							 | 
						|
								
							 | 
						|
								        values = {
							 | 
						|
								            'order': post.get('order', ''),
							 | 
						|
								            'category': category,
							 | 
						|
								            'attrib_values': attrib_values,
							 | 
						|
								            'attrib_set': attrib_set,
							 | 
						|
								            'pager': pager,
							 | 
						|
								            'pricelist': pricelist,
							 | 
						|
								            'add_qty': add_qty,
							 | 
						|
								            'products': products,
							 | 
						|
								            'search_product': search_product,
							 | 
						|
								            'search_count': product_count,
							 | 
						|
								            'bins': lazy(lambda: TableComputeCoffee().process(products, ppg, ppr)),
							 | 
						|
								            'ppg': ppg,
							 | 
						|
								            'ppr': ppr,
							 | 
						|
								            'categories': categs,
							 | 
						|
								            'attributes': attributes,
							 | 
						|
								            'keep': keep,
							 | 
						|
								            'search_categories_ids': search_categories.ids,
							 | 
						|
								            'products_prices': products_prices,
							 | 
						|
								            'get_product_prices': lambda product: lazy(
							 | 
						|
								                lambda: products_prices[product.id]
							 | 
						|
								            ),
							 | 
						|
								            'float_round': tools.float_round,
							 | 
						|
								        }
							 | 
						|
								        if category:
							 | 
						|
								            values['main_object'] = category
							 | 
						|
								        values.update(self._get_additional_shop_values(values))
							 | 
						|
								        return request.render("theme_coffee_shop.coffee_menu", values)
							 | 
						|
								
							 |