[IMP] website_sale: bin packing product for sweet layout
bzr revid: chm@openerp.com-20130925180538-qnq699csoof8jrt5
This commit is contained in:
parent
b0f8591ec2
commit
b8f1ffaafc
|
@ -1,3 +1,4 @@
|
|||
import controllers
|
||||
import website_styles
|
||||
import product
|
||||
import website_sale
|
||||
|
|
|
@ -68,6 +68,125 @@ class Ecommerce(http.Controller):
|
|||
|
||||
return (categories, full_category_ids)
|
||||
|
||||
def bin_packing_product_ids(self, domain, limit, offset):
|
||||
product_obj = request.registry.get('product.template')
|
||||
data_obj = request.registry.get('ir.model.data')
|
||||
|
||||
product_ids = product_obj.search(request.cr, request.uid, domain, limit=limit, offset=offset, context=request.context)
|
||||
|
||||
size_ids = {}
|
||||
data_domain = [('model', '=', 'website.product.style'), ('name', 'like', 'size%')]
|
||||
data_ids = data_obj.search(request.cr, SUPERUSER_ID, data_domain, context=request.context)
|
||||
for data in data_obj.read(request.cr, SUPERUSER_ID, data_ids, ['name', 'res_id'], context=request.context):
|
||||
size_ids[data['res_id']] = [int(data['name'][-3]), int(data['name'][-1])]
|
||||
|
||||
product_list = []
|
||||
bin_packing = {}
|
||||
bin_packing[0] = {}
|
||||
|
||||
for product in product_obj.browse(request.cr, SUPERUSER_ID, product_ids, context=request.context):
|
||||
index = len(product_list)
|
||||
|
||||
# get size and all html classes
|
||||
_class = ""
|
||||
x = 1
|
||||
y = 1
|
||||
for style_id in product.website_style_ids:
|
||||
if style_id.id in size_ids:
|
||||
size = size_ids[style_id.id]
|
||||
x = size[0]
|
||||
y = size[1]
|
||||
elif style_id.html_class:
|
||||
_class += " " + style_id.html_class
|
||||
product_list.append({'product': product, 'x': x, 'y': y, 'class': _class })
|
||||
|
||||
# bin packing products
|
||||
insert = False
|
||||
line = 0
|
||||
while not insert:
|
||||
# if not full column get next line
|
||||
if len(bin_packing.setdefault(line, {})) >= 4:
|
||||
line += 1
|
||||
continue
|
||||
|
||||
col = 0
|
||||
while col < 4:
|
||||
if bin_packing[line].get(col, None) != None:
|
||||
col += 1
|
||||
continue
|
||||
|
||||
insert = True
|
||||
|
||||
copy_line = line
|
||||
copy_y = y
|
||||
while copy_y > 0:
|
||||
# check if the box can be inserted
|
||||
copy_col = col
|
||||
copy_x = x
|
||||
while copy_x > 0:
|
||||
if copy_col >= 4 or bin_packing.setdefault(copy_line, {}).get(copy_col, None) != None:
|
||||
insert = False
|
||||
break
|
||||
copy_col += 1
|
||||
copy_x -= 1
|
||||
if not insert:
|
||||
break
|
||||
copy_line += 1
|
||||
copy_y -= 1
|
||||
|
||||
if not insert:
|
||||
col += 1
|
||||
continue
|
||||
|
||||
copy_y = y
|
||||
while copy_y > 0:
|
||||
copy_y -= 1
|
||||
copy_x = x
|
||||
while copy_x > 0:
|
||||
copy_x -= 1
|
||||
bin_packing[line + copy_y][col + copy_x] = False
|
||||
bin_packing[line + copy_y][col + copy_x] = product_list[index]
|
||||
break
|
||||
|
||||
if not insert:
|
||||
line += 1
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
# complete empty cell and packaging in list (from dict)
|
||||
nb_empty = 0
|
||||
line = 0
|
||||
length = len(bin_packing)
|
||||
while line < length:
|
||||
col = 0
|
||||
while col < 4:
|
||||
if bin_packing[line].get(col) == None:
|
||||
nb_empty += 1
|
||||
col += 1
|
||||
line += 1
|
||||
|
||||
products = []
|
||||
if nb_empty:
|
||||
product_ids = product_obj.search(request.cr, request.uid, domain, limit=nb_empty, offset=offset+limit, context=request.context)
|
||||
if product_ids:
|
||||
for product in product_obj.browse(request.cr, SUPERUSER_ID, product_ids, context=request.context):
|
||||
products.append(product)
|
||||
products.reverse()
|
||||
|
||||
bin_packing_list = []
|
||||
line = 0
|
||||
while line < length:
|
||||
bin_packing_list.append([])
|
||||
col = 0
|
||||
while col < 4:
|
||||
if products and bin_packing[line].get(col) == None:
|
||||
bin_packing[line][col] = {'product': products.pop(), 'x': 1, 'y': 1, 'class': _class }
|
||||
bin_packing_list[line].append(bin_packing[line].get(col, False))
|
||||
col += 1
|
||||
line += 1
|
||||
return bin_packing_list
|
||||
|
||||
@website.route(['/shop/', '/shop/category/<cat_id>/', '/shop/category/<cat_id>/page/<int:page>/', '/shop/page/<int:page>/'], type='http', auth="public")
|
||||
def category(self, cat_id=0, page=0, **post):
|
||||
|
||||
|
@ -92,14 +211,12 @@ class Ecommerce(http.Controller):
|
|||
product_count = len(product_obj.search(request.cr, request.uid, domain, context=request.context))
|
||||
pager = request.website.pager(url="/shop/category/%s/" % cat_id, total=product_count, page=page, step=step, scope=7, url_args=post)
|
||||
|
||||
product_ids = product_obj.search(request.cr, request.uid, domain, limit=step, offset=pager['offset'], context=request.context)
|
||||
|
||||
request.context['pricelist'] = self.get_pricelist()
|
||||
|
||||
values = {
|
||||
'get_categories': self.get_categories,
|
||||
'category_id': cat_id,
|
||||
'products': product_obj.browse(request.cr, SUPERUSER_ID, product_ids, context=request.context),
|
||||
'table_products': self.bin_packing_product_ids(domain, limit=step, offset=pager['offset']),
|
||||
'search': post.get("search"),
|
||||
'pager': pager,
|
||||
}
|
||||
|
|
|
@ -37,14 +37,7 @@ class product_template(osv.Model):
|
|||
'website_description': fields.html('Description for the website'),
|
||||
'suggested_product_id': fields.many2one('product.template', 'Suggested For Product'),
|
||||
'suggested_product_ids': fields.one2many('product.template', 'suggested_product_id', 'Suggested Products'),
|
||||
'website_sizex': fields.selection(map(lambda x: (str(x+1),str(x+1)), range(12)), 'Size X'),
|
||||
'website_sizey': fields.selection(map(lambda x: (str(x+1),str(x+1)), range(6)), 'Size Y'),
|
||||
'website_product_class': fields.selection([('','Default'), ('oe_image_full','Image Full')], 'Size Y'),
|
||||
}
|
||||
_defaults = {
|
||||
'website_sizex': '3',
|
||||
'website_sizey': '2',
|
||||
'website_product_class': '',
|
||||
'website_style_ids' : fields.many2many('website.product.style','product_website_style_rel', 'product_id', 'style_id', 'Styles'),
|
||||
}
|
||||
|
||||
def recommended_products(self, cr, uid, ids, context=None):
|
||||
|
|
|
@ -116,6 +116,7 @@
|
|||
border: 1px solid rgba(100, 100, 100, 0.2);
|
||||
max-width: 100%;
|
||||
max-height: 140px;
|
||||
position: relative;
|
||||
}
|
||||
.oe_list_products .oe_product_image {
|
||||
position: absolute;
|
||||
|
@ -142,3 +143,7 @@
|
|||
right: 0;
|
||||
border-top: 1px solid #dddddd;
|
||||
}
|
||||
|
||||
.oe_website_sale .row .row .col-md-12 {
|
||||
float: none;
|
||||
}
|
||||
|
|
|
@ -110,6 +110,7 @@
|
|||
border: 1px solid rgba(100, 100, 100, 0.2)
|
||||
max-width: 100%
|
||||
max-height: 140px
|
||||
position: relative
|
||||
.oe_product_image
|
||||
position: absolute
|
||||
left: 15px
|
||||
|
@ -132,3 +133,6 @@
|
|||
left: 180px
|
||||
right: 0
|
||||
border-top: 1px solid #dddddd
|
||||
|
||||
.oe_website_sale .row .row .col-md-12
|
||||
float: none
|
|
@ -81,6 +81,35 @@
|
|||
|
||||
<!-- Product list -->
|
||||
|
||||
<template id="products_cart">
|
||||
<div class="oe_product_description">
|
||||
<a t-attf-href="/shop/product/#{ product.id }/?#{ search and ('search=%s' % search) or ''}#{ category_id and ('&category_id=%s' % category_id) or ''}">
|
||||
<b t-field="product.name"/>
|
||||
</a>
|
||||
<!-- This should be an option -->
|
||||
<div t-if="product.description_sale" class="text-muted oe_subdescription">
|
||||
<div id="product_description" t-att-data-name="product.id"/>
|
||||
</div>
|
||||
<div>
|
||||
<b>
|
||||
<t t-if="product.product_variant_ids[0].lst_price != product.product_variant_ids[0].price">
|
||||
<span class="text-danger" style="text-decoration: line-through;">
|
||||
<t t-esc="product.product_variant_ids[0].lst_price" /> €
|
||||
</span>&nbsp;
|
||||
</t>
|
||||
<t t-esc="product.product_variant_ids[0].price" /> €
|
||||
</b>
|
||||
<span id="add_to_cart" t-att-data-name="product.id"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="oe_product_image text-center">
|
||||
<a t-attf-href="/shop/product/#{ product.id }/?#{ search and ('search=%s' % search) or ''}#{ category_id and ('&category_id=%s' % category_id) or ''}">
|
||||
<img class="img" t-att-src="product.img('image')"/>
|
||||
</a>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="products" page="True">
|
||||
<t t-call="website.layout">
|
||||
<t t-set="head">
|
||||
|
@ -110,41 +139,32 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class='row style_default'>
|
||||
<div class="col-md-12" id="products_grid">
|
||||
<t t-foreach="products" t-as="product">
|
||||
<div t-attf-class="col-md-#{ search and 3 or product.website_sizex } oe_product oe-height-#{ search and 2 or product.website_sizey } #{ product.website_product_class}" t-att-data-publish="product.website_published and 'on' or 'off'" data-name="products_layout">
|
||||
|
||||
<div class="oe_product_description">
|
||||
<a t-attf-href="/shop/product/#{ product.id }/?#{ search and ('search=%s' % search) or ''}#{ category_id and ('&category_id=%s' % category_id) or ''}">
|
||||
<b t-field="product.name"/>
|
||||
</a>
|
||||
<!-- This should be an option -->
|
||||
<div t-if="product.description_sale" class="text-muted oe_subdescription">
|
||||
<div id="product_description" t-att-data-name="product.id"/>
|
||||
</div>
|
||||
<div>
|
||||
<b>
|
||||
<t t-if="product.product_variant_ids[0].lst_price != product.product_variant_ids[0].price">
|
||||
<span class="text-danger" style="text-decoration: line-through;">
|
||||
<t t-esc="product.product_variant_ids[0].lst_price" /> €
|
||||
</span>&nbsp;
|
||||
</t>
|
||||
<t t-esc="product.product_variant_ids[0].price" /> €
|
||||
</b>
|
||||
<span id="add_to_cart" t-att-data-name="product.id"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="oe_product_image text-center">
|
||||
<a t-attf-href="/shop/product/#{ product.id }/?#{ search and ('search=%s' % search) or ''}#{ category_id and ('&category_id=%s' % category_id) or ''}">
|
||||
<img class="img" t-att-src="product.img('image')"/>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
<div class='style_default'>
|
||||
<table class="col-md-12" id="products_grid">
|
||||
<colgroup>
|
||||
<col style="width: 1px; margin-right: -1px;" />
|
||||
<col class="col-md-3" />
|
||||
<col class="col-md-3" />
|
||||
<col class="col-md-3" />
|
||||
<col class="col-md-3" />
|
||||
</colgroup>
|
||||
<tbody>
|
||||
<tr t-foreach="table_products" t-as="tr_product">
|
||||
<td class="oe-height-2"></td>
|
||||
<t t-foreach="tr_product" t-as="td_product">
|
||||
<t t-if="td_product">
|
||||
<t t-set="product" t-value="td_product['product']"/>
|
||||
<td t-att-colspan="td_product['x']"
|
||||
t-att-rowspan="td_product['y']"
|
||||
t-attf-class="oe_product #{ td_product['class'] }"
|
||||
t-att-data-publish="product.website_published and 'on' or 'off'">
|
||||
<t t-call="website_sale.products_cart"/>
|
||||
</td>
|
||||
</t>
|
||||
</t>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<t t-call="website.pager" />
|
||||
|
@ -178,7 +198,7 @@
|
|||
<!-- List view of products -->
|
||||
|
||||
<template id="list_view" inherit_option_id="website_sale.products" name="List View">
|
||||
<xpath expr="//div[@data-name='products_layout']" position="attributes">
|
||||
<xpath expr="//div[@id='products_grid']" position="replace">
|
||||
<attribute name="class">col-md-12 oe_list_products oe-height-1</attribute>
|
||||
</xpath>
|
||||
</template>
|
||||
|
|
|
@ -12,5 +12,47 @@
|
|||
<field name="state">open</field>
|
||||
</record>
|
||||
|
||||
<record id="website_sale.image_full" model="website.product.style">
|
||||
<field name="name">Image Full</field>
|
||||
<field name="html_class">oe_image_full</field>
|
||||
</record>
|
||||
|
||||
<record id="website_sale.size2x1" model="website.product.style">
|
||||
<field name="name">Size 2x1</field>
|
||||
<field name="html_class">col-md-6 oe-height-1</field>
|
||||
</record>
|
||||
<record id="website_sale.size3x1" model="website.product.style">
|
||||
<field name="name">Size 3x1</field>
|
||||
<field name="html_class">col-md-9 oe-height-1</field>
|
||||
</record>
|
||||
<record id="website_sale.size1x2" model="website.product.style">
|
||||
<field name="name">Size 1x2</field>
|
||||
<field name="html_class">col-md-3 oe-height-2</field>
|
||||
</record>
|
||||
<record id="website_sale.size1x3" model="website.product.style">
|
||||
<field name="name">Size 1x3</field>
|
||||
<field name="html_class">col-md-3 oe-height-2</field>
|
||||
</record>
|
||||
<record id="website_sale.size2x2" model="website.product.style">
|
||||
<field name="name">Size 2x2</field>
|
||||
<field name="html_class">col-md-6 oe-height-2</field>
|
||||
</record>
|
||||
<record id="website_sale.size2x3" model="website.product.style">
|
||||
<field name="name">Size 2x3</field>
|
||||
<field name="html_class">col-md-6 oe-height-3</field>
|
||||
</record>
|
||||
<record id="website_sale.size3x2" model="website.product.style">
|
||||
<field name="name">Size 2x3</field>
|
||||
<field name="html_class">col-md-9 oe-height-2</field>
|
||||
</record>
|
||||
<record id="website_sale.size3x3" model="website.product.style">
|
||||
<field name="name">Size 2x3</field>
|
||||
<field name="html_class">col-md-9 oe-height-3</field>
|
||||
</record>
|
||||
<record id="website_sale.size4x3" model="website.product.style">
|
||||
<field name="name">Size 2x3</field>
|
||||
<field name="html_class">col-md-9 oe-height-3</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
|
|
@ -8,9 +8,7 @@
|
|||
|
||||
<record id="product.product_product_4" model="product.product">
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="website_product_class">oe_image_full</field>
|
||||
<field name="website_sizex">6</field>
|
||||
<field name="website_sizey">4</field>
|
||||
<field name="website_style_ids" eval="[(6,0,[ref('website_sale.image_full'),ref('website_sale.size2x2')])]"/>
|
||||
<field name="website_description" type="html">
|
||||
<section snippet-id="text-image" class="mt16 mb16 dark">
|
||||
<div class="container">
|
||||
|
@ -59,11 +57,11 @@
|
|||
</record>
|
||||
<record id="product.product_product_5" model="product.product">
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="website_sizex">6</field>
|
||||
<field name="website_style_ids" eval="[(6,0,[ref('website_sale.size2x1')])]"/>
|
||||
</record>
|
||||
<record id="product.product_product_6" model="product.product">
|
||||
<field name="website_published" eval="True"/>
|
||||
<field name="website_product_class">oe_image_full</field>
|
||||
<field name="website_style_ids" eval="[(6,0,[ref('website_sale.image_full')])]"/>
|
||||
</record>
|
||||
<record id="product.product_product_7" model="product.product">
|
||||
<field name="website_published" eval="True"/>
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2013-Today OpenERP SA (<http://www.openerp.com>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License 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 <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv import osv, fields
|
||||
|
||||
|
||||
class website_product_style(osv.Model):
|
||||
_name = "website.product.style"
|
||||
_columns = {
|
||||
'name' : fields.char('Style Name', required=True, translate=True),
|
||||
'html_class': fields.char('HTML Classes', size=64),
|
||||
}
|
Loading…
Reference in New Issue