Multi-ship w/ Multiple Flat Rate Shipping Modification

@fc_adam,

In testing multiship on our store, we have noticed that the FC 2.0 shipping modification for multiple flat rates is calculating shipping rates based on the whole order, rather than on each shipment individually. In cases where a customer ships to multiple addresses, we want the shipping rates to be calculated and applied for each shipment. I searched the forums and found an older post that appears to provide a script modification to accommodate this, but it is from 2013 before the flat rate shipping modification was updated with FoxyCart 2.0. Here is a link to the mentioned post and the modified script referenced in it:

https://forum.foxycart.com/discussion/7850/multi-ship-multiple-shipping-rates
http://pastie.org/private/av8xfylnx5hsojnls8wcw#13,16

Will this work for FoxyCart 2.0, or will it require another updated modification?

An example of what is currently happening:

Our custom shipping logic uses FC.json.total_item_price as the primary condition. Because we have free shipping to US on orders of $38+, we get this result

Ship to : Gift Address (USA)
-- Items total : $23.00
-- Shipping cost: $0.00

Ship to : Me (USA)
-- Items total : $30.00
-- Shipping cost: $0.00

But that free shipping threshold should be on each shipment rather than the total order, so we want this:

Ship to : Gift Address (USA)
-- Items total : $23.00
-- Shipping cost: $5.00

Ship to : Me (USA)
-- Items total : $30.00
-- Shipping cost: $5.00

Please let me know how we can best approach this. Thanks!
Comments
  • fc_adamfc_adam FoxyCart Team
    @Geoffrey,

    The flat rates snippet for 2.0 does support multiship already, but you'll need to alter your logic so that it only deals with variables that are localised to the address.

    If you check the address parameter that is passed to the logic function - that should provide information on which shipto address it's for within address.address_name. You'll then need to loop through the cart to work out which products relate to that shipto address and calculate shipping accordingly.
  • @fc_adam,

    I've made a few attempts today to figure this out, but I don't think I understand how to do it right. A simplified example of our current logic looks like this:


    if (address.country == "US" || address.country == "CA"){
    FC.customFlatRates.add(100, 5, 'USPS', ' (7-10 business days)');

    if (FC.json.total_item_price >= 38) {
    FC.customFlatRates.update(100, 0);
    }
    }


    I am trying to use the address.address_name as a condition, and loop through the FC.json.multiship_data array to get the multiship_data.total_item_price for each shipment to use that in place of the FC.json.total_item_price, but I can't get it to work. I think I'm lost on how to correctly set up the loop to get the multiship_data.total_item_price from the multiship_data array, and modify the logic so that it runs on each separate shipment rather than both of them together. If you could provide me with some example code on how to go about constructing this properly, I would greatly appreciate it.

    Also, how would I preserve the original logic to run on the FC.json.total_item_price in cases where a customer has not used multiship? I've set things up so that the shipto value is null by default, so that multiship only engages when a customer opts to ship to a gift address.

    Thank you for all your help!
  • fc_adamfc_adam FoxyCart Team
    @Geoffrey,

    Sure thing. Give this a try:
    if (address.country == "US" || address.country == "CA"){
    FC.customFlatRates.add(100, 5, 'USPS', ' (7-10 business days)');

    var total_item_price = FC.json.total_item_price;

    if (FC.json.has_multiship) {
    for (var i = 0; i < FC.json.multiship_data.length; i++) {
    if (FC.json.multiship_data[i].address_name == address.address_name) {
    total_item_price = FC.json.multiship_data[i].total_item_price;
    }
    };
    }

    if (total_item_price >= 38) {
    FC.customFlatRates.update(100, 0);
    }
    }
  • GeoffreyGeoffrey Member
    edited December 2015
    @fc_adam,

    Thanks for the additional guidance, but it's still not working for me. The script appears to be continuing to use the default variable value of total_item_price = FC.json.total_item_price, and calculating the shipping fee for both shipments by the total item price of the whole order. I've copied our full logic below, and how I'm including your code. Am I still missing something or doing this wrong?


    /* BEGIN CUSTOM SHIPPING LOGIC */

    var hasDefault = false;
    var has5FOR5 = false;
    var hasTeaware = false;
    var hasSUB = false;
    var hasSale = false;
    for (p in FC.json.items) {
    switch (FC.json.items[p].category) {
    case "DEFAULT":
    hasDefault = true;
    break;
    case "SALE":
    hasSale = true;
    break;
    case "5FOR5S":
    has5FOR5 = true;
    break;
    case "SUBSCRIPTION":
    hasSUB = true;
    break;
    case "TEAWARE":
    hasTeaware = true;
    break;
    }
    }

    var hasSpecial = false;
    for (var i in FC.json.items) {
    for (var o = 0; o < FC.json.items[i].options.length; o++) {
    if (FC.json.items[i].options[o].name == "status") {
    hasSpecial = true;
    }
    }
    }


    /* FIX FOR MULTISHIP */

    var total_item_price = FC.json.total_item_price;

    if (FC.json.has_multiship) {
    for (var i = 0; i < FC.json.multiship_data.length; i++) {
    if (FC.json.multiship_data[i].address_name == address.address_name) {
    total_item_price = FC.json.multiship_data[i].total_item_price;
    }
    }
    }

    /* ----------------- */

    if (address.country == "US" || address.country == "CA"){
    FC.customFlatRates.add(100, 5, 'USPS', ' (7-10 business days)');

    if (has5FOR5 && !hasDefault && !hasSpecial && !hasSUB && !hasSale) {
    FC.customFlatRates.update(100, 0);
    } else if (hasSUB && !hasDefault && !hasSpecial && !has5FOR5 && !hasSale) {
    FC.customFlatRates.update(100, 0);
    } else if (!hasSpecial && total_item_price >= 38) {
    FC.customFlatRates.update(100, 0);
    } else if (hasSpecial && total_item_price >= 38) {
    FC.customFlatRates.update(100, 0, 'USPS', ' (10-17 business days)');
    jQuery('#cart-notice').remove();
    jQuery('.fc-cart__main__content').append('<span id="cart-notice">NOTE – Your cart contains special order items:<br> This order may take up to 7 additional business days to ship. Estimated delivery time will be adjusted below.</span>');
    } else if (hasSpecial && total_item_price < 38) {
    FC.customFlatRates.update(100, 5, 'USPS', ' (10-17 business days)');
    jQuery('#cart-notice').remove();
    jQuery('.fc-cart__main__content').append('<span id="cart-notice">NOTE – Your cart contains special order items:<br> This order may take up to 7 additional business days to ship. Estimated delivery time will be adjusted below.</span>');
    }

    } else if (address.country == "CN" || address.country == "HK" || address.country == "TW") {
    FC.customFlatRates.add(102, 5, 'China Post', ' (2-4 business days)');

    if (has5FOR5 && !hasDefault && !hasSpecial && !hasSUB && !hasSale) {
    FC.customFlatRates.update(102, 0);
    } else if (hasSUB && !hasDefault && !hasSpecial && !has5FOR5 && !hasSale) {
    FC.customFlatRates.update(102, 0);
    } else if (!hasSpecial && total_item_price >= 38) {
    FC.customFlatRates.update(102, 0);
    } else if (hasSpecial && total_item_price >= 38) {
    FC.customFlatRates.update(102, 0, 'China Post', ' (7-12 business days)');
    jQuery('#cart-notice').remove();
    jQuery('.fc-cart__main__content').append('<span id="cart-notice">NOTE – Your cart contains special order items:<br> This order may take up to 7 additional business days to ship. Estimated delivery time will be adjusted below.</span>');
    } else if (hasSpecial && total_item_price < 38) {
    FC.customFlatRates.update(102, 5, 'China Post', ' (7-12 business days)');
    jQuery('#cart-notice').remove();
    jQuery('.fc-cart__main__content').append('<span id="cart-notice">NOTE – Your cart contains special order items:<br> This order may take up to 7 additional business days to ship. Estimated delivery time will be adjusted below.</span>');
    }

    } else if (address.country == "AU" || address.country == "GB") {
    FC.customFlatRates.add(106, 5, 'China Post', ' (7-10 business days)');

    if (has5FOR5 && !hasDefault && !hasSpecial && !hasSUB && !hasSale) {
    FC.customFlatRates.update(106, 0);
    } else if (hasSUB && !hasDefault && !hasSpecial && !has5FOR5 && !hasSale) {
    FC.customFlatRates.update(106, 0);
    } else if (!hasSpecial && total_item_price >= 38) {
    FC.customFlatRates.update(106, 0);
    } else if (hasSpecial && total_item_price >= 38) {
    FC.customFlatRates.update(106, 0, 'China Post', ' (10-17 business days)');
    jQuery('#cart-notice').remove();
    jQuery('.fc-cart__main__content').append('<span id="cart-notice">NOTE – Your cart contains special order items:<br> This order may take up to 7 additional business days to ship. Estimated delivery time will be adjusted below.</span>');
    } else if (hasSpecial && total_item_price < 38) {
    FC.customFlatRates.update(106, 5, 'China Post', ' (10-17 business days)');
    jQuery('#cart-notice').remove();
    jQuery('.fc-cart__main__content').append('<span id="cart-notice">NOTE – Your cart contains special order items:<br> This order may take up to 7 additional business days to ship. Estimated delivery time will be adjusted below.</span>');
    }

    } else {
    FC.customFlatRates.add(107, 18, 'International Shipping', ' (10-15 business days)');
    if (!hasSpecial && total_item_price >= 100) {
    FC.customFlatRates.update(107, 0);
    } else if (hasSpecial && total_item_price >= 100) {
    FC.customFlatRates.update(107, 0, 'International Shipping', ' (17-20 business days)');
    jQuery('#cart-notice').remove();
    jQuery('.fc-cart__main__content').append('<span id="cart-notice">NOTE – Your cart contains special order items:<br> This order may take up to 7 additional business days to ship. Estimated delivery time will be adjusted below.</span>');
    } else if (hasSpecial && total_item_price < 100) {
    FC.customFlatRates.update(107, 18, 'International Shipping', ' (17-20 business days)');
    jQuery('#cart-notice').remove();
    jQuery('.fc-cart__main__content').append('<span id="cart-notice">NOTE – Your cart contains special order items:<br> This order may take up to 7 additional business days to ship. Estimated delivery time will be adjusted below.</span>');
    }

    }

  • GeoffreyGeoffrey Member
    edited December 2015
    Also, I had tried including your code within the context of each address.country condition, like you had structured it, but that didn't work either.

    I additionally tried to see if I could explicitly define the address.address_name in the condition to produce a different result between the two shipments, like so:

    if (FC.json.multiship_data[i].address_name == "Gift Address") {...}

    But this also did not work.
  • fc_adamfc_adam FoxyCart Team
    @Geoffrey,

    I tested out the code I gave you and it's working for me - calculating based on the total item price of each individual shipto.

    You can confirm what it's doing by adding a console log statement right after the "fix for multiship" block you added like this:

    console.log(address.address_name + " total item price of " + total_item_price);

    That will output to the console what it's calculating the total item price is. For me that's coming out as it should.

    I see you're also doing a few other conditionals based on the cart like hasSpecial and has5for5. These currently are based on the entire cart - not on each individual shipto as well. You need to alter the logic for setting those slightly like this:
        var hasDefault = false;
    var has5FOR5 = false;
    var hasTeaware = false;
    var hasSUB = false;
    var hasSale = false;
    for (p in FC.json.items) {
    if (FC.json.items[p].shipto == address.address_name) {
    switch (FC.json.items[p].category) {
    case "DEFAULT":
    hasDefault = true;
    break;
    case "SALE":
    hasSale = true;
    break;
    case "5FOR5S":
    has5FOR5 = true;
    break;
    case "SUBSCRIPTION":
    hasSUB = true;
    break;
    case "TEAWARE":
    hasTeaware = true;
    break;
    }
    }
    }
    That will ensure it only sets it to true if that product is from the current shipto address.
  • GeoffreyGeoffrey Member
    edited December 2015
    @fc_adam,

    I have now tried multiple variations on your suggestions, but am still having problems getting this to work as intended. This morning, I thought it would be helpful to simply remove all our other conditions and checkout scripts to see if this logic is working in its most basic form. Here's what I've found...

    After reducing our checkout scripts to only the custom flat rates modification, and using exactly the logic you suggested on 12/10, as follows:


    /* BEGIN CUSTOM SHIPPING LOGIC */

    if (address.country == "US" || address.country == "CA"){
    FC.customFlatRates.add(100, 5, 'USPS', ' (7-10 business days)');

    var total_item_price = FC.json.total_item_price;

    if (FC.json.has_multiship) {
    for (var i = 0; i < FC.json.multiship_data.length; i++) {
    if (FC.json.multiship_data[i].address_name == address.address_name) {
    total_item_price = FC.json.multiship_data[i].total_item_price;
    }
    }
    }

    console.log(address.address_name + " total item price of " + total_item_price);

    if (total_item_price >= 38) {
    FC.customFlatRates.update(100, 0);
    }
    }

    /* END CUSTOM SHIPPING LOGIC */


    I load up the checkout with the following cart totals:

    SHIP TO: GIFT ADDRESS
    1 item, totaling = $12.00

    SHIP TO: ME
    6 items, totaling = $40.50

    Cart Subtotal = $52.50

    Now, I have a customer account that I sign into with pre-populated addresses for both Gift Address and Me, and the console log shows the following:

    Gift Address total item price of 12
    Me total item price of 40.5


    But what happens in the checkout under each address is that I get the shipping options results as:

    Shipping methods for Gift Address
    USPS (7-10 business days) $0.00

    Shipping methods for Me
    USPS (7-10 business days) $0.00

    Further, when I adjust the quantity of items under the SHIP TO: ME to an amount that would be below the free shipping threshold (5 items, totaling = $33.75), the console log updates the values for total item price two times, like this:

    Gift Address total item price of 45.75
    Me total item price of 45.75
    Gift Address total item price of 45.75
    Me total item price of 45.75


    The value of $45.75 is actually the new subtotal of the entire order after updating the quantity. The provided shipping options for each address remain the same $0.00. Here it appears that the script is now actually drawing upon the total item price of the whole order to calculate shipping totals. This is confirmed when I update the the quantity under SHIP TO: ME again to a total of 1 item at $6.75, reducing the whole order subtotal $18.75, which results in the shipping options provided for each address changing to $5.00. Again total item price shown for each address in the console log shows $18.75, and is run twice.

    Additionally, if I reset the checkout to my original cart totals, refresh, and go through filling out the form without a customer account, here is what happens...

    1. Fill out first address, SHIP TO: GIFT ADDRESS
    2. Console log displays: Gift Address total item price of 12
    3. Shipping methods for Gift Address = USPS (7-10 business days) $5.00
    ...so far so good...
    4. Fill out second address, SHIP TO: ME
    5. Console log displays: Me total item price of 40.5
    6. Shipping methods for Me = USPS (7-10 business days) $0.00
    ...great!, but...
    7. Update qty and total in SHIP TO: ME to 5 items at $33.75
    8. Console log displays total item price for both addresses as same value as subtotal for whole order ($45.75)
    9. Shipping methods for both addresses = USPS (7-10 business days) $0.00

    I tested one more scenario where a customer account had a saved address under SHIP TO: GIFT ADDRESS that pre-populated, but did not have an address pre-populated for SHIP TO: ME. The result was the same as the example above where I filled out the form without a customer account: everything worked as intended until qty values are adjusted and it reverts to using the order subtotal as the total item price for each address.

    After running these tests, I put all our original checkout scrips back in, including the logic modifications that you suggested to accommodate my other conditions, and everything seems to work as it should except in the two scenarios I have described above:

    1. Where a customer account login pre-populates both SHIP TO addresses.
    2. Where a customer adjusts item qty of any kind after shipping options have been displayed.

    These two problems are pretty significant. How can they be resolved? Thanks again for your continued help on this.
  • fc_adamfc_adam FoxyCart Team
    @Geoffrey,

    Thanks for the thorough notes. I think I can see the root issue, and a way forward. It will be a breaking change for the snippet, but will make it more robust for multiship stores. I'll follow up again as soon as I can finish off some testing.
  • fc_adamfc_adam FoxyCart Team
    @Geoffrey,

    Just to check - do you have a development store you are able to test this out on your side too before applying it to your live store?
  • @fc_adam,

    yes, of course. I will test everything on our DEV site before deploying to production. thanks for asking!
  • fc_adamfc_adam FoxyCart Team
    @Geoffrey,

    Thanks for confirming - I'll whisper you the code.
Sign In or Register to comment.