[IMP] website_sale: allow optional products in shop; In shop, it opens the modal...
authorChristophe Matthieu <chm@odoo.com>
Wed, 11 Jun 2014 10:50:41 +0000 (12:50 +0200)
committerChristophe Matthieu <chm@odoo.com>
Wed, 11 Jun 2014 10:50:41 +0000 (12:50 +0200)
addons/product/product.py
addons/website_sale/controllers/main.py
addons/website_sale/models/product.py
addons/website_sale/models/sale_order.py
addons/website_sale/static/src/js/website_sale.js
addons/website_sale/views/templates.xml
addons/website_sale/views/views.xml

index db86047..34627f3 100644 (file)
@@ -802,10 +802,8 @@ class product_product(osv.osv):
                         uom.id, product.list_price, context['uom'])
             else:
                 res[product.id] = product.list_price
-            price_extra = 0.0
-            for variant_id in product.attribute_value_ids:
-                price_extra += variant_id.price_extra
-            res[product.id] =  (res[product.id] or 0.0) + price_extra
+            res[product.id] =  res[product.id] + product.price_extra
+
         return res
 
     def _get_partner_code_name(self, cr, uid, ids, product, partner_id, context=None):
@@ -864,6 +862,8 @@ class product_product(osv.osv):
     def _get_price_extra(self, cr, uid, ids, name, args, context=None):
         result = dict.fromkeys(ids, False)
         for product in self.browse(cr, uid, ids, context=context):
+            ctx = context.copy()
+            ctx.update(active_id=product.product_tmpl_id.id)
             price_extra = 0.0
             for variant_id in product.attribute_value_ids:
                 for price_id in variant_id.price_ids:
index 2322518..4cb5e6f 100644 (file)
@@ -212,6 +212,17 @@ class website_sale(http.Controller):
 
         variants = [[p.id, map(int, p.attribute_value_ids), p.price] for p in product.product_variant_ids]
 
+
+        optional_products = {}
+        for o in product.optional_product_ids:
+            if not optional_products.get(o.product_tmpl_id.id):
+                optional_products[o.product_tmpl_id.id] = {
+                    'product_tmpl_id': o.product_tmpl_id,
+                    'product_ids': []
+                }
+            if o not in optional_products[o.product_tmpl_id.id]:
+                optional_products[o.product_tmpl_id.id]['product_ids'].append(o)
+
         values = {
             'search': search,
             'category': category,
@@ -223,6 +234,7 @@ class website_sale(http.Controller):
             'main_object': product,
             'product': product,
             'variants': variants,
+            'optional_products': optional_products
         }
         return request.website.render("website_sale.product", values)
 
@@ -240,21 +252,31 @@ class website_sale(http.Controller):
 
     @http.route(['/shop/cart'], type='http', auth="public", website=True)
     def cart(self, **post):
+        cr, uid, context, pool = request.cr, request.uid, request.context, request.registry
         order = request.website.sale_get_order()
+
         values = {
             'order': order,
-            'suggested_products': [],
+            'suggested_products': []
         }
         if order:
-            if not request.context.get('pricelist'):
-                request.context['pricelist'] = order.pricelist_id.id
-            values['suggested_products'] = order._cart_accessories(context=request.context)
+            if not context.get('pricelist'):
+                context['pricelist'] = order.pricelist_id.id
+            values['suggested_products'] = order._cart_accessories(context=context)
+
         return request.website.render("website_sale.cart", values)
 
     @http.route(['/shop/cart/update'], type='http', auth="public", methods=['POST'], website=True)
     def cart_update(self, product_id, add_qty=1, set_qty=0, **kw):
-        cr, uid, context = request.cr, request.uid, request.context
-        request.website.sale_get_order(force_create=1)._cart_update(product_id=int(product_id), add_qty=add_qty, set_qty=set_qty)
+        cr, uid, context, pool = request.cr, request.uid, request.context, request.registry
+        order = request.website.sale_get_order(force_create=1)
+
+        print kw
+        for key,val in kw.items():
+            if 'option-' in key:
+                order._cart_update(product_id=int(key.split("-")[1]), add_qty=1)
+
+        order._cart_update(product_id=int(product_id), add_qty=add_qty, set_qty=set_qty)
         return request.redirect("/shop/cart")
 
     @http.route(['/shop/cart/update_json'], type='json', auth="public", methods=['POST'], website=True)
@@ -445,7 +467,7 @@ class website_sale(http.Controller):
 
     @http.route(['/shop/checkout'], type='http', auth="public", website=True)
     def checkout(self, **post):
-        cr, uid, context, registry = request.cr, request.uid, request.context, request.registry
+        cr, uid, context, pool = request.cr, request.uid, request.context, request.registry
 
         order = request.website.sale_get_order(force_create=1, context=context)
 
index 116822f..bedf0d1 100644 (file)
@@ -132,6 +132,7 @@ class product_template(osv.Model):
         'website_sequence': fields.integer('Sequence', help="Determine the display order in the Website E-commerce"),
         'website_url': fields.function(_website_url, string="Website url", type="char"),
         'public_categ_ids': fields.many2many('product.public.category', string='Public Category', help="Those categories are used to group similar products for e-commerce."),
+        'optional_product_ids': fields.many2many('product.product', string='Optional Products'),
     }
 
     def _defaults_website_sequence(self, cr, uid, *l, **kwargs):
index d985503..759884a 100644 (file)
@@ -116,6 +116,28 @@ class sale_order(osv.Model):
             product_ids = random.sample(s, min(len(s),3))
             return self.pool['product.product'].browse(cr, uid, product_ids, context=context)
 
+    def _cart_optional_products(self, cr, uid, ids, context=None):
+        for order in self.browse(cr, uid, ids, context=context):
+            products = {}
+            for l in order.website_order_line:
+                opt = {}
+                for o in l.product_id.optional_product_ids:
+                    if not opt.get(o.product_tmpl_id.id):
+                        opt[o.product_tmpl_id.id] = {
+                            'product_tmpl_id': o.product_tmpl_id,
+                            'product_ids': []
+                        }
+                    if o not in opt[o.product_tmpl_id.id]:
+                        opt[o.product_tmpl_id.id]['product_ids'].append(o)
+
+                if opt:
+                    products[l.id] = {
+                        'order_line_id': l,
+                        'optional_product_tmpl': opt
+                    }
+
+            return products
+
 class website(orm.Model):
     _inherit = 'website'
 
index 62c1f82..1325807 100644 (file)
@@ -50,6 +50,13 @@ $(document).ready(function () {
         $(this).closest("form").submit();
     });
 
+    // modal to select optional product in my cart
+    $("#modal_optional_products label").mousedown(function(event) {
+        $(event.currentTarget).parents('li.optional_product_tmpl:first').find("input[type=checkbox]").each(function () {
+            if($(this).parent()[0] != event.currentTarget) $(this).removeAttr('checked');
+        });
+    });
+
     // change price when they are variants
     var $price = $(".oe_price .oe_currency_value");
     $('form.js_add_cart_json label').on('mouseup', function (ev) {
@@ -70,12 +77,12 @@ $(document).ready(function () {
 
     var $form_var = $('form.js_add_cart_variants');
     var variant_ids = $form_var.data("attribute_value_ids");
-    $form_var.on('change', 'input, select', function (ev) {
+    $form_var.on('change', 'input.js_variant_change, select.js_variant_change', function (ev) {
         var values = [];
         $form_var.find("label").removeClass("text-muted css_not_available");
         $form_var.find(".a-submit").removeProp("disabled");
 
-        $form_var.find('input:checked, select').each(function () {
+        $form_var.find('input.js_variant_change:checked, select').each(function () {
             values.push(+$(this).val());
         });
         var available = false;
@@ -89,10 +96,10 @@ $(document).ready(function () {
             }
         }
 
-        $form_var.find("input:radio, select").each(function () {
+        $form_var.find("input.js_variant_change:radio, select.js_variant_change").each(function () {
             var id = +$(this).val();
             var values = [id];
-            $form_var.find(">ul>li:not(:has(input[value='" + id + "'])) input:checked, select").each(function () {
+            $form_var.find(">ul>li:not(:has(input.js_variant_change[value='" + id + "'])) input.js_variant_change:checked, select").each(function () {
                 values.push(+$(this).val());
             });
             for (var k in variant_ids) {
@@ -112,10 +119,9 @@ $(document).ready(function () {
             $(".oe_price_h4").addClass("hidden");
             $(".oe_not_available").removeClass("hidden");
             $form_var.find('input[name="product_id"]').val(0);
-            $form_var.find(".a-submit").prop("disabled", "disabled");
+            $form_var.find(".js_check_product").prop("disabled", "disabled");
         }
     });
-    $form_var.find("input:first").trigger('change');
-
+    $form_var.find("input.js_variant_change:first").trigger('change');
 
 });
index 470ca83..9b51d81 100644 (file)
 
                     <t t-call="website_sale.product_price"/>
 
-                    <a class="btn btn-primary btn-lg mt8 a-submit">Add to Cart</a>
+                    <a t-if="not optional_products" class="btn btn-primary btn-lg mt8 a-submit js_check_product">Add to Cart</a>
+                    <a t-if="optional_products" class="btn btn-primary btn-lg mt8 js_check_product" href="#" data-toggle="modal" data-target="#modal_optional_products">Add to Cart</a>
+
+                    <t t-call="website_sale.product_modal_optional_products"/>
+
                 </form>
 
                 <hr t-if="product.description_sale"/>
           </div>
         </section>
         <div itemprop="description" t-field="product.website_description" class="oe_structure mt16" id="product_full_description"/>
+
       </div>
   </t>
 </template>
 
+<template id="product_modal_optional_products">
+  <div t-if="optional_products" id="modal_optional_products" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
+    <div class="modal-dialog">
+      <div class="modal-content">
+        <div class="modal-header">
+          <button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
+          <h4 class="modal-title" id="myModalLabel">Choose your optional products</h4>
+        </div>
+        <div class="modal-body">
+          <ul>
+            <li class="optional_product_tmpl" t-foreach="optional_products" t-as="t">
+              <t t-set="o_tmpl" t-value="optional_products[t]"/>
+
+              <t t-if="len(o_tmpl['product_ids']) == 1">
+                <label><input t-att-name="'option-%s' % o_tmpl['product_ids'][0].id" type="checkbox"/> <span t-field="o_tmpl['product_tmpl_id'].name"/></label>
+
+                <span class="badge" t-if="o_tmpl['product_ids'][0].price">
+                    + <span t-field="o_tmpl['product_ids'][0].price" t-field-options='{
+                           "widget": "monetary",
+                           "display_currency": "website.pricelist_id.currency_id"
+                      }'/>
+                </span>
+              </t>
+              <t t-if="len(o_tmpl['product_ids']) > 1">
+                <span t-field="o_tmpl['product_tmpl_id'].name"/>
+                <ul>
+                  <li t-foreach="o_tmpl['product_ids']" t-as="product_id">
+                    <label><input t-att-name="'option-%s' % product_id.id" type="checkbox"/> <span t-esc="', '.join([value_id.name for value_id in product_id.attribute_value_ids])"/></label>
+                    <span class="badge" t-if="product_id.price">
+                      + <span t-field="product_id.price" t-field-options='{
+                             "widget": "monetary",
+                             "display_currency": "website.pricelist_id.currency_id"
+                        }'/>
+                    </span>
+                  </li>
+                </ul>
+              </t>
+            </li>
+          </ul>
+        </div>
+        <div class="modal-footer">
+          <a class="btn btn-primary a-submit">Continue</a>
+          <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
 <template id="product_price">
   <div itemprop="offers" itemscope="itemscope" itemtype="http://schema.org/Offer" class="product_price mt16">
       <h4 class="oe_price_h4">
                 <strong t-field="variant_id.attribute_id.name"/>
 
                 <t t-if="variant_id.attribute_id.type == 'select'">
-                  <select class="form-control" t-att-name="'attribute-%s' % variant_id.attribute_id.id">
+                  <select class="form-control js_variant_change" t-att-name="'attribute-%s' % variant_id.attribute_id.id">
                     <t t-foreach="variant_id.value_ids" t-as="value_id">
                       <option t-att-value="value_id.id">
                           <span t-field="value_id.name"/>
                       <t t-foreach="variant_id.value_ids" t-as="value_id">
                           <li t-if="value_id.product_ids" class="form-group js_attribute_value" style="margin: 0;">
                               <label class="control-label" style="margin: 0 20px;">
-                                  <input type="radio" t-att-checked="'checked' if not inc else ''" t-att-name="'attribute-%s' % variant_id.attribute_id.id" t-att-value="value_id.id" style="vertical-align: top; margin-right: 10px;"/>
+                                  <input type="radio" class="js_variant_change" t-att-checked="'checked' if not inc else ''" t-att-name="'attribute-%s' % variant_id.attribute_id.id" t-att-value="value_id.id" style="vertical-align: top; margin-right: 10px;"/>
                                   <span t-field="value_id.name"/>
                                   <span class="badge" t-if="value_id.price_extra">
                                       <t t-esc="value_id.price_extra > 0 and '+' or ''"/><span t-field="value_id.price_extra" t-field-options='{
                       <t t-foreach="variant_id.value_ids" t-as="value_id">
                           <label t-attf-style="background-color:#{value_id.color or value_id.name}"
                               t-attf-class="css_attribute_color #{'active' if not inc else ''}">
-                            <input type="radio"
+                            <input type="radio" class="js_variant_change"
                               t-att-checked="'checked' if not inc else ''"
                               t-att-name="'attribute-%s' % variant_id.attribute_id.id"
                               t-att-value="value_id.id"
 
         <t t-call="website_sale.product_price"/>
 
-        <a class="btn btn-primary btn-lg mt8 a-submit">Add to Cart</a>
+        <a t-if="not optional_products" class="btn btn-primary btn-lg mt8 a-submit">Add to Cart</a>
+        <a t-if="optional_products" class="btn btn-primary btn-lg mt8 " href="#" data-toggle="modal" data-target="#modal_optional_products">Add to Cart</a>
+
+        <t t-call="website_sale.product_modal_optional_products"/>
     </form>
   </xpath>
 </template>
                   </table>
                   <t t-call="website_sale.total"/>
                   <div class="clearfix"/>
-                  <a t-if="website_sale_order and website_sale_order.website_order_line" href="/shop/checkout" class="btn btn-primary pull-right mb32">Process Checkout <span class="fa fa-long-arrow-right"/></a>
+
+                  <a t-if="not optional_products and website_sale_order and website_sale_order.website_order_line" class="btn btn-primary pull-right mb32" href="/shop/checkout">Process Checkout <span class="fa fa-long-arrow-right"/></a>
+
                   <div class="oe_structure"/>
               </div>
               <div class="col-lg-3 col-lg-offset-1 col-sm-3 col-md-3 text-muted" id="right_column">
index 86e369f..c6c30d0 100644 (file)
@@ -26,6 +26,7 @@
                     <field name="website_published" class="pull-right" widget="website_button"/>
                     <field name="alternative_product_ids" widget="many2many_tags"/>
                     <field name="accessory_product_ids" widget="many2many_tags"/>
+                    <field name="optional_product_ids" widget="many2many_tags"/>
                     <field name="website_style_ids" widget="many2many_tags"/>
                     <field name="website_sequence"/>
                 </group>