Announcement

Collapse
No announcement yet.

Free Inline Shipping Calculator with source code

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

    Free Inline Shipping Calculator with source code

    Free Inline Shipping Calculator with source code

    Hi Everyone,

    Well, we've benefitted a lot from reading this forum and the mailing list, so we thought we'd contribute a little mod we built for ourselves to get live shipping rate calculation on the Basket page, without having to install other modules that interfere with the GUI.

    Here's a picture. Instructions follow. You can see the mod in action at http://www.hollywoodcamerawork.us/
    Attached Files

    #2
    The vast majority of the work is done by the template code pasted below. The code is messy looking, but it gets the job done, and you can probably tweak it for your purposes.

    Basically, the code creates a "Calculate Shipping Rates" link, that when clicked, un-hides a Country/State/Zip selector with a "Get Rates" button. This code is mostly Miva template code with some Javascript. You should be able to Copy/Paste this code into the desired place in your Basket page. The entire thing is wrapped in a table and right-aligned.

    IMPORTANT: This template code requires the following Items to be enabled: "shippingmethods", "states" and "countries" (under the Items tab of the Basket page in Miva Admin).

    Code:
    <table align="right"><tr align="right"><td align="right">
    <br>
    <script language="javascript">
    // Various Shipping Calculator Functions
    function turnOnCalculator()
    {
      document.all.enablecalculator.style.display = 'none';
      document.all.shippingcalculator.style.display = 'block';
    }
    </script>
    <mvt:if expr="g.BasketCalculator EQ '1'">
      <div align="right" id="enablecalculator" style="display: none">
    <mvt:else>
      <div align="right" id="enablecalculator" style="display: block">
    </mvt:if>
    <mvt:item name="fonts" param="body_font">
    <a href="javascript:;" onClick="turnOnCalculator();">Calculate Shipping Rates</a>
    </mvt:item>
    </div>
    <mvt:if expr="g.BasketCalculator EQ '1'">
      <div align="right" id="shippingcalculator" style="display: block">
    <mvt:else>
      <div align="right" id="shippingcalculator" style="display: none">
    </mvt:if>
    <table>
    <tr><td valign="top">
    <table>
    <form name="custform" method="POST" action="&mvt:global:sessionurl;">
    <input type="hidden" name="SaveShipCountry" value="&mvt:global:saveshipcountry;">
    <input type="hidden" name="SaveBillCountry" value="&mvt:global:savebillcountry;">
    <mvt:if expr="g.BasketCalculator NE '1'">
      <input type="hidden" name="BasketCalculator" value="1">
    <mvt:else>
      <input type="hidden" name="BasketCalculator" value="&mvt:global:BasketCalculator;">
    </mvt:if>
    <input type="hidden" name="Action" value="CALC">
    <input type="hidden" name="Screen" value="BASK">
    <input type="hidden" name="Store_Code" value="&mvte:store:code;">
    <tr><td>
    <mvt:item name="fonts" param="body_font">
    <b>Country:</b>
    </mvt:item>
    </td><td>
    <mvt:item name="countries" param="ShipCountry" />
    </td></tr>
    <tr><td>
    <mvt:item name="fonts" param="body_font">
    <b>State (US Only):</b>
    </mvt:item>
    </td><td>
    <mvt:item name="states" param="ShipStateSelect" />
    </td></tr>
    <tr><td>
    <mvt:item name="fonts" param="body_font">
    <b>Zip/Postal Code:</b>
    </mvt:item>
    </td><td>
    <input type="text" name="ShipZip" size=12 maxlength=12 value="&mvte:global:ShipZip;">
    </td></tr>
    <tr><td>&nbsp;
    </td><td>
    <input type="submit" alt="Submit" name="submit" value="Get Rates"
         onClick='document.custform.submit.value = "Please Wait...";return true'>
    </td><tr>
    </form>
    </table>
    <mvt:if expr="g.BasketCalculator NE '1'">
    <script language="javascript">
       //document.custform.ShipCountry.value="US";
       //document.custform.ShipStateSelect.value="CA";
       //document.custform.ShipZip.value="";
    </script>
    </mvt:if>
    </td><td valign="top">
    <mvt:if expr="g.BasketCalculator EQ '1'">
    <script language="javascript">
         var arrayIndex = 0;
         var shipArray = new Array();
    function insertMethod(name, price)
    {
         shipArray[arrayIndex++] = new Array(name, price);
    }
    </script>
       <mvt:if expr="ISNULL l.settings:shippingmethods">
        Unable to Calculate Shipping Costs
       <mvt:else>
        <mvt:foreach array="shippingmethods" iterator="method">
                                            <mvt:if expr="ISNULL l.settings:method:price">
                                              <script language="javascript"> var thisPrice=0; </script>
         <mvt:else>
                                              <script language="javascript"> var thisPrice=&mvt:method:price;; </script>
                                            </mvt:if>
    <script language="javascript"> insertMethod( "&mvt:method:name;" , thisPrice); </script>
        </mvt:foreach>
       </mvt:if>
     
       <mvt:if expr="ISNULL l.settings:shippingmethods">
        <br>
        <b>No valid shipping methods are available for your order.</b>
       </mvt:if>
     
    <script language="javascript">
      var count;
      function priceCheck(a,b)
      {
        if (a[1] < b[1]) return -1;    // Checking prices
        if (a[1] > b[1]) return 1;
        return 0;  // Prices are equal
      }
      if (arrayIndex != 0)
      {
        shipArray.sort(priceCheck);
        document.write('&nbsp;&nbsp;&nbsp;</td><td valign="top">');
        document.write('<table border="1" cellspacing="0" cellpadding="2" bgcolor="#eeeeee">');
        document.write('<tr><td colspan="2" align="center">');
        document.write('<mvt:item name="fonts" param="body_font">');
        document.write('<b>Available Shipping Options:</b></mvt:item></td></tr>');
        for (count=0 ; count<arrayIndex ; count++)
        {
          document.write('<tr height="1"><td>');
          document.write('<mvt:item name="fonts" param="body_font">');
          document.write('<div align="right">&nbsp;');
          document.write(shipArray[count][0]);
          document.write(':&nbsp;');
          document.write('</div>');
          document.write('</mvt:item>');
          document.write('</td><td>');
          document.write('<mvt:item name="fonts" param="body_font">');
          document.write('<div align="right">');
          document.write('&nbsp;$');
          document.write(shipArray[count][1].toFixed(2));
          document.write('&nbsp;</div>');
          document.write('</mvt:item>');
          document.write('</td></tr>');
        }
        document.write('</table>');
      }
    </script>
    </mvt:if>
    </td></tr></table>
    </div>
    </td></tr></table>
    When the "Get Rates" button is clicked, it submits the form with the 'Screen' command 'BASK", which is the same page as you were on, and an 'Action' of 'CALC'. That submission is intercepted by the calcshipping.mvc module (included in the Zip file), which you have to install. We've also included the source, as you should rightly be concerned about security when installing someone else's .mvc file, so you can compile it yourself with the MVC.

    Almost the entire source file is just stubs for required system functions. The only thing that matters is this piece of code:

    Code:
    <MvFUNCTION NAME = "SystemModule_Action" PARAMETERS = "module var, action" STANDARDOUTPUTLEVEL = "">
     <MvIF EXPR="{g.Action EQ 'CALC' AND g.Screen EQ 'BASK'}">
      <MvASSIGN NAME="g.Basket:SHIP_STATE" VALUE="{ g.ShipStateSelect }">
      <MvASSIGN NAME="g.Basket:SHIP_ZIP" VALUE="{ g.ShipZip }">
      <MvASSIGN NAME="g.Basket:SHIP_CNTRY" VALUE="{ g.ShipCountry }">
     </MvIF>
     
     <MvFUNCRETURN VALUE="{1}">  
    </MvFUNCTION>
    This code takes the values from the Country/State/Zip fields in the form, and assigns them to the variables needed for internal shipping calculation. The shipping calculation is done when the Basket page template code is run again, which happens immediately after as the form submit had the Screen of BASK, which reloads the Basket page.

    I hope you will enjoy it. Please receive this module with all possible disclaimers. If you trash your store, it's completely your own responsibility. Install it in a test store first.

    Best,

    Per Holmes
    Hollywood Camera Work
    Attached Files
    Last edited by perholmes; 10-26-06, 12:45 AM.

    Comment


      #3
      By the way, some of the code handles the fact that the shipping rates in Miva's internal variables are in random order, so they have to be sorted, which usually happens automatically in the drop-down-list-control on the shipping page.

      In order to get them sorted here, they're first loaded into a Javascript two-dimensional array, then sorted, and then the list table is printed.

      Per

      Comment


        #4
        DON'T INSTALL IT YET -- WE JUST TESTED IT ON A COMPUTER THAT HADN'T USED THE STORE BEFORE, AND THERE'S A BUG (NOT DANGEROUS BUT MAJOR COSMETIC PROBLEM).

        Comment


          #5
          UPDATE: There does not appear to be a bug after all -- instead, one of our other modules is written in a dumb way. If you have a test store, please by all means try the code.

          Comment


            #6
            OK, no bug for sure. However, I have a question for the experts on the forum:

            When exactly does MM go out and get the shipping rates? As far as I can tell, it happens simply when the 'shippingoptions' item is enabled for a page, because even on a first page-load of the Basket page (with wiped cookies and history), there appears to be a lag, which I can only attribute to shipping rates being fetched. This is also what caused trouble in our other module, because it never expected to be run without customer information having been entered.

            I can not find any tags in the original Basket template code (which our code is based on), which causes a sort of Render_Start, which would fetch the shipping rates. It appears that they're being fetched at every page-load of a page where the 'shippingoptions' Item is enabled.

            Can this be true?

            In that case, the way to make this Shipping Calculator work perfectly would be to have two Basket pages, first BASK (the original), and then a BAS2, which the 'shippingoptions' Item enabled, so they only get fetched from FedEx/USPS/UPS etc. when they're actually needed.

            Best,

            Per

            Comment


              #7
              OK, and here's the answer to that too:

              Miva fetches shipping rates whenever the 'shippingmethods' Item is enabled for a page. That means that if you only have one Basket page, and enable 'shippingmethods' on it, Miva will waste time trying to fetch shipping rates, even if no address has been entered, and this will happen every time a user visits the View Basket page -- very undesirable.

              So you should do this instead. After you've built the Basket (BASK) page the way you like it, you should create another page called BAS2, which is an exact copy of BASK. Everything should be the same, same enabled Items, copy paste all the code for each tab that doesn't have an (*) asterisk.

              BUT: On the BASK page, disable 'shippingmethods'. And do the following: Change

              <input type="hidden" name="Screen" value="BASK">

              to

              <input type="hidden" name="Screen" value="BAS2">

              Make sure to make this change on both pages, otherwise people will bounce back and forth between BASK and BAS2.

              This way, people will get the regular basket page when they open, but have the option to flip open the shipping calculator and enter Country/State/Zip. Then, when they submit, they're redirected to BAS2, which is identical, except that 'shippingmethods' is enabled. From here, they can keep entering different Country/State/Zips and get new rates, because they keep getting redirected back to BAS2.

              One thing, I had to recompile the code for the module that you have to install. If you look at the code:

              Code:
              
              <MvFUNCTION NAME = "SystemModule_Action" PARAMETERS = "module var, action" STANDARDOUTPUTLEVEL = "">
               
              <MvIF EXPR="{g.Action EQ 'CALC' AND ( g.Screen EQ 'BASK' OR g.Screen EQ 'BAS2' ) }">
              <MvASSIGN NAME="g.Basket:SHIP_STATE" VALUE="{ g.ShipStateSelect }">
              <MvASSIGN NAME="g.Basket:SHIP_ZIP" VALUE="{ g.ShipZip }">
              <MvASSIGN NAME="g.Basket:SHIP_CNTRY" VALUE="{ g.ShipCountry }">
              </MvIF>
               
              <MvFUNCRETURN VALUE="{1}"> 
              </MvFUNCTION>
              

              This had to be changed so the behind-the-scenes copying into the right variables will happen on both BASK and BAS2. And obviously, you have to name the pages exactly like this.

              Zip file with .mv and .mvc attached.

              Per
              Attached Files

              Comment


                #8
                Why not write your module so it does not call the shipping options unless that link is clicked and valid values have been entered and saved to the basket database for country, zip, and state? That is how the minibasket module works so that the flow is efficient without having to have two basket screens. Likewise, it can be used on other screens (eg storefront, product, category, basket, etc), not just in the basket screen. You'll need to change your code to querry the installed shipping modules directly and not use the native module that does that. Thus, don't rely on the render start; instead make it on-demand.
                Bill Weiland - Emporium Plus http://www.emporiumplus.com/store.mvc
                Online Documentation http://www.emporiumplus.com/tk3/v3/doc.htm
                Question http://www.emporiumplus.com/mivamodu...vc?Screen=SPTS
                Facebook http://www.facebook.com/EmporiumPlus
                Twitter http://twitter.com/emporiumplus

                Comment


                  #9
                  Hi Will,

                  Well, I probably would have, but I gave up on finding out how -- I don't know the function calls, and it seems like there's no help from Miva.

                  But also, this code uses the 'shippingmethods' Item to retrieve the shipping methods, and since that Item polls shipping the moment it's attached to a page, you have no choice -- it gets run every time.

                  If you know the Miva Script to retrieve shipping methods, that would be great to know. But I gave up on finding it.

                  By the way, this method doesn't need to write it to the database -- it simply writes to the internal global variables, and the built-in shipping poller responds to that. It works like a charm.

                  Per

                  Comment


                    #10
                    If you don't write to the database, are they being saved. If they add a couple more things to the basket and want to check shipping, do they have to supply their address info again? I couldn't tell on your store you referenced since it didn't work for me.
                    Bill Weiland - Emporium Plus http://www.emporiumplus.com/store.mvc
                    Online Documentation http://www.emporiumplus.com/tk3/v3/doc.htm
                    Question http://www.emporiumplus.com/mivamodu...vc?Screen=SPTS
                    Facebook http://www.facebook.com/EmporiumPlus
                    Twitter http://twitter.com/emporiumplus

                    Comment


                      #11
                      The link again is http://www.hollywoodcamerawork.us

                      No, it doesn't save it to the database, it only changes the runtime variables good enough for one calculation. Obviously, if you want persistent shipping rates, then the mini-basket would be better. We wanted a simpler one-time-use solution, and didn't want to add a foreign GUI element like the mini-basket.

                      It actually works like a charm. If I knew how to poll the shipping rates directly, I'd probably do that, but this works fine, and is pretty easy to maintain. If the code on the BASK page ever changes, it just has to be copy/pasted to BAS2.

                      What is the function-call to poll shipping rates?

                      Best,

                      Per

                      Comment


                        #12
                        I used Netscape 7 at the link you provided. When I click with one item in the basket, nothing happens. The mouse over shows javascript:; in the message bar at the bottom. Your js needs to be tweaked for browser compatibility.

                        There are multiple functions to identify the assigned shipping modules, query the modules, and parse the returned results.
                        Bill Weiland - Emporium Plus http://www.emporiumplus.com/store.mvc
                        Online Documentation http://www.emporiumplus.com/tk3/v3/doc.htm
                        Question http://www.emporiumplus.com/mivamodu...vc?Screen=SPTS
                        Facebook http://www.facebook.com/EmporiumPlus
                        Twitter http://twitter.com/emporiumplus

                        Comment


                          #13
                          Hi,

                          Yes, you're right with the browser compatibility. It's a small thing, but has to be done. I should say the absolute vast majority of our users are IE or FireFox.

                          Best,

                          Per

                          Comment


                            #14
                            BTW, the javascript compatibility thing mere relates to the way <div> tags are used to show/hide content. The tags that show/hide content are different in Netscape. I'll post an update for anyone who's interested.

                            Best,

                            Per

                            Comment


                              #15
                              Yes, it's a matter of using the "visibility:visible" and "visibility:hidden" style-tags in Netscape instead of "display: none" and "display: block".

                              I'll put that in. If anyone wants to use the code right away, you can also simply remove the whole <DIV> thing for the "Calculate Shipping Rates" button, and have the shipping calculator be on the screen always.

                              Per

                              Comment

                              Working...
                              X