# -*- coding: utf-8 -*- ############################################################################# # # Cybrosys Technologies Pvt. Ltd. # # Copyright (C) 2020-TODAY Cybrosys Technologies() # Author: Midilaj () # # 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 . # ############################################################################# import json import logging from datetime import datetime from werkzeug.exceptions import Forbidden, 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_sale.controllers.main import WebsiteSale _logger = logging.getLogger(__name__) class TableCompute(object): def __init__(self): self.table = {} def _check_place(self, posx, posy, sizex, sizey, ppr): res = True for y in range(sizey): for x in range(sizex): if posx + x >= ppr: res = False break row = self.table.setdefault(posy + y, {}) if row.setdefault(posx + x) is not None: res = False break for x in range(ppr): self.table[posy + y].setdefault(x, None) return res def process(self, products, ppg=20, ppr=4): # Compute products positions on the grid minpos = 0 index = 0 maxy = 0 x = 0 for p in products: x = min(max(p.website_size_x, 1), ppr) y = min(max(p.website_size_y, 1), ppr) if index >= ppg: x = y = 1 pos = minpos while not self._check_place(pos % ppr, pos // ppr, x, y, ppr): pos += 1 # if 21st products (index 20) and the last line is full (ppr products in it), break # (pos + 1.0) / ppr is the line where the product would be inserted # maxy is the number of existing lines # + 1.0 is because pos begins at 0, thus pos 20 is actually the 21st block # and to force python to not round the division operation if index >= ppg and ((pos + 1.0) // ppr) > maxy: break if x == 1 and y == 1: # simple heuristic for CPU optimization minpos = pos // ppr for y2 in range(y): for x2 in range(x): self.table[(pos // ppr) + y2][(pos % ppr) + x2] = False self.table[pos // ppr][pos % ppr] = { 'product': p, 'x': x, 'y': y, 'ribbon': p.website_ribbon_id, } if index <= ppg: maxy = max(maxy, y + (pos // ppr)) index += 1 # Format table according to HTML needs rows = sorted(self.table.items()) rows = [r[1] for r in rows] for col in range(len(rows)): cols = sorted(rows[col].items()) x += len(cols) rows[col] = [r[1] for r in cols if r[1]] return rows class WebsiteSales(WebsiteSale): @http.route([ '''/shop''', '''/shop/page/''', '''/shop/category/''', '''/shop/category//page/''', '''/shop/brand/''', ], type='http', auth="public", website=True) def shop(self, page=0, category=None, search='', ppg=False, brand=None, **post): add_qty = int(post.get('add_qty', 1)) compute_brand = brand 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 Brand = request.env['product.brand'] if not brand: brand = Brand if ppg: try: ppg = int(ppg) post['ppg'] = ppg except ValueError: ppg = False if not ppg: ppg = request.env['website'].get_current_website().shop_ppg or 20 ppr = request.env['website'].get_current_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} domain = self._get_search_domain(search, category, attrib_values) keep = QueryURL('/shop', category=category and int(category), search=search, attrib=attrib_list, order=post.get('order')) pricelist_context, pricelist = self._get_pricelist_context() request.context = dict(request.context, pricelist=pricelist.id, partner=request.env.user.partner_id) url = "/shop" if search: post["search"] = search if attrib_list: post['attrib'] = attrib_list Product = request.env['product.template'].with_context(bin_size=True) search_product = Product.search(domain) website_domain = request.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 = Category.search(categs_domain) if category: url = "/shop/category/%s" % slug(category) product_count = len(search_product) pager = request.website.pager(url=url, total=product_count, page=page, step=ppg, scope=7, url_args=post) products = Product.search(domain, limit=ppg, offset=pager['offset'], order=self._get_search_order(post)) print(products) ProductAttribute = request.env['product.attribute'] if products: # get all products without limit attributes = ProductAttribute.search([('product_tmpl_ids', 'in', search_product.ids)]) else: attributes = ProductAttribute.browse(attributes_ids) layout_mode = request.session.get('website_sale_shop_layout_mode') if not layout_mode: if request.website.viewref('website_sale.products_list_view').active: layout_mode = 'list' else: layout_mode = 'grid' Brand = request.env['product.brand'].search([]) if compute_brand: products_brand = request.env['product.template'].search( ['&', ('brand_id', '=', brand.id), ('sale_ok', '=', True)]) product_brand_count = len(products_brand) pager_brand = request.website.pager(url=url, total=product_brand_count, page=page, step=ppg, scope=7, url_args=post) values = { 'search': search, 'category': category, 'brand': brand, 'attrib_values': attrib_values, 'attrib_set': attrib_set, 'pager': pager_brand, 'pricelist': pricelist, 'add_qty': add_qty, 'products': products_brand, 'search_count': product_brand_count, # common for all searchbox 'bins': TableCompute().process(products_brand, ppg, ppr), 'ppg': ppg, 'ppr': ppr, 'categories': categs, 'attributes': attributes, 'keep': keep, 'search_categories_ids': search_categories.ids, 'layout_mode': layout_mode, 'brands': Brand, } return request.render("website_sale.products", values) else: values = { 'brand': brand, 'search': search, 'category': category, 'attrib_values': attrib_values, 'attrib_set': attrib_set, 'pager': pager, 'pricelist': pricelist, 'add_qty': add_qty, 'products': products, 'search_count': product_count, # common for all searchbox 'bins': TableCompute().process(products, ppg, ppr), 'ppg': ppg, 'ppr': ppr, 'categories': categs, 'attributes': attributes, 'keep': keep, 'search_categories_ids': search_categories.ids, 'layout_mode': layout_mode, 'brands': Brand, } if category: values['main_object'] = category return request.render("website_sale.products", values)