This is an old revision of the document!


type:
snippet
category:
Misc cart and checkout
name:
Product Bundles/Addons
reference:
http://forum.foxycart.com/comments.php?DiscussionID=1572&page=1#Comment_25071
tags:
Advance, Snippets, cart and checkout
date:
2011-05-27

Product Bundles/Addons

Allows setting certain products that are dependent on a parent product being in the cart. Ensures that all grouped products share the same quantity and removes them if any are missing.

So just to detail whats going on here, this is what the script does:

  1. Loops through the productPairs array
  2. Loops through fc_json.products looking for the parent product
    1. Overwrites change and keyup functions and the delete function
    2. Sets a variable noting if the product was found or not
  3. Loops through the dependent products array for the current productPairs array
  4. Loops through fc_json.products looking for each dependent product
    1. Sets the quantity input to read only and adds a fc_readonly class
    2. Hides the delete button
    3. Adds a fc_dependent class to the parent tr
    4. Adds a span to the dependent products name stating which parent product it came from
    5. Compares its quantity to the parent quantity
      1. If different, updates the quantity, shows an error and prevents checkout
    6. Sets a variable noting if the variable was not found
  5. If a parent or dependent product was not found, shows an error message, makes the parent product read only with a class fc_readonly and prevents checkout

Paste the following just above the closing </head> tag in your *cart* template. Then edit the productPairs array at the top of the code. Note that it relies on the product code to find the products, so they need to be set *and unique* for each product.

<script type="text/javascript">
  jQuery(document).ready(function($){
    // [["parentCode"], ["dependantCode1","dependantCode2"]]
    var productPairs = [
      [["sub"], ["subfee"]],
      [["tee"], ["freetee","freeteefee"]]
    ];
 
    for (var c = 0; c < productPairs.length; c++) {
      var parentFound = false;
      var parentName = "";
      var parentQuantity = 0;
      for (var p = 0; p < fc_json.products.length; p++) {
        // setup parent elements
        if (productPairs[c][0] == fc_json.products[p].code) {
          parentFound = true;
          parentName = fc_json.products[p].name;
          var parentChildren = jQuery("input[value="+fc_json.products[p].id+"]").parent("td").next("td.fc_cart_item_quantity");
          parentQuantity = jQuery(parentChildren).children("input").val();
 
          jQuery(parentChildren).children("input").addClass("parent-"+c).unbind().change(function() {
            var id = jQuery(this).attr('class').match(/parent-(\d+)/)[1]
            jQuery("input.child-"+id).val(jQuery(this).val());
            fc_TestCheckout();
          }).keyup(function(event){
            var id = jQuery(this).attr('class').match(/parent-(\d+)/)[1]
            jQuery("input.child-"+id).val(jQuery(this).val());
            fc_TestCheckout(event);
          });
          jQuery(parentChildren).children("span").children("a").attr("onclick","").click(function() {
            var id = jQuery(this).parent("span").siblings("input.fc_cart_item_quantity").attr('class').match(/parent-(\d+)/)[1];
            jQuery("input.parent-"+id).val(0);
            jQuery("input.child-"+id).val(0);
            fc_TestCheckout();
          });
          p = fc_json.products.length; // Finishes the loop
        }
      }
 
      // Find dependant fields
      var dependantMissing = false;
      for (var d = 0; d < productPairs[c][1].length; d++) {
        var dependantFound = false;
        for (var p = 0; p < fc_json.products.length; p++) {
          if (productPairs[c][1][d] == fc_json.products[p].code) {
            var dependantFound = true;
            jQuery("input[value="+fc_json.products[p].id+"]").parent("td").next("td")
            var dependantChildren = jQuery("input[value="+fc_json.products[p].id+"]").parent("td").next("td.fc_cart_item_quantity");
            jQuery(dependantChildren).children("input").attr("readonly", true).addClass("fc_readonly child-"+c);
            jQuery(dependantChildren).children("span").hide();
            jQuery(dependantChildren).parent("tr").addClass("fc_dependant");
            jQuery("input[value="+fc_json.products[p].id+"]").siblings(".fc_cart_item_options").before("<span class=\"fc_dependant_text\"> - added with '"+parentName+"'</span>");
            if (jQuery(dependantChildren).children("input").val() != parentQuantity) {
              jQuery(dependantChildren).children("input").val(parentQuantity);
              showError("The product '"+fc_json.products[p].name+"' must have a matching quantity to '"+parentName+"'. The quantity has been updated, please update the cart to save the new quantity.");
              fc_PreventCheckout();
            }
            p = fc_json.products.length; // Finishes the loop
          }
        }
        if (dependantFound === false) {
          dependantMissing = true;
        }
      }
      if (parentFound === false || dependantMissing === true) {
        if (parentFound === false && dependantMissing === false) {
          showError("Parent product missing. Please re-add the product '"+parentName+"'.");
        } else if (parentFound === true && dependantMissing === true) {
          showError("A required addon product for '"+parentName+"' is missing. Please re-add the product.");
        }
        jQuery("input.parent-"+c).val(0).attr("readonly", true).addClass("fc_readonly");
        jQuery("input.child-"+c).val(0);
        fc_PreventCheckout();
      }
    }
  });
 
  function showError(text) {
    if ($('#fc_error_container ul li').length) {
      jQuery("#fc_error_container ul").append("<li>"+text+"</li>");
    } else {
      jQuery("table#fc_cart_table").before("<div id=\"fc_message_container\"><div id=\"fc_error_container\" class=\"fc_message fc_error\"><ul><li>"+text+"</li></ul></div></div>");
    }
  }
</script>
<style type="text/css" media="screen">
  input.fc_readonly {
    color:#888;
    border:1px solid #CCC;
    background:#F1F1F1;
  }
  tr.fc_dependant td {
    background:#F1f1f1 !important;
  }
  span.fc_dependant_text {
    color:#AAA;
    font-style:italic;
  }
</style>

How to implement:

  1. Add a 'code' attribute to any of your products that are grouped, ensuring that they are unique across your product catalogue. For grouped products, this script leans towards them being added at the same time, through the same add-to-cart link or form. For adding multiple products in one click, http://wiki.foxycart.com/v/0.7.0/getting_started/adding_links_and_forms#adding_multiple_distinct_products_at_once.
  2. Paste the code in the pastie into your cart template, just above the </head> tag in the template.
  3. At the top of the code you just pasted, there is an array called productPairs. You need to edit this to be the codes of your grouped products. Add them in the following format [ [ “ ParentProduct ” ] ,[ “ DependentProduct1 ” , “ DependentProduct2 ” ] ]. If you have more than one groups, ensure that there is a comma after each grouping except the last.
  4. Test, test, and test some more

Worth noting, this script was developed on an 070 based test store. I can't think of a reason that it shouldn't work on 060, but obviously test thoroughly no matter what your store version.

Site Tools