Dynamic flat rate shipping based on product weight (and product weight added to cart JSON)

lukeluke FoxyCart Team
in General edited December 2008
Hey all. I was helping a friend with a site today and ended up adding a few fields to the cart JSON in 0.4.0, namely weight_each, weight, and total_weight. We don't normally make changes to existing store versions, but this change shouldn't affect anyone as far as we could tell.

Having the weight as part of the JSON cart makes dynamic flat rate shipping based on product weight possible like so:
var productWeightToWeightPerPound = {
	"50":1.15,
	"100":1,
	"150":.8,
	"200":.7,
	"250":.65,
	"300":.6,
	"350":.55,
	"400":.5,
	"750":.45,
	"1000":.4,
	"1500":.35
}


function calculateShipping(productWeight) {
	var pricePerPound = 1.25;
	var weightArray = new Array();
	var pricePerPoundArray = new Array();
	for (var priceTier in productWeightToWeightPerPound) {
		weightArray[weightArray.length] = parseInt(priceTier);
		pricePerPoundArray[pricePerPoundArray.length] = productWeightToWeightPerPound[priceTier];
	}
	// sort ascending
	weightArray = weightArray.sort(function(a,b){return a - b});
	// sort descending
	pricePerPoundArray = pricePerPoundArray.sort(function(a,b){return b - a});
	for (var i=0; i<weightArray.length; i++) {
		if (weightArray[i] <= productWeight) {
			pricePerPound = pricePerPoundArray[i];
		} else {
			break;
		}
	}
	return (pricePerPound * productWeight);
}

$j(document).ready(function() {
	FC.checkout.override("updatePrice","myUpdatePrice");
	FC.checkout.config.orderShipping = calculateShipping(fc_json.total_weight);
	FC.checkout.updatePriceDisplay();
});

var myUpdatePrice = function() {
	FC.checkout.updatePaymentDisplay();
}

There's some undocumented stuff going on here such as the FC.checkout.override method which allows you to bypass standard FoxyCart functions and roll your own. Eventually we'll document and organize the checkout javascript but until then here's a teaser of what's possible. Enjoy.
Comments
  • Hi Luke, this sounds interesting. And while I am not a JS expert, I get the gist of what this is doign. I am working on a site now that has weight and destination as the modifiers for shipping. Could the code above be modified to address an array of zip codes (matching the first 3 numbers of a zip code) to dynamically update the shipping cost as well? All items are shipping from one single distribution hub.
  • lukeluke FoxyCart Team
    Yeah, I don't see why not. I did something similar for http://modernash.com/ but in that case it was done directly on the cart because of the dynamic nature of their calculation process for their direct delivery charges. If you do a view source on the cart frame, you should get some other ideas (though they are rather complex). Feel free to post here as you go along with any questions you have.
  • OK cool, well some new information has surfaced and they have a specific Zipcode matrix which is multiplied into the weight and another variable to generate a shipping cost. They are providing the matrix, the weights, the variable code AND their formula for generating the shipping cost. My question is this, what would be the best way to integrate this calculation to provide the most secure solution? I have thought of either a JS that would do the calculation OR a separate dB that will take care of it and pass it in to the cart. But in terms of where this should happen, do you have some guidance? We will have the variable code and the weight passed in with product info, but the zip code will have to wait until they've entered that in on checkout. Or would you suggest doing the shipping calculation before they even hit the cart (like a popup that says, enter your zip code for shipping).

    The reason the security is an issue, is that shipping could be upwards of a $1000 on some items.

    TIA
    Chuck
  • lukeluke FoxyCart Team
    Hey Chuck. Wow, with shipping at those levels, you're probably going to need something build in directly. Please vote for "Super Ship" here: http://requests.foxycart.com

    That being said... though spoofing could happen, most legitimate customers would never attempt something like that while still giving their actual account and contact information. Also, if you can verify the order via the XML Datafeed after the fact, you should be able to confirm the shipping costs were collected before sending the product.

    Did you get a chance to look at the code on the modernash cart? They are doing just about what you're describing except they are doing shipping based on the product price total, not the weight (while linking to a zipcode matrix which they collect on the cart). Doing something like that might work.

    Though you could probably just do it all on the checkout as I described in the initial post. Whenever the zipcode changes, the "updateShipping" method is called which also calls the updatePrice method (which is being overwritten above). So if you added to the code I originally posted to get the value for the zipcode then used that in your matrix to find a price, you should be good to go. If you're not using multiship, you can get the zipcode like this:
    var postal_code = ($j("#use_different_addresses").is(":checked") ? $j("#shipping_postal_code").val() : $j("#customer_postal_code").val());
    

    I hope that makes sense. If not, post back and someone interesting in doing some work for hire might contact you about this.
  • Thanks for the info Luke. I already voted! I'm hoping you guys can get SuperShop rolling soon :)

    I am waiting for the info form the client and am supposed to have it by Wednesday. I will post more when I get more info. Tnx

    Chuck
  • Hey Luke, thanks for providing the code for price tier based on how much the client is buying. In my case I needed a way to have a flat fee based on how much the client was paying. So if they spent under $100, they pay X amount, between 100 and 200, X amount, etc. Here is the code Luke provided me:
    function calculateShipping(amount) {
    	if (amount >= 100) {
    		if (amount >= 200) {
    			return 20;
    		} else {
    			return 15;
    		}
    	} else {
    		return 10;
    	}
    }
    
    $j(document).ready(function() {
    	FC.checkout.override("updatePrice","myUpdatePrice");
    	FC.checkout.config.orderShipping = calculateShipping(fc_json.total_price);
    	FC.checkout.updatePriceDisplay();
    });
    
    var myUpdatePrice = function() {
    	FC.checkout.updatePaymentDisplay();
    }
    

    Thanks again for providing that for me Luke!
  • Okay, I'm in way over my head. I don't know anything about JS, but it appears to be relatively simple to override the shipping cost, if I'm understanding this post correctly.

    I need to set the shipping cost based on total purchase price: If you buy $5 or less, you pay $3 for shipping. If you buy between $5 and $15, you pay $6 for shipping, etc. (At the high end, if you spend anything over $200, you pay $40.)

    I have two questions.

    First, is the code below correctly structured?

    Second, where do I stick it? I have no idea where it's supposed to go. Reading through the docs hasn't resolved this, yet... because I don't have a clue what I'm doing.

    Help?

    Thanks,
    Taz

    +++

    function calculateShipping(amount) {
    if (amount <= 5) {
    return 3;
    } else if (amount <= 15){
    return 6;
    } else if (amount <= 30){
    return 9;
    } else if (amount <= 50){
    return 16;
    } else if (amount <= 75){
    return 23;
    } else if (amount <= 100){
    return 28;
    } else if (amount <= 200){
    return 32;
    } else {
    return 40;
    }

    $j(document).ready(function() {
    FC.checkout.override("updatePrice","myUpdatePrice");
    FC.checkout.config.orderShipping = calculateShipping(fc_json.total_price);
    FC.checkout.updatePriceDisplay();
    });

    var myUpdatePrice = function() {
    FC.checkout.updatePaymentDisplay();
    }
  • lukeluke FoxyCart Team
    Hey Taz. You probably don't want to venture in this direction if you don't have a solid grasp on what's going on. The code you're copying from is over 9 months old and may not work with our latest version (or future versions). What you're looking for is "SuperShip" which we haven't build yet: http://requests.foxycart.com/pages/4162-general-requests/suggestions/33314-super-ship?ref=title

    That's probably not what you want to hear, but we can't continue to support these hacks with any level of certainty, especially if you're not confident on what the changes are actually doing to your checkout flow. If I remember right, this code may also break the tax system in 051.
  • peredurperedur Member
    edited September 2009
    Well, I don't need taxes. And I do need this. If I have to pay someone to make it work, I'll do that. I told the client he could have this, based on this discussion, and am supposed to be finishing up a site rebuild. So I'm out about $2000 if I can't deliver, and will have one very pissed client.

    Update: Got it working. (I was missing a closing brace, and evidently needed the "-1" from here ) All good now.
  • ...except: Now I need to filter based on country, as per this thread: http://forum.foxycart.com/comments.php?DiscussionID=324

    Specifically as per Brett's note:
    The if/then/else logic would be handled with regular javascript (not jQuery), but it should certainly work (at least theoretically).

    What you'd have to do is add an onchange event to the country field and go from there. Actually, you'd add the check for the country before you did anything (since the country is auto-populated based on a geoIP database, so it might not start out as Canada), then recheck onchange. The only thing that might be odd is the autocomplete code on the country field, but I don't think it'd create any problems.

    If you need help with the js, let me know.

    And..um...I do need help with the js.

    What I'm trying to accomplish is 3 options: US = one set of rates; Canada = another set; Elsewhere = 0 (shipping to be billed separately). And I'm not sure how to get the onchange to fire. I'm guessing that once I achieve that I can trigger the calculateShipping stuff...?
  • brettbrett FoxyCart Team
    Hi peredur.
    I'll whisper you a contact that would be able to help you pretty quickly. We'd really love to be able to tackle this stuff, but it's just not something we can do in a "healthy" way at this point. But I'll put you in touch with somebody that can help.
  • just now found that whisper...thanks!
Sign In or Register to comment.