# -*- coding: utf-8 -*- ################################################################################### # # Cybrosys Technologies Pvt. Ltd. # Copyright (C) 2019-TODAY Cybrosys Technologies(). # # This program is free software: you can modify # it under the terms of the GNU Affero General Public License (AGPL) as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # # 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 Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # 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.base.models.ir_qweb_fields import nl2br from odoo.addons.http_routing.models.ir_http import slug from odoo.addons.payment.controllers.portal import PaymentProcessing from odoo.addons.website.controllers.main import QueryURL from odoo.addons.website.models.ir_http import sitemap_qs2dom from odoo.exceptions import ValidationError from odoo.addons.website.controllers.main import Website from odoo.addons.website_sale.controllers.main import WebsiteSale from odoo.osv import expression _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, 'class': " ".join(x.html_class for x in p.website_style_ids if x.html_class) } 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)