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.
251 lines
9.9 KiB
251 lines
9.9 KiB
# -*- coding: utf-8 -*-
|
|
###################################################################################
|
|
#
|
|
# Cybrosys Technologies Pvt. Ltd.
|
|
# Copyright (C) 2019-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
|
|
#
|
|
# 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 <https://www.gnu.org/licenses/>.
|
|
#
|
|
###################################################################################
|
|
|
|
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/<int:page>''' ,
|
|
'''/shop/category/<model("product.public.category"):category>''' ,
|
|
'''/shop/category/<model("product.public.category"):categorys>/page/<int:page>''' ,
|
|
'''/shop/brand/<model("product.brand"):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)
|
|
|