Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
v:2.0:snippets:updating_total_price_with_option_modifers [2017/06/23 13:46] – [Step 1: Include snippet] adamv:2.0:snippets:updating_total_price_with_option_modifers [2023/07/12 10:38] (current) – [Dynamically triggering recalculation] adam
Line 10: Line 10:
 <code javascript> <code javascript>
 <script> <script>
-ADJUST={};var pricemod_regex=/[{\|]p([+\-:])([\d\.]+)(?:\D{3})?(?=[\|}])/,FC=FC||{}; +// Dynamic Price Calculation v3.3 
-FC.onLoad=function(){FC.client.on("ready.done",function(){$('form[action*="'+FC.settings.storedomain+'"]').each(function(){var b={base_price:parseFloat($(this).find("[name='price'],[name^='price||']").first().val()),quantity:1},c={},d=$(this).find("[name='quantity'],[name^='quantity||']").first(),g=!1,f=$(this).find("[name='code'],[name^='code||']");if(0!=f.length){var e=clearHash(f.first().val());$(this).find("input,select").each(function(){if("SELECT"==this.tagName){var b=!1;$(this).children("option").each(function(){-1< +var pricemod_regex=/[{\|]p([+\-:])([\d\.]+)(?:\D{3})?(?=[\|}])/,id_regex=/^(\d+):/,FC=FC||{};function initDynamicPrice(){ADJUST={},$("input,select").off("change.foxy-dynamic-price"),$('form[action*="'+FC.settings.storedomain+'"]').each((function(){var t=$(this),a="",e={products:{}};$(this).find("[name='name'],[name^='name||'],[name$=':name'],[name*=':name||']").each((function(){var r=getId(this.name),i=r?r+":":"",s=parseFloat(t.find("[name='"+i+"price'],[name^='"+i+"price||']").first().val()),c={id:r,code:"",base_price:isNaN(s)?0:s,quantity:1,attributes:{},has_quantity:!1},n=t.find("[name='"+i+"quantity'],[name^='"+i+"quantity||']"),o=t.find("[name='"+i+"code'],[name^='"+i+"code||']");if(o.length>0&&(c.code=clearHash(o.first().val()),""===a&&(a=c.code)),n.length>0){var d=0,h=getElementType(n);["select","text"].indexOf(h)>-1?(c.has_quantity=!0,d=parseFloat(clearHash(n.val()))):["radio","checkbox"].indexOf(h)>-1&&(c.has_quantity=!0,1==n.filter(":checked").length&&(d=parseFloat(clearHash(n.filter(":checked").val())))),isNaN(d)&&(d=0),c.quantity=d}e.products[r]=c}));t.attr("data-fc-form-code")&&(a=t.attr("data-fc-form-code")),""!==a&&($(this).find("input,select").each((function(){if(!this.disabled){var t=getId(this.name),r=getName(this.name),i=getElementType($(this));if("quantity"!=r)if("price"!=r||"hidden"==i){if("SELECT"==this.tagName){var s=!1;$(this).children("option").each((function(){this.value.search(pricemod_regex)>-1&&(s=!0)})),s&&($(this).data("fc-adjust-for",a),e.products[t].attributes[clearHash(this.name)]=clearHash(this.value),$(this).on("change.foxy-dynamic-price",(function(){ADJUST[$(this).data("fc-adjust-for")].products[t].attributes[clearHash(this.name)]=clearHash(this.value),recalcTotal()})))}else if(this.value.search(pricemod_regex)>-1)switch($(this).data("fc-adjust-for",a),$(this).attr("type")){case"checkbox":$(this).is(":checked")?e.products[t].attributes[clearHash(this.name)]=clearHash(this.value):e.products[t].attributes[clearHash(this.name)]="",$(this).on("change.foxy-dynamic-price",(function(){$(this).is(":checked")?ADJUST[$(this).data("fc-adjust-for")].products[t].attributes[clearHash(this.name)]=clearHash(this.value):ADJUST[$(this).data("fc-adjust-for")].products[t].attributes[clearHash(this.name)]="",recalcTotal()}));break;case"radio":e.products[t].attributes.hasOwnProperty(clearHash(this.name))||(e.products[t].attributes[clearHash(this.name)]=""),$(this).is(":checked")&&(e.products[t].attributes[clearHash(this.name)]=clearHash(this.value)),$("[name='"+this.name+"']").data("fc-adjust-for",a).on("change.foxy-dynamic-price",(function(){ADJUST[$(this).data("fc-adjust-for")].products[t].attributes[clearHash(this.name)]=clearHash(this.value),recalcTotal()}))}}else $(this).data("fc-adjust-for",a).on("change.foxy-dynamic-price",(function(){var a=0;(["select","text"].indexOf(i)>-1||["radio","checkbox"].indexOf(i)>-1&&$(this).is(":checked"))&&(a=parseFloat(clearHash(this.value))),isNaN(a)&&(a=0),ADJUST[$(this).data("fc-adjust-for")].products[t].base_price=a,recalcTotal()}));else $(this).data("fc-adjust-for",a).on("change.foxy-dynamic-price",(function(){var a=0;(["select","text"].indexOf(i)>-1||["radio","checkbox"].indexOf(i)>-1&&$(this).is(":checked"))&&(a=parseFloat(clearHash(this.value))),isNaN(a)&&(a=0),ADJUST[$(this).data("fc-adjust-for")].products[t].quantity=a,recalcTotal()}))}})),ADJUST[a]=e)})),recalcTotal()}function clearHash(t){return t.replace(/\|\|[\d\w]+(?:\|\|open)?$/,"")}function getNameParts(t){return(t=clearHash(t)).match(/(?:(\d+):)?(.*)/)}function getId(t){var a=getNameParts(t);return id_regex.test(this.name)&&(prefix=parseInt(this.name.match(id_regex)[0])),void 0===a[1]?0:parseInt(a[1])}function getName(t){return getNameParts(t)[2]}function getElementType(t){if("SELECT"==t[0].tagName)return"select";if("INPUT"==t[0].tagName)switch(t.attr("type").toLowerCase()){case"text":case"number":case"tel":return"text";default:return t.attr("type").toLowerCase()}}function recalcTotal(){for(f in ADJUST){var t=0,e=0;for(p in ADJUST[f].products){var r=ADJUST[f].products[p].base_price,i=0;for(a in ADJUST[f].products[p].attributes){var s=ADJUST[f].products[p].attributes[a].match(pricemod_regex);if(s)switch(s[1]){case":":r=parseFloat(s[2]);break;case"+":i+=parseFloat(s[2]);break;case"-":i-=parseFloat(s[2])}}r+=i,t+=r*=ADJUST[f].products[p].quantity,e+=ADJUST[f].products[p].quantity}"function"==typeof fcFormatPrice&&(t=fcFormatPrice(t,f)),"function"==typeof fcFormatQuantity&&(e=fcFormatQuantity(e,f)),t=adjustDisplayPrice(t);var c="object"==typeof FC&&FC.hasOwnProperty("json")&&FC.json.config.hasOwnProperty("currency_format")?jQuery.trim(FC.util.money_format(FC.json.config.currency_format,t)):t.formatMoney(2);$("."+f+"_total").html(c),$("."+f+"_total_quantity").html(e)}}function adjustDisplayPrice(t){return"function"!=typeof fc_adjust_price_display||(adjusted_price=fc_adjust_price_display(t),void 0===adjusted_price||isNaN(parseFloat(adjusted_price)))?t:parseFloat(adjusted_price)}FC.onLoad=function(){FC.client.on("ready.done",initDynamicPrice)},Number.prototype.formatMoney=function(t,a,e){var r=this,i=(t=isNaN(t=Math.abs(t))?2:t,a=null==a?".":a,e=null==e?",":e,r<0?"-":""),s=parseInt(r=Math.abs(+r||0).toFixed(t))+"",c=(c=s.length)>3?c%3:0;return i+(c?s.substr(0,c)+e:"")+s.substr(c).replace(/(\d{3})(?=\d)/g,"$1"+e)+(t?a+Math.abs(r-s).toFixed(t).slice(2):"")};
-this.value.search(pricemod_regex)&&(b=!0)});b&&($(this).data("fc-adjust-for",e),c[clearHash(this.name)]=clearHash(this.value),$(this).on("change",function(){ADJUST[$(this).data("fc-adjust-for")].attributes[clearHash(this.name)]=clearHash(this.value);recalcTotal()}))}else if(-1<this.value.search(pricemod_regex))switch($(this).data("fc-adjust-for",e),$(this).attr("type")){case "checkbox":$(this).is(":checked")?c[clearHash(this.name)]=clearHash(this.value):c[clearHash(this.name)]="";$(this).on("change", +
-function(){$(this).is(":checked")?ADJUST[$(this).data("fc-adjust-for")].attributes[clearHash(this.name)]=clearHash(this.value):ADJUST[$(this).data("fc-adjust-for")].attributes[clearHash(this.name)]="";recalcTotal()});break;case "radio":c.hasOwnProperty(clearHash(this.name))||(c[clearHash(this.name)]=""),$(this).is(":checked")&&(c[clearHash(this.name)]=clearHash(this.value)),$("[name='"+this.name+"']").data("fc-adjust-for",e).on("change",function(){ADJUST[$(this).data("fc-adjust-for")].attributes[clearHash(this.name)]= +
-clearHash(this.value);recalcTotal()})}});b.attributes=c;0<d.length&&(f=parseFloat(clearHash(d.val())),isNaN(f)&&(f=0),b.quantity=f,-1<["text","number","tel"].indexOf(d.attr("type").toLowerCase())&&(g=!0,d.data("fc-adjust-for",e).on("change",function(){var b=parseFloat(clearHash(this.value));isNaN(b)&&(b=0);ADJUST[$(this).data("fc-adjust-for")].quantity=b;recalcTotal()})));if(!$.isEmptyObject(c)||g)ADJUST[e]=b}});recalcTotal()})}; +
-function clearHash(b){return b.replace(/\|\|[\d\w]+(?:\|\|open)?$/,"")} +
-function recalcTotal(){for(p in ADJUST){var b=ADJUST[p].base_price,c=0;for(a in ADJUST[p].attributes){var d=ADJUST[p].attributes[a].match(pricemod_regex);if(d)switch(d[1]){case ":":b=parseFloat(d[2]);break;case "+":c+=parseFloat(d[2]);break;case "-":c-=parseFloat(d[2])}}b+=c;b*=ADJUST[p].quantity;b="object"==typeof FC&&FC.hasOwnProperty("json")&&FC.json.config.hasOwnProperty("currency_format")?jQuery.trim(FC.util.money_format(FC.json.config.currency_format,b)):b.formatMoney(2);$("."+p+"_total").html(b)}} +
-Number.prototype.formatMoney=function(b,c,d){var g=this;b=isNaN(b=Math.abs(b))?2:b;c=void 0==c?".":c;d=void 0==d?",":d;var f=0>g?"-":"",e=parseInt(g=Math.abs(+g||0).toFixed(b))+"",h=3<(h=e.length)?h%3:0;return f+(h?e.substr(0,h)+d:"")+e.substr(h).replace(/(\d{3})(?=\d)/g,"$1"+d)+(b?c+Math.abs(g-e).toFixed(b).slice(2):"")};+
 </script> </script>
 </code> </code>
Line 24: Line 18:
  
 Setup your forms like so: Setup your forms like so:
-  *  Every FoxyCart based product that you want to dynamically calculate the total needs to have a unique code set like: +==== Product Code ==== 
-<code html> <input type="hidden" name="code" value="p1" /> </code> +Every FoxyCart based product that you want to dynamically calculate the total needs to have a unique code set like this
-    * Set an element to be where the total cost is shown and updated, by combining the code and "_total" as its class, like: +<code html><input type="hidden" name="code" value="p1" /></code>
-<code html> <h4><span class="p1_total">$20.00</span></h4></code>+
  
 +The code from the first product in your form will be used as a key for using in the element that dynamically shows the total product price.
  
 +If you're not able to know what product code is first, or you just want to set a specific code to use for the price modifier, you can also add a data attribute to your form like this: 
 +
 +<code html><form ... data-fc-form-code="p1"></code>
 +
 +==== Total Price Element ====
 +
 +Next, set an element to be where the total cost is shown and updated, by combining the code from above and "_total" as its class, like this:
 +<code html><h4 class="p1_total">$20.00</h4></code>
 +
 +If you'd like to show the total quantity, you can also do that using an element with a class that is a combination of the code and "_total_quantity" as its class, like:
 +<code html><p class="p1_total_quantity">0</p></code>
 +
 +==== Dynamically triggering recalculation ====
 +
 +If for whatever reason you need to trigger the dynamic price calculations to be recreated - for example if you dynamically add in a new input that includes a price modifier, you can execute this function in your javascript:
 +
 +<code javascript>initDynamicPrice();</code>
 +
 +==== Modify dynamically calculated total price ====
 +
 +If you need to dynamically modify the calculated price, for example if your product prices are tax exclusive, but you want to show the calculated price as tax inclusive, you can define a function on your page to achieve that result. The function has a name of ''fc_adjust_price_display'', and is passed a single argument which is the dynamically calculated total price. The function should return your modified price.
 +
 +As an example, let's say you want to account for a 25% inclusive tax on your product prices, that would look like this:
 +
 +<code javascript><script>
 +  function fc_adjust_price_display(price) {
 +    return price * 1.25;
 +  }
 +</script></code>
 ===== That's it! ===== ===== That's it! =====
  
-That should be it. The script basically runs through all the inputs within FoxyCart add to cart forms on the page, and grabs what the active value is. If that value has a price modifier (p:X, p+X, p-X), it chucks it into an array. After looping over all the form elements, it loops through all the selected price modifiers, adjusts the base price, multiplies it by the quantity and sets the total element.+That should be it. The script basically runs through all the inputs within FoxyCart add to cart forms on the page, and grabs what the active value is. If that value has a price modifier (''p:X''''p+X''''p-X''), it chucks it into an array. After looping over all the form elements, it loops through all the selected price modifiers, adjusts the base price, multiplies it by the quantity and sets the total element.
  
 ===== Notes and Limitations ===== ===== Notes and Limitations =====
  
-  * Currently this snippet does not support forms with multiple products defined within it. 
   * The snippet is run after the FoxyCart javascript has been initialized. As such - there may be a small time while the page is loading before the dynamic total is calculated.   * The snippet is run after the FoxyCart javascript has been initialized. As such - there may be a small time while the page is loading before the dynamic total is calculated.

Site Tools