Installation Instructions for "ups-lib.pl" Written by Jeffrey J. Walters Email: design@jjw.cc Web Site: http://www.jjw.cc/ Date Created: 01-23-2000 Date Last Modified: 05-13-2000 Description of ups-lib.pl: ups-lib.pl is used to implement the UPS Quick Cost Calculator service, available from www.ups.com, into the Web Store. I recommend that you register your company with UPS so you will receive updates from UPS when they become available. ups-lib.pl contains two subroutines; &display_ups_cost_table and &get_ups_cost. &display_ups_cost_table is used to display a table of UPS services for a user to select. The table includes the different service names, service costs, and any warning messages. The &get_ups_cost subroutine retrieves the UPS Quick Cost information (via a socket connection), based on input variables, and formats the information into two associative arrays. See the ups-lib.pl library for more information. Most recent modifications for version dated 05-13-2000: Please note, that the new version of ups-lib.pl dated 05-13-2000 will not work with the older installations. Several new setup variables have been created. You will need to work through these instructions in order to use the new library file. 1.) Worldwide shipping from US or PR. This change was developed by myself and Paul Hastings at paul@iecom.com, http://www.iecom.com/. Paul and I both concluded that UPS seems to calculate shipping outside of the US based on the Country alone and not based on the Country and the State/Territory. We also both agreed that the UPS Worldwide shipping costs seemed unusually high. 2.) More setup variables for less typo's during installation. To avoid confusion between destination FORM variable names, several setup variables have been created. This will hopefully reduce typos such as using a double quote (") where a single quote (') was necessary. 3.) Comment lines added to "ups-lib.pl". The two subroutines &get_ups_cost and &display_ups_cost_table are now documented much better to help explain the inner workings on these subroutines. 4.) Flexible Calculated Weight (linear adjustment). There are now two setup variables $sc_weight_multiplier and $sc_weight_addition which allow you to adjust the measured weight field prior to the UPS quick cost calculation. See the &get_ups_cost subroutine. 5.) UPS server not responding - bug fix. Kevin Dennis at kevin.l.dennis@mail.sprint.com sent me a small hack to fix a problem with the socket connection. It seems the "I Love You" Virus caused the UPS Server to be shut down for precautionary purposes. During this time the $get_ups_cost subroutine got hung-up at the line: "while (<$socket>) { push (@lines, $_) }" Eventually the program was terminated by the Web Server and the resulting HTML was truncated. This problem has been fixed and orders can be processed even without the interaction of the UPS Server. Installation Instructions: Installation of the ups-lib.pl is not an easy task. If you do not feel comfortable making your own "personal" changes to the Web Store then you should probably not attempt this change; in other words, "Stop Here"! On the other hand if you have 'some' CGI/Perl programming experience, then please continue, the result will be worth the effort! The creation of this library was inspired by Gheorghe Chesler's UPS Module and many Perl authors such as Gunther Birznieks, Selena Sol and countless others. http://www.extropia.com/hacks/chesler_shipping.html STEP #1.) First, you will need to add a confirmation page to the Web Store. Do this before you continue. See the following link: http://www.extropia.com/hacks/jackson_ws_confirms.html A confirmation page is needed because we need to get the users "Zipcode" in order to ask the UPS server to calculate the various shipping costs. STEP #2.) Second, we will also need establish the shipping "Weight" of the package. In other words, you will need to create a Weight field in your database and you need it to be your "Measured Field" as well. This is done in your setup file. Add the "weight" field to your database and to the Web Store. Your numbers may and probably will differ!!!! You probably will not have a "description2" field!!! Add "weight" to the $db definition array: $db{"product_id"} = 0; $db{"product"} = 1; $db{"price"} = 2; $db{"name"} = 3; $db{"image_url"} = 4; $db{"description"}= 5; $db{"description2"}= 6; $db{"weight"} = 7; $db{"options"} = 8; Add "weight" to the @sc_db_index_for_defining_item_id array: @sc_db_index_for_defining_item_id = ($db{"product_id"}, $db{"product"}, $db{"price"}, $db{"name"}, $db{"weight"}); Add the "weight" field to the cart definition: $cart{"quantity"} = 0; $cart{"product_id"} = 1; $cart{"product"} = 2; $cart{"price"} = 3; $cart{"name"} = 4; $cart{"weight"} = 5; $cart{"options"} = 6; $cart{"price_after_options"} = 7; $cart{"unique_cart_line_id"} = 8; Define the measured field to be the shipping "weight": $sc_cart_index_of_measured_value = $cart{"weight"}; Add the "weight" to the following two arrays. The cart display fields and the index for display: @sc_cart_display_fields = ("Description", "Options", "Weight", "After Options"); @sc_cart_index_for_display = ($cart{"name"}, $cart{"options"}, $cart{"weight"}, $cart{"price_after_options"}); Now that you have added the weight to your database and setup all the previous additions to your setup file, you should now see the Weight field in your shopping cart. Measured Field Web Store Bug Fix: Note that the Original Web Store program does not calculate the measured_field properly! The "Quantity" was not included in the calculation. Do the following to correct this bug. Open your "web_store_html_lib.pl" file and search for "$total_measured_quantity". The second search will display a section like the following. if ($display_index == $sc_cart_index_of_measured_value) { $total_measured_quantity += $cart_fields[$display_index]; } Replace it with the following code and the bug is fixed: if ($display_index == $sc_cart_index_of_measured_value) { $total_measured_quantity += $cart_fields[$display_index]*$quantity; } STEP #3.) We now need to install the ups-lib.pl library and make sure it gets required when needed. This is very important. Copy/FTP the ups-lib.pl to the Web Store ./Library directory. Be careful with the name spelling "ups-lib.pl" and the case sensitivity. You may need to rename it from "ups-lib_ Open up your setup file again, and add the following line to the Global File Location variables section. This section is the first section of your setup file. Add the following line: $sc_ups_lib_path = "./Library/ups-lib.pl"; Next open up your webstore.cgi (main Web Store program) and add the UPS library ("$sc_ups_lib_path") to the following so ups-lib.pl is properly "required" when needed. Don't forget to FTP the "ups-lib.pl" file over to your Web server. elsif ($form_data{'confirm_order_button'} ne "") { &require_supporting_libraries (__FILE__, __LINE__, "$sc_ups_lib_path", "$sc_order_lib_path"); &confirm_order; exit; } elsif ($form_data{'submit_order_confirm_button'} ne "") { &require_supporting_libraries (__FILE__, __LINE__, "$sc_ups_lib_path", "$sc_order_lib_path"); &process_order_confirm; exit; } STEP #4.) Setup file additions: Add the following to your setup file. You will need to define your country and origin postal zipcode. You will also need to define the destination FORM fields to be used. Your shipping fields may be different, see the setup file %sc_order_form_array to find your shipping fields for the shipping destination zipcode, country, and shipping selection. # UPS Shipping Cost Variables # Shipping Origin Information: $sc_origin_country = "US"; # Either US (United States) or PR (Puerto Rico) $sc_origin_postal_code = "32608"; # Gainsville, Florida US # Destination FORM Fields $sc_ups_zip_field = '10-m_zip'; $sc_ups_country_field = '11-m_country'; $sc_ups_shipping_field = '22-shipping'; Next we will add the UPS services we wish to offer the Web Store users. See the UPS Quick Cost Calculator documentation for more information on the UPS service codes. # An array of possible UPS Services you wish to allow the user to select. # The number's 1,2,3,4,...11 are used to sort the possible UPS services # in the order you wish for the display selection. # Note the commented-out lines "#" which are not used in this case! %sc_ups_services_to_display = ( # '7-1DM','Next Day Air Early AM', # '6-1DA','Next Day Air', # '5-1DP','Next Day Air Saver', # '4-2DM','2nd Day Air AM', '3-2DA','2nd Day Air', '2-3DS','3 Day Select', '1-GNDRES','Ground Residential', '8-STD','Canada Standard', '9-XPR','Worldwide Express', # '10-XDM','Worldwide Express Plus', # '11-XPD','Worldwide Expedited' ); Next we will add the Shipping Weight Adjustment Section. All of these changes are to go in the shipping section of your setup file. # Shipping Weight Adjustments: It is important to calculate UPS shipping # based on a reasonable weight ("measured_field"). You can adjust the weight # that the &get_ups_cost subroutine uses by changing the following: $sc_weight_multiplier = "1"; # Example: 1.07 would be a 7% increase. $sc_weight_addition = "0"; # Measured in Pounds (lbs.) One last setup file addition: A background color for the HTML tables. Feel free to make this any color you wish: # Various Color Variables $sc_background_color1 = qq~#EEEEEE~; Setup Change: You may need to adjust your shipping display variables so the shipping cost is displayed on the proper pages. This can be found in your setup file. $sc_calculate_shipping_at_display_form = 0; # "before" $sc_calculate_shipping_at_process_form = 3; # "after" We don't want the shipping cost displayed on the &confirm_order subroutine page, but rather on the &process_order_confirm page. More on these two variables in the next couple steps. STEP #5.) Removing the old Shipping Selection from your Order Form HTML: Order Form HTML: Your order form might be called, "outlet_order_form_with_shipping.html" or possibly something different. Just make sure to cut out the shipping selection! The user will be making the shipping selection on the following Web Store generated page (subroutine: confirm_order). This is very important. If the shipping variable is accidentally given a value and passed by your Order Form HTML then the next page's Shipping Selection may get over-ridden. In order to help with the Selections for State/Province and Country I've added the following HTML. Cut and past this into your Order Form HTML. Be aware that the UPS Server only recognizes these Country Codes! You might have to change the "04-b_state"! And for the Country Selection, again you might have to change the "06-b_country". STEP #6.) Passing the Hidden FORM Fields: web_store_process.pl - subroutine confirm_order Make sure that you DO NOT pass the shipping field using a Hidden FORM Field! Instead to pass your hidden fields use the following block of code which will create the HTML for ALL Order Fields EXCEPT the Shipping Field (Example: '22-shipping'). This is to basically clean up your code some. # For each field in sc_order_form_array add # a hidden field so values get passed thru FORM @associative_array_keys = sort(keys(%sc_order_form_array)); foreach $field (@associative_array_keys) { if ($field ne "$sc_ups_shipping_field") { print qq!\n!; } } Also, some other useful code. If the users mailing address was left blank and you wish to set the their "mailing" address equal to their "billing" address, then use the following block of code. Watch out for the default selections - those could ruin the logic. # If Mailing Address is blank then set the # Mailing Address equal to the Billing Address. if ($form_data{'07-m_street_address'} eq "" && $form_data{'08-m_city'} eq "" && $form_data{'09-m_state'} eq "" && $form_data{'10-m_zip'} eq "") { $form_data{'07-m_street_address'} = $form_data{'02-b_street_address'}; $form_data{'08-m_city'} = $form_data{'03-b_city'}; $form_data{'09-m_state'} = $form_data{'04-b_state'}; $form_data{'10-m_zip'} = $form_data{'05-b_zip'}; $form_data{'11-m_country'} = $form_data{'06-b_country'}; } STEP #7.) "web_store_process_lib.pl" changes: Now for the fun part, we need to make the following additions to the "web_store_order_lib.pl". Open up this file and find the beginning of the "confirm_order" subroutine. Find the call to &display_calculations and make sure it uses "before", because we don't want it to try to calculate the shipping at this point. We want to allow for a selection of the shipping method first - the next step. $text_of_cart = &display_calculations($subtotal,"before",$total_measured_quantity,$text_of_cart); Remember the setup file variable: $sc_calculate_shipping_at_display_form = 0; The combination of the two will hopefully NOT display the shipping cost. STEP #8.) "web_store_process_lib.pl" changes: Subroutine &confirm_order Check to make sure that the following two subroutines call using "before" as their input values. This is important. ($subtotal, $total_quantity, $total_measured_quantity, $text_of_cart) = &display_cart_table("before"); $text_of_cart = &display_calculations($subtotal,"before",$total_measured_quantity,$text_of_cart); Making the Shipping Selection Appear. Add the following code just after the customer information is displayed. The customer information can be found towards the end of the "confirm_order" subroutine. ############################################################ if ($required_fields_filled_in eq "yes") { print qq!
UPS Shipping Selection
Our products are shipped using the United Parcel Service, the Internet's leader in E-Commerce solutions. Please, select the type of UPS shipping you would prefer.

!; &get_ups_cost ($sc_origin_postal_code, $sc_origin_country, $form_data{$sc_ups_zip_field}, $form_data{$sc_ups_country_field}, $total_measured_quantity, $subtotal); &display_ups_cost_table("order form"); print qq!
If the information above is correct and you have selected the type of shipping you prefer, just click the "submit secure order" button at the bottom of this page and your order will be completed.
!; } else { ############################################################ Be careful how you insert this information; especially the location of the "} else {". Many people have 'goofed' here. You might decide to move this code above the customers displayed information, it truly doesn't matter. IMHO, the only logic that should be followed is that, $required_fields_filled_in must equal "yes". Feel free to change the wording and formatting HTML. Notice the usage of the two new subroutines &get_ups_cost and &display_ups_cost_table. These are the only two subroutines in the ups-lib.pl library. &get_ups_cost defines all the proper UPS input variables, creates the socket connection to the UPS server and defines the proper output variables which the &display_ups_cost_table uses to display the UPS selections for the user. For more information on how these subroutines work please see the ups-lib.pl library file. If the UPS shipping "selection" table is not displaying try looking/playing at the following setup variables: $sc_calculate_shipping_at_display_form = 0; $sc_calculate_shipping_at_process_form = 3; STEP #9.) "&process_order_confirm" subroutine: Believe it or not there are no "big" changes to the process_order_confirm subroutine. Let me try to run you through the logic at this point. Lets assume that the user has confirmed his/her information and made a shipping selection. The program then calls the "&process_order_confirm" subroutine in the "web_store_process_lib.pl" library. The "&process_order_confirm" subroutine calls the following two subroutines: ($subtotal, $total_quantity, $total_measured_quantity, $text_of_cart) = &display_cart_table("process order"); $text_of_cart = &display_calculations($subtotal,"at",$total_measured_quantity,$text_of_cart); &display_cart_table - displays the users shopping cart information. It also defines the $total_measured_quantity; the weight field! &display_calculations is the one which is pretty hard to follow! Here are the subroutines involved to get the the &get_ups_cost subroutine: Many people have asked about this flow of subroutines. &display_calculations ----------- calls --- &calculate_final_values &calculate_final_values --------- calls --- &calculate_shipping subroutine &calculate_shipping subroutine -- calls --- &get_ups_cost The shipping cost is then returned for display. STEP #10.) subroutine &calculate_shipping in web_store_order_lib.pl Remove the old calculate_shipping subroutine and replace it with this new subroutine. ############################################################ # # subroutine: calculate_shipping # Usage: # $shipping = # &calculate_shipping($sub_total, # $total_quantity, # $total_measured_quantity); # # Parameters: # $sub_total = the subtotal to calculate shipping on # $total_quantity = quantity of items to calc shipping on # $total_measured_quantity = quanity of measured item to # calc shipping on # Output: # The value of the shipping # ############################################################ sub calculate_shipping { local($subtotal, $total_quantity, $total_measured_quantity) = @_; local ($form_service_code, $form_service_text, $form_service_cost, $shipping_new); # Call UPS subroutine get_ups_cost to define the # available service costs in %ups_cost. &get_ups_cost ($sc_origin_postal_code, $sc_origin_country, $form_data{$sc_ups_zip_field}, $form_data{$sc_ups_country_field}, $total_measured_quantity, $subtotal); # Split the VALUE from FORM NAME="$sc_ups_shipping_field". # This gives us the user selected information in the format # "service code|service text|service cost". For more # information see the display_ups_cost_table subroutine. ($form_service_code, $form_service_text, $form_service_cost) = split(/\|/,$form_data{$sc_ups_shipping_field}); # Compare the service cost from %ups_cost to the # service cost from the FORM variable. Check for # value consistency! if ($form_service_cost ne $ups_cost{$form_service_code}) { $ups_shipping_msg .= "Error: An inconsistency in the calculated " . "shipping cost has occured!"; } # Re-define the $sc_ups_shipping_field as the shipping text. # This can be used later to display to the user. $form_data{$sc_ups_shipping_field} = $form_service_text; # Determine if we should return shipping # cost or an error message! if ($ups_shipping_msg ne "" || $ups_socket_msg ne "" || $form_data{$sc_ups_shipping_field} eq "") { print qq!

\n\n

\n\n\n \n\n
\n\n!; print qq!$ups_socket_msg\n\n

!; print qq!$ups_shipping_msg\n\n

!; print qq~There seems to have been an error while trying to calculate your UPS shipping cost. We will try to contact you via email or phone within 24 hours to provide you with a shipping cost for your order!\n~; print qq!

\n
\n

\n\n!; # Re-define the $sc_ups_shipping_field as an Error message # to display to the user in the Final Customer Information. $form_data{$sc_ups_shipping_field} = "An error has occured!"; # For Troubleshooting Only # print qq~\n\n \n\n~; return (); } else { # Return UPS Cost for Selected Service return ($form_service_cost); } } # End of calculate_shipping ############################################################ Failure Mode: Please make note that you can easily create a "failure mode" using the above calculate_shipping subroutine. At first, I thought it would be nice to rely on the "old" shipping calculation as a failure mode, but now that I've seen it in action I think it's better off without it. You may feel that you "need" to rely on the old shipping calculation as a "failure mode" for the UPS calculation. If so, just call the &calculate_general_logic as in the original Web Store program. Well, that's about it except for general cosmetic changes to the HTML. I "think" I've covered just about everything. If you get this to work properly, please drop me an email so I can see it! If you are having problem that you just can not figure out, feel free to email me and I will try to help as time permits. Please include the Web location of the script you are trying to debug and a detailed description of your problem. If you think I've forgotten something, please email me as well so I can make any necessary changes to these instructions. A working example of this change can be found at http://www.outdoorstoreusa.com/. Jeff Walters ------------------------------- Jeffrey J. Walters Web Design http://www.jjw.cc/ design@jjw.cc or hiker_jjw@yahoo.com ------------------------------- If money is your only hope for independence you will never have it. The only real security that a man will have is a reserve of knowledge, experience, and ability. (Henry Ford) -------------------------------