Jump to content
blucollarguy

Multi_Vendor_Shipping new thread

Recommended Posts

Anybody there? I would appreciate somebody to post checkout_shipping.php code...

 

Thanks in advance

You shouldn't need that file posted. Use the one included in the package to do a comparison with. One of the problems I see is that you don't have the "define" installed. The uppercase string of text is a constant, that get's defined in the language files.

 

This one should be in includes/languages/english/checkout_shipping.php,

define('TABLE_HEADING_SHIPPING_METHOD', 'Shipping Method');

 

Your "headers already sent" issue is different, usually from some bit of code being done twice, or an error in the code preventing the script from completing it's job(like an extra bracket( } or { ).

 

If you use a file comparison tool, such WinMerge, which is free from source forge(google it and you will find it) you will probably be able to find your problem.

 

Good luck, Craig :)


Happy Coding!

Craig Garrison Sr

Anything worth having, is worth working for.

Multi Vendor Shipping V1.1 Demo Catalog

3 Vendors, each category, "buy" a product from each category to see how MVS works during checkout.

Multi Vendor Shipping V1.1 Demo Admin

login: webmaster@blucollarsales.com

pass: mvs_demo

MVS Thread:

Multi-Vendor Shipping

My contribs:

Download Multi Vendor Shipping V1.1

Vendor Email

Vendor Info in easypopulate

EZ Price Updater

And more to come!

Share this post


Link to post
Share on other sites

Well thanks, I'll keep looking for the missing bracket... Awesome contribution, certainly!

Share this post


Link to post
Share on other sites

Can anyone tell me how vendor Tare Weight works?

 

thanks,

Ron

Share this post


Link to post
Share on other sites
Can anyone tell me how vendor Tare Weight works?

 

thanks,

Ron

The tare weight is the weight of the box, so if the product weighs 6 lbs, the tare weight is set to 1 lb, the final weight would be 5 lbs. The tare weight is deducted from the total weight of a given package in an attempt to determine the weight of the box, without the product.

 

Odd thing is, I don't think we are actually using if for anything. It was added for completeness, but I'm not sure that we ever implemented anything that uses that value. LOL


Happy Coding!

Craig Garrison Sr

Anything worth having, is worth working for.

Multi Vendor Shipping V1.1 Demo Catalog

3 Vendors, each category, "buy" a product from each category to see how MVS works during checkout.

Multi Vendor Shipping V1.1 Demo Admin

login: webmaster@blucollarsales.com

pass: mvs_demo

MVS Thread:

Multi-Vendor Shipping

My contribs:

Download Multi Vendor Shipping V1.1

Vendor Email

Vendor Info in easypopulate

EZ Price Updater

And more to come!

Share this post


Link to post
Share on other sites
The tare weight is the weight of the box, so if the product weighs 6 lbs, the tare weight is set to 1 lb, the final weight would be 5 lbs. The tare weight is deducted from the total weight of a given package in an attempt to determine the weight of the box, without the product.

 

Odd thing is, I don't think we are actually using if for anything. It was added for completeness, but I'm not sure that we ever implemented anything that uses that value. LOL

So it should work like the standard install of osCommerce's tare weight works? If that is true, then the tare weight value is added to the product weight, giving the total weight of a packaged product ready to ship. I have been using tare weight prior to my installing MVS, and my value of .13 equates to .13 lb, or a little over 2 ounces.

 

My confusion with MVS vendor Tare Weight stems from the fact that it does actually do something. If I put in a high value, it begins to effect the shipping costs, but the values I am getting back don't seem to make any sense, as a value of 2.00 equates to something like 3 ounces. It seems that 2.00 should be 2 lb, not 3 ounces. So it seems that it needs a little tweaking? LOL

 

Ron

Share this post


Link to post
Share on other sites

hiya,

 

in file: admin/vendor_email_send.php, i noticed a few lines of code referring to this url: http://newfedora/3/admin/post_vendor_order.php

 

if ($vendors_email_list['vendors_id'] == 1) {
 $order_number = $oID;
 $product_codes = $vendors_email_products['vendors_prod_id'] .  $vendors_email_products['products_model'];
 $p_name = $vendors_email_products['customers_name'];
 $p_email = $vendors_email_products['customers_email_address'];
 [b]$url = 'http://newfedora/3/admin/post_vendor_order.php';[/b]
 $request_string .= 'order_number=' . $oID;
 $request_string .= 'product_codes=' . $product_codes;
 $request_string .= 'customer_name=' . $p_name;
 $request_string .= 'customer_email=' . $p_email;
//  echo  'The string of data: ' . $request_string . '<br>';

header ("Location: [b]http://newfedora/3/admin/post_vendor_order.php?order_number=".$order_number."&product_codes=".$product_codes."&p_name=".$p_name."&p_email=".$p_email);

[/b]

 

anyone know what this is for? im bit curious, if not a bit paranoid.. ;)

thanks,

 

JJ

Share this post


Link to post
Share on other sites
hiya,

 

in file: admin/vendor_email_send.php, i noticed a few lines of code referring to this url: http://newfedora/3/admin/post_vendor_order.php

 

if ($vendors_email_list['vendors_id'] == 1) {
 $order_number = $oID;
 $product_codes = $vendors_email_products['vendors_prod_id'] .  $vendors_email_products['products_model'];
 $p_name = $vendors_email_products['customers_name'];
 $p_email = $vendors_email_products['customers_email_address'];
 [b]$url = 'http://newfedora/3/admin/post_vendor_order.php';[/b]
 $request_string .= 'order_number=' . $oID;
 $request_string .= 'product_codes=' . $product_codes;
 $request_string .= 'customer_name=' . $p_name;
 $request_string .= 'customer_email=' . $p_email;
//  echo  'The string of data: ' . $request_string . '<br>';

header ("Location: [b]http://newfedora/3/admin/post_vendor_order.php?order_number=".$order_number."&product_codes=".$product_codes."&p_name=".$p_name."&p_email=".$p_email);

[/b]

 

anyone know what this is for? im bit curious, if not a bit paranoid.. ;)

thanks,

 

JJ

You should also have noticed the notes in this block of code that state it is for developement purposes only. "newfedora" is the name of my home test server and I left that in so anyone wanting to explore other options with the vendor email(such as posting data to another web page) might use it if they so desired. Nothing malicious about it, you would have heard about it by now considering that's been there since the second or third release I think.

 

No big deal, I guess I should clean all that out with the next release.

 

Craig :)


Happy Coding!

Craig Garrison Sr

Anything worth having, is worth working for.

Multi Vendor Shipping V1.1 Demo Catalog

3 Vendors, each category, "buy" a product from each category to see how MVS works during checkout.

Multi Vendor Shipping V1.1 Demo Admin

login: webmaster@blucollarsales.com

pass: mvs_demo

MVS Thread:

Multi-Vendor Shipping

My contribs:

Download Multi Vendor Shipping V1.1

Vendor Email

Vendor Info in easypopulate

EZ Price Updater

And more to come!

Share this post


Link to post
Share on other sites

A quick question about Vendor Reports.

 

Is there a way to run off a report that shows a month's worth of purchases JUST from a SPECIFIC vendor?

 

What snags on my shop is that if there is a purchase order from one customer for goods from 2 different vendors, that purchase shows up FULLY on both vendors accounts, so I can't rely on it to produce an accurate reflection of the money owed by me to that vendor for goods supplied by them.

 

I've also not yet found where I can see the total of a) prices paid by customers and B) Vendors price (which I owe to the vendors.)

 

A bit of help to the back-end retarded like me would be appreciated, as what I would like to do to each of my 7 vendors, once a month is to run a report that shows goods bought in that month, and what I owe the vendors for them. I don't want goods for other vendors getting in the way just because the customer made a multiple order.

 

Thanks for any assistance

 

Martin

Share this post


Link to post
Share on other sites

I'm trying to adapt UPS XML 1.2.8 to MVS but am having a little trouble. When i upload upsxml.php and go to manage vendor shipping modules I get this:

 

Warning: constant() [function.constant]: Couldn't find constant MODULE_SHIPPING_UPSXML_RATES_ZONE_3 in /home/grdstore/public_html/includes/modules/vendors_shipping/upsxml.php on line 340

 

 

All the modules are being displayed and it still allows me to install the new upsxml but I then get this:

 

1054 - Unknown column 'configuration_id' in 'field list'

 

insert into vendor_configuration ( configuration_id, configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, last_modified, date_added, use_function, set_function, vendors_id) values ('', 'Shipping Delay', 'SHIPPING_DAYS_DELAY_3', '1', 'How many days from when an order is placed to when you ship it (Decimals are allowed). Arrival date estimations are based on this value.', '6', '21', NULL, now(), '3', NULL, NULL)

 

[TEP STOP]

 

 

Obviously I have missed or forgot something but after a half a day trying to figure out what I want another opinion.

 

heres the code I have so far:

 

 

<?php
/*
   $Id: upsxml.php,v 1.1.4 2004/12/19 13:30:00 sgo Exp $
 Modified for MVS V1.0 2006/03/25 JCK/CWG
 osCommerce, Open Source E-Commerce Solutions
http://www.oscommerce.com

 Copyright © 2006 osCommerce

   Original copyright © 2003 Torin Walker
   This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
   This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
   warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
   See the GNU General Public License for more details.
   You should have received a copy of the GNU General Public License along with this program;
   If not, you may obtain one by writing to and requesting one from:
   The Free Software Foundation, Inc.,
   59 Temple Place, Suite 330,
   Boston, MA 02111-1307 USA

   Written by Torin Walker.
   Some code/style borrowed from both Fritz Clapp's UPS Choice 1.7 Module,
   and Kelvin, Kenneth, and Tom St.Croix's Canada Post 3.1 Module.
   Insurance support by Joe McFrederick
*/

require ('includes/classes/xmldocument.php');
// if using the optional dimensional support, set to 1, otherwise leave as 0
// define('DIMENSIONS_SUPPORTED', 1);
// obsolete: is set in admin now

class upsxml {
   var $code, $title, $description, $icon, $enabled, $types, $boxcount;

   //***************
   function upsxml() {
       global $order;
       $this->code = 'upsxml';
       $this->title = MODULE_SHIPPING_UPSXML_RATES_TEXT_TITLE;
       $this->description = MODULE_SHIPPING_UPSXML_RATES_TEXT_DESCRIPTION;
       $this->sort_order = MODULE_SHIPPING_UPSXML_RATES_SORT_ORDER;
       $this->icon = DIR_WS_ICONS . 'shipping_ups.gif';
       $this->tax_class = MODULE_SHIPPING_UPSXML_RATES_TAX_CLASS;
       $this->enabled = ((MODULE_SHIPPING_UPSXML_RATES_STATUS == 'True') ? true : false);
       $this->access_key = MODULE_SHIPPING_UPSXML_RATES_ACCESS_KEY;
       $this->access_username = MODULE_SHIPPING_UPSXML_RATES_USERNAME;
       $this->access_password = MODULE_SHIPPING_UPSXML_RATES_PASSWORD;
       $this->access_account_number = MODULE_SHIPPING_UPSXML_RATES_UPS_ACCOUNT_NUMBER;
       $this->use_negotiated_rates = MODULE_SHIPPING_UPSXML_RATES_USE_NEGOTIATED_RATES;
       $this->origin = MODULE_SHIPPING_UPSXML_RATES_ORIGIN;
       $this->origin_city = MODULE_SHIPPING_UPSXML_RATES_CITY;
       $this->origin_stateprov = MODULE_SHIPPING_UPSXML_RATES_STATEPROV;
       $this->origin_country = MODULE_SHIPPING_UPSXML_RATES_COUNTRY;
       $this->origin_postalcode = MODULE_SHIPPING_UPSXML_RATES_POSTALCODE;
       $this->pickup_method = MODULE_SHIPPING_UPSXML_RATES_PICKUP_METHOD;
       $this->package_type = MODULE_SHIPPING_UPSXML_RATES_PACKAGE_TYPE;
       $this->unit_weight = MODULE_SHIPPING_UPSXML_RATES_UNIT_WEIGHT;
       $this->unit_length = MODULE_SHIPPING_UPSXML_RATES_UNIT_LENGTH;
       if (MODULE_SHIPPING_UPSXML_DIMENSIONS_SUPPORT == 'Ready-to-ship only') {
         $this->dimensions_support = 1;
       } elseif (MODULE_SHIPPING_UPSXML_DIMENSIONS_SUPPORT == 'With product dimensions') {
         $this->dimensions_support = 2;
       } else {
         $this->dimensions_support = 0;
       }
       $this->email_errors = ((MODULE_SHIPPING_UPSXML_EMAIL_ERRORS == 'Yes') ? true : false);
       $this->handling_type = MODULE_SHIPPING_UPSXML_HANDLING_TYPE;
       $this->handling_fee = MODULE_SHIPPING_UPSXML_RATES_HANDLING;
       $this->quote_type = MODULE_SHIPPING_UPSXML_RATES_QUOTE_TYPE;
       $this->customer_classification = MODULE_SHIPPING_UPSXML_RATES_CUSTOMER_CLASSIFICATION_CODE;
       $this->protocol = 'https';
       $this->host = ((MODULE_SHIPPING_UPSXML_RATES_MODE == 'Test') ? 'wwwcie.ups.com' : 'www.ups.com');
       $this->port = '443';
       $this->path = '/ups.app/xml/Rate';
       $this->transitpath = '/ups.app/xml/TimeInTransit';
       $this->version = 'UPSXML Rate 1.0001';
       $this->transitversion = 'UPSXML Time In Transit 1.0002';
       $this->timeout = '60';
       $this->xpci_version = '1.0001';
       $this->transitxpci_version = '1.0002';
       $this->items_qty = 0;
       $this->timeintransit = '0';
       $this->timeInTransitView = MODULE_SHIPPING_UPSXML_RATES_TIME_IN_TRANSIT_VIEW;
       $this->weight_for_timeintransit = '0';
       $now_unix_time = mktime(date("H"), date("i"), date("s"), date("m"), date("d"), date("Y"));
       $this->today_unix_time = $now_unix_time;
       $this->today = date("Ymd");
       // insurance addition.
       if (MODULE_SHIPPING_UPSXML_INSURE == 'False')
         { $this->pkgvalue = 100; }
       if (MODULE_SHIPPING_UPSXML_INSURE == 'True')
         { $this->pkgvalue = ceil($order->info['subtotal']); }
       // end insurance addition
       // to enable logging, create an empty "upsxml.log" file at the location you set below, give it write permissions (777) and uncomment the next line
       //   $this->logfile = '/var/www/html/3/vendor-files/upsxml.log';

       // to enable logging of just the errors, do as above but call the file upsxml_error.log
       //      $this->ups_error_file = '/srv/www/htdocs/catalog/includes/modules/shipping/upsxml_error.log';
       // when cURL is not compiled into PHP (Windows users, some Linux users)
       // you can set the next variable to "1" and then exec(curl -d $xmlRequest, $xmlResponse)
       // will be used
       $this->use_exec = '0';

//        if (($this->enabled == true) && ((int)MODULE_SHIPPING_UPSXML_RATES_ZONE > 0)) {
//            $check_flag = false;
//            $check_query = tep_db_query("select zone_id from " . TABLE_ZONES_TO_GEO_ZONES . " where geo_zone_id = '" . MODULE_SHIPPING_UPSXML_RATES_ZONE . "' and zone_country_id = '" . $order->delivery['country']['id'] . "' order by zone_id");
//            while ($check = tep_db_fetch_array($check_query)) {
//                if ($check['zone_id'] < 1) {
//                    $check_flag = true;
//                    break;
//                } elseif ($check['zone_id'] == $order->delivery['zone_id']) {
//                    $check_flag = true;
//                    break;
//                }
//            }
//            if ($check_flag == false) {
//                $this->enabled = false;
//            }
//        }

       // Available pickup types - set in admin
       $this->pickup_methods = array(
           'Daily Pickup' => '01',
           'Customer Counter' => '03',
           'One Time Pickup' => '06',
           'On Call Air Pickup' => '07',
           'Suggested Retail Rates (UPS Store)' => '11',
           'Letter Center' => '19',
           'Air Service Center' => '20'
       );

       // Available package types
       $this->package_types = array(
           'UPS Letter' => '01',
           'Package' => '02',
           'UPS Tube' => '03',
           'UPS Pak' => '04',
           'UPS Express Box' => '21',
           'UPS 25kg Box' => '24',
           'UPS 10kg Box' => '25'
       );

       // Human-readable Service Code lookup table. The values returned by the Rates and Service "shop" method are numeric.
       // Using these codes, and the administratively defined Origin, the proper human-readable service name is returned.
       // Note: The origin specified in the admin configuration affects only the product name as displayed to the user.
       $this->service_codes = array(
           // US Origin
           'US Origin' => array(
               '01' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_01,
               '02' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_02,
               '03' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_03,
               '07' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_07,
               '08' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_08,
               '11' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_11,
               '12' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_12,
               '13' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_13,
               '14' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_14,
               '54' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_54,
               '59' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_59,
               '65' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_65
           ),
           // Canada Origin
           'Canada Origin' => array(
               '01' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_CANADA_ORIGIN_01,
               '02' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_CANADA_ORIGIN_02,
               '07' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_CANADA_ORIGIN_07,
               '08' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_CANADA_ORIGIN_08,
               '11' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_CANADA_ORIGIN_11,
               '12' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_CANADA_ORIGIN_12,
               '13' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_CANADA_ORIGIN_13,
               '14' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_CANADA_ORIGIN_14,
               // service code 54 gone after January 2, 2007
               '54' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_CANADA_ORIGIN_54,
               '65' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_CANADA_ORIGIN_65
           ),
           // European Union Origin
           'European Union Origin' => array(
               '07' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_EU_ORIGIN_07,
               '11' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_EU_ORIGIN_11,
               '54' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_EU_ORIGIN_54,
               '65' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_EU_ORIGIN_65,
               // next three services Poland domestic only (Stolica)
               '82' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_EU_ORIGIN_82,
               '83' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_EU_ORIGIN_83,
               '84' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_EU_ORIGIN_84
           ),
           // Puerto Rico Origin
           'Puerto Rico Origin' => array(
               '01' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_PR_ORIGIN_01,
               '02' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_PR_ORIGIN_02,
               '03' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_PR_ORIGIN_03,
               '07' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_PR_ORIGIN_07,
               '08' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_PR_ORIGIN_08,
               '14' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_PR_ORIGIN_14,
               '54' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_PR_ORIGIN_54,
               '65' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_PR_ORIGIN_65
           ),
           // Mexico Origin
           'Mexico Origin' => array(
               '07' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_MEXICO_ORIGIN_07,
               '08' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_MEXICO_ORIGIN_08,
               '54' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_MEXICO_ORIGIN_54,
               '65' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_MEXICO_ORIGIN_65
           ),
           // All other origins
           'All other origins' => array(
               // service code 7 seems to be gone after January 2, 2007
               '07' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_OTHER_ORIGIN_07,
               '08' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_OTHER_ORIGIN_08,
               '54' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_OTHER_ORIGIN_54,
               '65' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_PR_ORIGIN_65
           )
       );
   }

   function access_key($vendors_id='1') {
     $this->access_key = constant('MODULE_SHIPPING_UPSXML_RATES_ACCESS_KEY_' . $vendors_id);
     return $this->access_key;
   }

   function access_username($vendors_id='1') {
     $this->access_username = constant('MODULE_SHIPPING_UPSXML_RATES_USERNAME_' . $vendors_id);
     return $this->access_username;
   }

   function access_password($vendors_id='1') {
     $this->access_password = constant('MODULE_SHIPPING_UPSXML_RATES_PASSWORD_' . $vendors_id);
     return $this->access_password;
   }

   function origin($vendors_id='1') {
     $this->origin = constant('MODULE_SHIPPING_UPSXML_RATES_ORIGIN_' . $vendors_id);
     return $this->origin;
   }

   function origin_city($vendors_id='1') {
     $this->origin_city = constant('MODULE_SHIPPING_UPSXML_RATES_CITY_' . $vendors_id);
     return $this->origin_city;
   }

   function origin_stateprov($vendors_id='1') {
     $this->origin_stateprov = constant('MODULE_SHIPPING_UPSXML_RATES_STATEPROV_' . $vendors_id);
     return $this->origin_stateprov;
   }

   function origin_country($vendors_id='1') {
     $this->origin_country = constant('MODULE_SHIPPING_UPSXML_RATES_COUNTRY_' . $vendors_id);
     return $this->origin_country;
   }

   function origin_postalcode($vendors_id='1') {
     $this->origin_postalcode = constant('MODULE_SHIPPING_UPSXML_RATES_POSTALCODE_' . $vendors_id);
     return $this->origin_postalcode;
   }

   function pickup_method($vendors_id='1') {
     $this->pickup_method = constant('MODULE_SHIPPING_UPSXML_RATES_PICKUP_METHOD_' . $vendors_id);
     return $this->pickup_method;
   }

   function package_type($vendors_id='1') {
     $this->package_type = constant('MODULE_SHIPPING_UPSXML_RATES_PACKAGE_TYPE_' . $vendors_id);
     return $this->package_type;
   }

   function unit_weight($vendors_id='1') {
     $this->unit_weight = constant('MODULE_SHIPPING_UPSXML_RATES_UNIT_WEIGHT_' . $vendors_id);
     return $this->unit_weight;
   }

   function unit_length($vendors_id='1') {
     $this->unit_length = constant('MODULE_SHIPPING_UPSXML_RATES_UNIT_LENGTH_' . $vendors_id);
     return $this->unit_length;
   }

   function handling_fee($vendors_id='1') {
     $this->handling_fee = constant('MODULE_SHIPPING_UPSXML_RATES_HANDLING_' . $vendors_id);
     return $this->handling_fee;
   }

   function quote_type($vendors_id='1') {
     $this->quote_type = constant('MODULE_SHIPPING_UPSXML_RATES_QUOTE_TYPE_' . $vendors_id);
     return $this->quote_type;
   }

   function customer_classification($vendors_id='1') {
     $this->customer_classification = constant('MODULE_SHIPPING_UPSXML_RATES_CUSTOMER_CLASSIFICATION_CODE_' . $vendors_id);
     return $this->customer_classification;
   }

   function host($vendors_id) {
     $this->host = @constant('MODULE_SHIPPING_UPSXML_RATES_TEST_MODE_' . $vendors_id) == 'Test' ? 'wwwcie.ups.com' : 'wwwcie.ups.com';
     return $this->host;
   }

  function sort_order($vendors_id='1') {
    $sort_order = @constant ('MODULE_SHIPPING_UPSXML_RATES_SORT_ORDER_' . $vendors_id);
    if (isset ($sort_order)) {
      $this->sort_order = $sort_order;
    } else {
      $this->sort_order = '-';
    }
    return $this->sort_order;
  }

   function tax_class($vendors_id='1') {
     $this->tax_class = constant('MODULE_SHIPPING_UPSXML_RATES_TAX_CLASS_' . $vendors_id);
     return $this->tax_class;
   }

   function enabled($vendors_id='1') {
     $this->enabled = false;
     $status = @constant('MODULE_SHIPPING_UPSXML_RATES_STATUS_' . $vendors_id);
                       if (isset ($status) && $status != '') {
       $this->enabled = (($status == 'True') ? true : false);
     }
     if ( ($this->enabled == true) && ((int)constant('MODULE_SHIPPING_UPSXML_RATES_ZONE_' . $vendors_id) > 0) ) {
       $check_flag = false;
       $check_query = tep_db_query("select zone_id from " . TABLE_ZONES_TO_GEO_ZONES . " where geo_zone_id = '" . (int)constant('MODULE_SHIPPING_UPSXML_RATES_ZONE_' . $vendors_id) . "' and zone_country_id = '" . $this->delivery_country_id . "' order by zone_id");
       while ($check = tep_db_fetch_array($check_query)) {
         if ($check['zone_id'] < 1) {
           $check_flag = true;
           break;
         }
          elseif ($check['zone_id'] == $this->delivery_zone_id) {
           $check_flag = true;
           break;
           }
       }

       if ($check_flag == false) {
         $this->enabled = false;
       }//if
     }//if
     return $this->enabled;
   }

   function zones($vendors_id='1') {
     if ( ($this->enabled == true) && ((int)constant('MODULE_SHIPPING_UPSXML_RATES_ZONE_' . $vendors_id) > 0) ) {
       $check_flag = false;
       $check_query = tep_db_query("select zone_id from " . TABLE_ZONES_TO_GEO_ZONES . " where geo_zone_id = '" . (int)constant('MODULE_SHIPPING_UPSXML_RATES_ZONE_' . $vendors_id) . "' and zone_country_id = '" . $this->delivery_zone_id . "' order by zone_id");
       while ($check = tep_db_fetch_array($check_query)) {
         if ($check['zone_id'] < 1) {
           $check_flag = true;
           break;
         } elseif ($check['zone_id'] == $this->delivery_zone_id) {
           $check_flag = true;
           break;
         } //if
       }//while

       if ($check_flag == false) {
         $this->enabled = false;
       }//if
     }//if
     return $this->enabled;
   } // end function upsxml

   // class methods
   function quote($method = '', $module = '', $vendors_id = '1') {
       global $HTTP_POST_VARS, $order, $shipping_weight, $shipping_num_boxes, $total_weight, $boxcount, $cart;
       // UPS purports that if the origin is left out, it defaults to the account's location. Yeah, right.
       $state = $order->delivery['state'];
       $zone_query = tep_db_query("select zone_code from " . TABLE_ZONES . " where zone_name = '" .  $order->delivery['state'] . "'");
       if (tep_db_num_rows($zone_query)) {
           $zone = tep_db_fetch_array($zone_query);
           $state = $zone['zone_code'];
       }
       $this->_upsOrigin(constant('MODULE_SHIPPING_UPSXML_RATES_CITY_' . $vendors_id), constant('MODULE_SHIPPING_UPSXML_RATES_STATEPROV_' . $vendors_id), constant('MODULE_SHIPPING_UPSXML_RATES_COUNTRY_' . $vendors_id), constant('MODULE_SHIPPING_UPSXML_RATES_POSTALCODE_' . $vendors_id));
       $this->_upsDest($order->delivery['city'], $state, $order->delivery['country']['iso_code_2'], $order->delivery['postcode']);
       if (method_exists($cart, 'get_products_for_packaging') ) {
         $productsArray = $cart->get_products_for_packaging();
       } else {
         $productsArray = $cart->get_products();
       }
			if ($this->dimensions_support > 0) {
				$productsArray = $this->more_dimensions_to_productsArray($productsArray);
			}

       if ($this->dimensions_support == '2') {
           // sort $productsArray according to ready-to-ship (first) and not-ready-to-ship (last)
           usort($productsArray, ready_to_shipCmp);
           // Use packing algoritm to return the number of boxes we'll ship
           $boxesToShip = $this->packProducts($productsArray);
           // Quote for the number of boxes
           for ($i = 0; $i < count($boxesToShip); $i++) {
               $this->_addItem($boxesToShip[$i]['length'], $boxesToShip[$i]['width'], $boxesToShip[$i]['height'], $boxesToShip[$i]['current_weight'], $boxesToShip[$i]['price']);
               $totalWeight += $boxesToShip[$i]['current_weight'];
           }
       } elseif ($this->dimensions_support == '1') {
           $totalWeight = 0;
           $total_non_ready_to_ship_weight = 0;
					$total_non_ready_to_ship_value = 0;
           // sort $productsArray according to ready-to-ship (first) and not-ready-to-ship (last)
           usort($productsArray, ready_to_shipCmp);
           $non_ready_to_shipArray = array();
           // walk through the productsArray, separate the items ready-to-ship and add them to
           // the items (boxes) list, add the weight to the totalWeight
           // and add the other items to a separate array
           for ($i = 0; $i < count($productsArray); $i++) {
               if ($productsArray[$i]['ready_to_ship'] == '1') {
                   for ($z = 0 ; $z < $productsArray[$i]['quantity']; $z++) {
                       $this->_addItem($productsArray[$i]['length'], $productsArray[$i]['width'], $productsArray[$i]['height'], $productsArray[$i]['weight'], (MODULE_SHIPPING_UPSXML_INSURE == 'False' ? $this->pkgvalue : $productsArray[$i]['final_price']));
                       $totalWeight += $productsArray[$i]['weight'];
                   } // end for ($z = 0 ; $z < $productsArray[$i]['quantity']; $z++)
               } // end if($productsArray['ready_to_ship'] == '1')
               else {
                   $non_ready_to_shipArray[] = $productsArray[$i];
               }
           } // end for ($i = 0; $i < count($productsArray); $i++)
           // Ready_to_ship items out of the way, now assess remaining weight and remaining value of products

           for ($x = 0 ; $x < count($non_ready_to_shipArray) ; $x++) {
               $total_non_ready_to_ship_weight += ($non_ready_to_shipArray[$x]['weight'] * $non_ready_to_shipArray[$x]['quantity']);
							$total_non_ready_to_ship_value += ($non_ready_to_shipArray[$x]['final_price'] * $non_ready_to_shipArray[$x]['quantity']);
           } // end for ($x = 0 ; count($non_ready_to_shipArray) ; $x++)

           if (tep_not_null($non_ready_to_shipArray)) {
               // adapted code from includes/classes/shipping.php
               $shipping_non_ready_to_ship_boxes = 1;
               $shipping_non_ready_to_ship_weight = $total_non_ready_to_ship_weight;
               if (SHIPPING_BOX_WEIGHT >= $total_non_ready_to_ship_weight*SHIPPING_BOX_PADDING/100) {
                 $total_non_ready_to_ship_weight = $total_non_ready_to_ship_weight+SHIPPING_BOX_WEIGHT;
               } else {
                 $total_non_ready_to_ship_weight += $total_non_ready_to_ship_weight*SHIPPING_BOX_PADDING/100;
               }
               if ($total_non_ready_to_ship_weight > SHIPPING_MAX_WEIGHT) { // Split into many boxes
                   $shipping_non_ready_to_ship_boxes = ceil($total_non_ready_to_ship_weight/SHIPPING_MAX_WEIGHT);
                   $shipping_non_ready_to_ship_weight = round($total_non_ready_to_ship_weight/$shipping_non_ready_to_ship_boxes,1);
               }
               // end adapted code from includes/classes/shipping.php
               // weight and number of boxes of non-read-to-ship is determined, now add them to the items list
               for ($y = 0; $y < $shipping_non_ready_to_ship_boxes ; $y++) {
                   $this->_addItem(0, 0, 0, $shipping_non_ready_to_ship_weight, (MODULE_SHIPPING_UPSXML_INSURE == 'False' ? $this->pkgvalue : number_format(($total_non_ready_to_ship_value/$shipping_non_ready_to_ship_boxes), 2, '.', '')) );
                   $totalWeight += $shipping_non_ready_to_ship_weight;
               } // end for ($y = 0; $y < $shipping_non_ready_to_ship_boxes ; $y++)
           } // end if (tep_not_null($non_ready_to_shipArray))
       } else {
           // The old method. Let osCommerce tell us how many boxes, plus the weight of each (or total? - might be sw/num boxes)
           $this->items_qty = 0; //reset quantities
					if (MODULE_SHIPPING_UPSXML_INSURE == 'False') {
              for ($i = 0; $i < $shipping_num_boxes; $i++) {
                 $this->_addItem(0, 0, 0, $shipping_weight, $this->pkgvalue);
              }
					} else {
						// $this->pkgvalue has been set as order subtotal around line 86, it will cause overcharging
						// of insurance if not divided by the number of boxes
						 for ($i = 0; $i < $shipping_num_boxes; $i++) {
                 $this->_addItem(0, 0, 0, $shipping_weight, number_format(($this->pkgvalue/$shipping_num_boxes), 2, '.', ''));
              }
					} // end if/else  (MODULE_SHIPPING_UPSXML_INSURE == 'False')
       }

       // BOF Time In Transit: comment out this section if you don't want/need to have
       // expected delivery dates
       if ($this->dimensions_support > 0) {
           $this->weight_for_timeintransit = round($totalWeight,1);
       } else {
           $this->weight_for_timeintransit = round($shipping_num_boxes * $shipping_weight,1);
       }
       // Added to workaround time in transit error 270033 if total weight of packages is over 150lbs or 70kgs
       if (($this->weight_for_timeintransit > 150) && ($this->unit_weight == "LBS")) {
         $this->weight_for_timeintransit = 150;          
       } else if (($this->weight_for_timeintransit > 70) && ($this->unit_weight == "KGS")) {
         $this->weight_for_timeintransit = 70;          
       }
       // debug only:
       /*  echo '<pre>Packages and variables:<br />';
        print_r($this);
        echo '<br />';
        exit; */
       $this->servicesTimeintransit = $this->_upsGetTimeServices($vendors_id);
       if ($this->logfile) {
           error_log("------------------------------------------\n", 3, $this->logfile);
           error_log("Time in Transit: " . $this->timeintransit . "\n", 3, $this->logfile);
       }
       // EOF Time In Transit

       $upsQuote = $this->_upsGetQuote($vendors_id);
       if ((is_array($upsQuote)) && (sizeof($upsQuote) > 0)) {
           if ($this->dimensions_support > 0) {
               $this->quotes = array('id' => $this->code, 'module' => $this->title . ' (' . $this->boxCount . ($this->boxCount > 1 ? ' pkg(s), ' : ' pkg, ') . round($totalWeight,0) . ' ' . strtolower($this->unit_weight) . ' total)');
           } else {
               $this->quotes = array('id' => $this->code, 'module' => $this->title . ' (' . $shipping_num_boxes . ($this->boxCount > 1 ? ' pkg(s) x ' : ' pkg x ') . round($shipping_weight,0) . ' ' . strtolower($this->unit_weight) . ' total)');
           }
           $methods = array();
           for ($i=0; $i < sizeof($upsQuote); $i++) {
               list($type, $cost) = each($upsQuote[$i]);
               // BOF limit choices, behaviour changed from versions < 1.2
               if (!exclude_choices($type, $vendors_id)) continue;
               // EOF limit choices
               if ( $method == '' || $method == $type ) {
                   $_type = $type;

                   if ($this->timeInTransitView == "Raw") {
                     if (isset($this->servicesTimeintransit[$type])) {
                       $_type = $_type . ", ".$this->servicesTimeintransit[$type]["date"];
                     }        
                   } else {
                     if (isset($this->servicesTimeintransit[$type])) {
                       $eta_array = explode("-", $this->servicesTimeintransit[$type]["date"]);
                       $months = array (" ", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December");
                       $eta_arrival_date = $months[(int)$eta_array[1]]." ".$eta_array[2].", ".$eta_array[0];
                       $_type .= ", <acronym title='Estimated Delivery Date'>EDD</acronym>: ".$eta_arrival_date;
                     }          
                   }                    

                   // changed to make handling percentage based
                   if ($this->handling_type == "Percentage") {
                       $methods[] = array('id' => $type, 'title' => $_type, 'cost' => ((($this->handling_fee * $cost)/100) + $cost));
                   } else {
                       $methods[] = array('id' => $type, 'title' => $_type, 'cost' => ($this->handling_fee + $cost));
                   }
               }
           }
           if ($this->tax_class($vendors_id) > 0) {
          $this->quotes['tax'] = tep_get_tax_rate($this->tax_class($vendors_id), $order->delivery['country']['id'], $order->delivery['zone_id']);
           }
           $this->quotes['methods'] = $methods;
       } else {
           if ( $upsQuote != false ) {
               $errmsg = $upsQuote;
           } else {
               $errmsg = MODULE_SHIPPING_UPSXML_RATES_TEXT_UNKNOWN_ERROR;
           }
           $errmsg .= '<br>' . MODULE_SHIPPING_UPSXML_RATES_TEXT_IF_YOU_PREFER . ' ' . STORE_NAME.' via <a href="mailto:'.STORE_OWNER_EMAIL_ADDRESS.'"><u>Email</U></a>.';
           $this->quotes = array('module' => $this->title, 'error' => $errmsg);
       }
       if (tep_not_null($this->icon)) {
           $this->quotes['icon'] = tep_image($this->icon, $this->title);
       }
       return $this->quotes;
   }

   //**************
   function check($vendors_id='1') {
       if (!isset($this->_check)) {
       $check_query = tep_db_query("select configuration_value from " . TABLE_VENDOR_CONFIGURATION . " where vendors_id = '". $vendors_id ."' and configuration_key = 'MODULE_SHIPPING_UPSXML_RATES_STATUS_" . $vendors_id . "'");
           $this->_check = tep_db_num_rows($check_query);
       }
       return $this->_check;
   }

   //**************
   function install($vendors_id='1') {
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Enable UPS Shipping', 'MODULE_SHIPPING_UPSXML_RATES_STATUS_" . $vendors_id . "', 'True', 'Do you want to offer UPS shipping?', '6', '0', 'tep_cfg_select_option(array(\'True\', \'False\'), ', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added, vendors_id) values ('UPS Rates Access Key', 'MODULE_SHIPPING_UPSXML_RATES_ACCESS_KEY_" . $vendors_id . "', '', 'Enter the XML rates access key assigned to you by UPS.', '6', '1', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added, vendors_id) values ('UPS Rates Username', 'MODULE_SHIPPING_UPSXML_RATES_USERNAME_" . $vendors_id . "', '', 'Enter your UPS Services account username.', '6', '2', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added, vendors_id) values ('UPS Rates Password', 'MODULE_SHIPPING_UPSXML_RATES_PASSWORD_" . $vendors_id . "', '', 'Enter your UPS Services account password.', '6', '3', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Pickup Method', 'MODULE_SHIPPING_UPSXML_RATES_PICKUP_METHOD_" . $vendors_id . "', 'Daily Pickup', 'How do you give packages to UPS (only used when origin is US)?', '6', '4', 'tep_cfg_select_option(array(\'Daily Pickup\', \'Customer Counter\', \'One Time Pickup\', \'On Call Air Pickup\', \'Letter Center\', \'Air Service Center\', \'Suggested Retail Rates (UPS Store)\'), ', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Packaging Type', 'MODULE_SHIPPING_UPSXML_RATES_PACKAGE_TYPE_" . $vendors_id . "', 'Package', 'What kind of packaging do you use?', '6', '5', 'tep_cfg_select_option(array(\'Customer Package\', \'UPS Letter\', \'UPS Tube\', \'UPS Pak\', \'UPS Express Box\', \'UPS 25kg Box\', \'UPS 10kg box\'), ', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added, vendors_id) values ('Customer Classification Code', 'MODULE_SHIPPING_UPSXML_RATES_CUSTOMER_CLASSIFICATION_CODE_" . $vendors_id . "', '01', '01 - If you are billing to a UPS account and have a daily UPS pickup, 03 - If you do not have a UPS account or you are billing to a UPS account but do not have a daily pickup, 04 - If you are shipping from a retail outlet', '6', '6', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Shipping Origin', 'MODULE_SHIPPING_UPSXML_RATES_ORIGIN_" . $vendors_id . "', 'US Origin', 'What origin point should be used (this setting affects only what UPS product names are shown to the user)', '6', '7', 'tep_cfg_select_option(array(\'US Origin\', \'Canada Origin\', \'European Union Origin\', \'Puerto Rico Origin\', \'Mexico Origin\', \'All other origins\'), ', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added, vendors_id) values ('Origin City', 'MODULE_SHIPPING_UPSXML_RATES_CITY_" . $vendors_id . "', '', 'Enter the name of the origin city.', '6', '8', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added, vendors_id) values ('Origin State/Province', 'MODULE_SHIPPING_UPSXML_RATES_STATEPROV_" . $vendors_id . "', '', 'Enter the two-letter code for your origin state/province.', '6', '9', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added, vendors_id) values ('Origin Country', 'MODULE_SHIPPING_UPSXML_RATES_COUNTRY_" . $vendors_id . "', '', 'Enter the two-letter code for your origin country.', '6', '10', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added, vendors_id) values ('Origin Zip/Postal Code', 'MODULE_SHIPPING_UPSXML_RATES_POSTALCODE_" . $vendors_id . "', '', 'Enter your origin zip/postcode.', '6', '11', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Test or Production Mode', 'MODULE_SHIPPING_UPSXML_RATES_MODE_" . $vendors_id . "', 'Test', 'Use this module in Test or Production mode?', '6', '12', 'tep_cfg_select_option(array(\'Test\', \'Production\'), ', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Unit Weight', 'MODULE_SHIPPING_UPSXML_RATES_UNIT_WEIGHT_" . $vendors_id . "', 'LBS', 'By what unit are your packages weighed?', '6', '13', 'tep_cfg_select_option(array(\'LBS\', \'KGS\'), ', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Unit Length', 'MODULE_SHIPPING_UPSXML_RATES_UNIT_LENGTH_" . $vendors_id . "', 'IN', 'By what unit are your packages sized?', '6', '14', 'tep_cfg_select_option(array(\'IN\', \'CM\'), ', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Dimensions Support', 'MODULE_SHIPPING_UPSXML_DIMENSIONS_SUPPORT_" . $vendors_id . "', 'No', 'Do you use the additional dimensions support (read dimensions.txt in the package)?', '6', '23', 'tep_cfg_select_option(array(\'No\', \'Ready-to-ship only\', \'With product dimensions\'), ', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Quote Type', 'MODULE_SHIPPING_UPSXML_RATES_QUOTE_TYPE_" . $vendors_id . "', 'Commercial', 'Quote for Residential or Commercial Delivery', '6', '15', 'tep_cfg_select_option(array(\'Commercial\', \'Residential\'), ', now(), '" . $vendors_id . "')");
       // next two keys added to be able to use negotiated rates (available from UPS since about July 2006)
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Negotiated rates', 'MODULE_SHIPPING_UPSXML_RATES_USE_NEGOTIATED_RATES_" . $vendors_id . "', 'False', 'Do you receive discounted rates from UPS and want to use these for shipping quotes? <b>Note:</b>  You need to enter your UPS account number below.', '6', '25', 'tep_cfg_select_option(array(\'True\', \'False\'), ', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added, vendors_id) values ('UPS Account Number', 'MODULE_SHIPPING_UPSXML_RATES_UPS_ACCOUNT_NUMBER_" . $vendors_id . "', '', 'Enter your UPS Account number when you have and want to use negotiated rates.', '6', '26', now(), '" . $vendors_id . "')");				
       // added for handling type selection
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Handling Type', 'MODULE_SHIPPING_UPSXML_HANDLING_TYPE_" . $vendors_id . "', 'Flat Fee', 'Handling type for this shipping method.', '6', '16', 'tep_cfg_select_option(array(\'Flat Fee\', \'Percentage\'), ', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added, vendors_id) values ('Handling Fee', 'MODULE_SHIPPING_UPSXML_RATES_HANDLING_" . $vendors_id . "', '0', 'Handling fee for this shipping method.', '6', '16', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added, vendors_id) values ('UPS Currency Code', 'MODULE_SHIPPING_UPSXML_CURRENCY_CODE_" . $vendors_id . "', '', 'Enter the 3 letter currency code for your country of origin. United States (USD)', '6', '2', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Enable Insurance', 'MODULE_SHIPPING_UPSXML_INSURE_" . $vendors_id . "', 'True', 'Do you want to insure packages shipped by UPS?', '6', '22', 'tep_cfg_select_option(array(\'True\', \'False\'), ', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, use_function, set_function, date_added, vendors_id) values ('Tax Class', 'MODULE_SHIPPING_UPSXML_RATES_TAX_CLASS_" . $vendors_id . "', '0', 'Use the following tax class on the shipping fee.', '6', '17', 'tep_get_tax_class_title', 'tep_cfg_pull_down_tax_classes(', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, use_function, set_function, date_added, vendors_id) values ('Shipping Zone', 'MODULE_SHIPPING_UPSXML_RATES_ZONE_" . $vendors_id . "', '0', 'If a zone is selected, only enable this shipping method for that zone.', '6', '18', 'tep_get_zone_class_title', 'tep_cfg_pull_down_zone_classes(', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added, vendors_id) values ('Sort order of display.', 'MODULE_SHIPPING_UPSXML_RATES_SORT_ORDER_" . $vendors_id . "', '0', 'Sort order of display. Lowest is displayed first.', '6', '19', now(), '" . $vendors_id . "')");
       // add key for disallowed shipping methods
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " ( configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Disallowed Shipping Methods', 'MODULE_SHIPPING_UPSXML_TYPES_" . $vendors_id . "', '', 'Select the UPS services <span style=\'color: red; font-weight: bold\'>not</span> to be offered.', '6', '20', 'tep_cfg_select_multioption(array(\'Next Day Air\', \'2nd Day Air\', \'Ground\', \'Worldwide Express\', \'Worldwide Express Plus\', \'Worldwide Expedited\', \'Express\', \'Standard\', \'3 Day Select\', \'Next Day Air Saver\', \'Next Day Air Early A.M.\', \'Expedited\', \'2nd Day Air A.M.\', \'Saver\', \'Express Early A.M.\', \'Express Plus\'), ',  now(), '" . $vendors_id . "')");
       // add key for shipping delay
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " ( configuration_id, configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, last_modified, date_added, use_function, set_function, vendors_id) values ('', 'Shipping Delay', 'SHIPPING_DAYS_DELAY_" . $vendors_id . "', '1', 'How many days from when an order is placed to when you ship it (Decimals are allowed). Arrival date estimations are based on this value.', '6', '21', NULL, now(), '" . $vendors_id . "', NULL, NULL)");
       // add key for enabling email error messages
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Email UPS errors', 'MODULE_SHIPPING_UPSXML_EMAIL_ERRORS_" . $vendors_id . "', 'Yes', 'Do you want to receive UPS errors by email?', '6', '24', 'tep_cfg_select_option(array(\'Yes\', \'No\'), ', now(), '" . $vendors_id . "')");
       // add key for time in transit view type
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Time in Transit View Type', 'MODULE_SHIPPING_UPSXML_RATES_TIME_IN_TRANSIT_VIEW_" . $vendors_id . "', 'Raw', 'How the module should display the time in transit to the customer.', '6', '16', 'tep_cfg_select_option(array(\'Raw\', \'Detailed\'), ', now(), '" . $vendors_id . "')");
   }

   //****************
   function remove($vendors_id) {
     tep_db_query("delete from " . TABLE_VENDOR_CONFIGURATION . " where vendors_id = '". $vendors_id ."' and configuration_key in ('" . implode("', '", $this->keys($vendors_id)) . "')");
   }

   //*************
   function keys($vendors_id) {
       // add MODULE_SHIPPING_UPSXML_TYPES to end of array for selectable shipping methods
       return array('MODULE_SHIPPING_UPSXML_RATES_STATUS_' . $vendors_id,
                    'MODULE_SHIPPING_UPSXML_RATES_ACCESS_KEY_' . $vendors_id,
                    'MODULE_SHIPPING_UPSXML_RATES_USERNAME_' . $vendors_id,
                    'MODULE_SHIPPING_UPSXML_RATES_PASSWORD_' . $vendors_id,
                    'MODULE_SHIPPING_UPSXML_RATES_PICKUP_METHOD_' . $vendors_id,
                    'MODULE_SHIPPING_UPSXML_RATES_PACKAGE_TYPE_' . $vendors_id,
                    'MODULE_SHIPPING_UPSXML_RATES_CUSTOMER_CLASSIFICATION_CODE_' . $vendors_id,
                    'MODULE_SHIPPING_UPSXML_RATES_ORIGIN_' . $vendors_id,
                    'MODULE_SHIPPING_UPSXML_RATES_CITY_' . $vendors_id,
                    'MODULE_SHIPPING_UPSXML_RATES_STATEPROV_' . $vendors_id,
                    'MODULE_SHIPPING_UPSXML_RATES_COUNTRY_' . $vendors_id,
                    'MODULE_SHIPPING_UPSXML_RATES_POSTALCODE_' . $vendors_id,
                    'MODULE_SHIPPING_UPSXML_RATES_MODE_' . $vendors_id,
                    'MODULE_SHIPPING_UPSXML_RATES_UNIT_WEIGHT_' . $vendors_id,
                    'MODULE_SHIPPING_UPSXML_RATES_UNIT_LENGTH_' . $vendors_id,
	     'MODULE_SHIPPING_UPSXML_DIMENSIONS_SUPPORT_' . $vendors_id,
                    'MODULE_SHIPPING_UPSXML_RATES_QUOTE_TYPE_' . $vendors_id,
	     'MODULE_SHIPPING_UPSXML_RATES_USE_NEGOTIATED_RATES_' . $vendors_id,
	     'MODULE_SHIPPING_UPSXML_RATES_UPS_ACCOUNT_NUMBER_' . $vendors_id,
	     'MODULE_SHIPPING_UPSXML_HANDLING_TYPE_' . $vendors_id,
	     'MODULE_SHIPPING_UPSXML_RATES_HANDLING_' . $vendors_id,
	     'MODULE_SHIPPING_UPSXML_INSURE_' . $vendors_id,
	     'MODULE_SHIPPING_UPSXML_CURRENCY_CODE_' . $vendors_id,
	     'MODULE_SHIPPING_UPSXML_RATES_TAX_CLASS_' . $vendors_id,
	     'MODULE_SHIPPING_UPSXML_RATES_ZONE_' . $vendors_id,
	     'MODULE_SHIPPING_UPSXML_RATES_SORT_ORDER_' . $vendors_id,
	     'MODULE_SHIPPING_UPSXML_TYPES_' . $vendors_id,
	     'SHIPPING_DAYS_DELAY_' . $vendors_id,
	     'MODULE_SHIPPING_UPSXML_EMAIL_ERRORS_' . $vendors_id,
	     'MODULE_SHIPPING_UPSXML_RATES_TIME_IN_TRANSIT_VIEW_' . $vendors_id,);
   }

   //***********************
   function _upsProduct($prod, $vendors_id){
       $this->_upsProductCode = $prod;
   }

   //**********************************************
   function _upsOrigin($city, $stateprov, $country, $postal){
       $this->_upsOriginCity = $city;
       $this->_upsOriginStateProv = $stateprov;
       $this->_upsOriginCountryCode = $country;
       $postal = str_replace(' ', '', $postal);
       if ($country == 'US') {
           $this->_upsOriginPostalCode = substr($postal, 0, 5);
       } else {
           $this->_upsOriginPostalCode = $postal;
       }
   }

   //**********************************************
   function _upsDest($city, $stateprov, $country, $postal) {
       $this->_upsDestCity = $city;
       $this->_upsDestStateProv = $stateprov;
       $this->_upsDestCountryCode = $country;
       $postal = str_replace(' ', '', $postal);
       if ($country == 'US') {
           $this->_upsDestPostalCode = substr($postal, 0, 5);
       } else {
           $this->_upsDestPostalCode = $postal;
       }
   }

   //************************
   function _upsAction($action, $vendors_id) {
       // rate - Single Quote; shop - All Available Quotes
       $this->_upsActionCode = $action;
   }

   //********************************************
	// default value of 100 added for insurance (100 shouldn't trigger costs for insurance)
   function _addItem($length, $width, $height, $weight, $price = 0 ) {
       // Add box or item to shipment list. Round weights to 1 decimal places.
       if ((float)$weight < 1.0) {
           $weight = 1;
       } else {
           $weight = round($weight, 1);
       }
       $index = $this->items_qty;
       $this->item_length[$index] = ($length ? (string)$length : '0' );
       $this->item_width[$index] = ($width ? (string)$width : '0' );
       $this->item_height[$index] = ($height ? (string)$height : '0' );
       $this->item_weight[$index] = ($weight ? (string)$weight : '0' );
			$this->item_price[$index] = $price;
       $this->items_qty++;
   }

   //********************
   function getPackagesByVol() {
       $packages = array();
       $packages_query = tep_db_query("select *, (package_length * package_width * package_height) as volume from " . TABLE_PACKAGING . " order by volume;");
			$counter = 0;
       while ($package = tep_db_fetch_array($packages_query)) {
           $packages[] = array(
           'id' => $package['package_id'],
           'name' => $package['package_name'],
           'description' => $package['package_description'],
           'length' => $package['package_length'],
           'width' => $package['package_width'],
           'height' => $package['package_height'],
           'empty_weight' => $package['package_empty_weight'],
           'max_weight' => $package['package_max_weight'],
           'volume' => $package['volume']);
// sort dimensions from low to high, used in the function fitsInBox
           $dimensions = array($package['package_length'], $package['package_width'], $package['package_height']);
           sort($dimensions);
					foreach($dimensions as $key => $value) {
						if ($key == 0 ) { $packages[$counter]['x'] = $value; }
						if ($key == 1 ) { $packages[$counter]['y'] = $value; }
						if ($key == 2 ) { $packages[$counter]['z'] = $value; }
					}
					$counter++;
       }
       return $packages;
   }

   //********************************
   function packProducts($productsArray) {
       $definedPackages = $this->getPackagesByVol();
	/*		echo '<pre>';
			print_r($definedPackages);
			exit; */
       $emptyBoxesArray = array();
       for ($i = 0; $i < count($definedPackages); $i++) {
           $definedBox = $definedPackages[$i];
           $definedBox['remaining_volume'] = $definedBox['volume'];
           $definedBox['current_weight'] = $definedBox['empty_weight'];
           $emptyBoxesArray[] = $definedBox;
       }
         if (count($emptyBoxesArray) == 0) {
            print("ERROR: No boxes to ship unpackaged product<br />\n");
            break;
         }

       $packedBoxesArray = array();
       $currentBox = NULL;
       $index_of_largest_box = count($emptyBoxesArray)-1;
       // Get the product array and expand multiple qty items.
       $productsRemaining = array();
       for ($i = 0; $i < count($productsArray); $i++) {
           $product = $productsArray[$i];
           // sanity checks on the product, should not be done for ready-to-ship times
           if ((int)$product['ready_to_ship'] == 0) {
					  $product['ready_to_ship'] = '1';
					  for ($x = 0; $x <= $index_of_largest_box; $x++) {
						  if ($this->fitsInBox($product, $emptyBoxesArray[$x])) {
                 $product['ready_to_ship'] = '0';
                 $product['largest_box_it_will_fit'] = $x;
               } 
					  } // end for ($x = 0; $x <= $index_of_largest_box; $x++) 
           } // end if ($product['ready_to_ship'] == '0')

           for ($j = 0; $j < $productsArray[$i]['quantity']; $j++) {
               $productsRemaining[] = $product;
           }
       } // end for ($i = 0; $i < count($productsArray); $i++)
       // make sure the products that did not fit the largest box and are now set as ready-to-ship
       // are out of the way as soon as possible
       usort($productsRemaining, ready_to_shipCmp);
       // Worst case, you'll need as many boxes as products ordered.
       $index_of_largest_box_to_use = count($emptyBoxesArray) -1;
       while (count($productsRemaining)) {
           // Immediately set aside products that are already packed and ready.
           if ($productsRemaining[0]['ready_to_ship'] == '1') {
               $packedBoxesArray[] = array (
               'length' => $productsRemaining[0]['length'],
               'width' => $productsRemaining[0]['width'],
               'height' => $productsRemaining[0]['height'],
               'current_weight' => $productsRemaining[0]['weight'],
               'price' => $productsRemaining[0]['final_price']);
               $productsRemaining = array_slice($productsRemaining, 1);
               continue;
           }
           // Cycle through boxes, increasing box size if all doesn't fit.
           // but if the remaining products only fit in a box of smaller size, use that one to pack it away
           for ($b = 0; $b < count($emptyBoxesArray) && tep_not_null($productsRemaining); $b++) {
               $result = $this->fitProductsInBox($productsRemaining, $emptyBoxesArray[$b], $packedBoxesArray, $b, $index_of_largest_box_to_use);
               $packedBoxesArray = $result['packed_boxes'];
               $productsRemaining = $result['remaining'];
               if (isset($result['index_of_largest_box_to_use']) && $result['index_of_largest_box_to_use'] >= 0 ) {
                 $index_of_largest_box_to_use = $result['index_of_largest_box_to_use'];
               }
           }
       } // end while

       return $packedBoxesArray;
   }

   //*****************************
   function fitsInBox($product, $box) {
       // in case by accident or by choice length, width or height is not set
       // we will estimate it by using a set density and the product['weight'] variable
       // will only be used in the check for whether it fits the largest box
       // after that it will already be set, if product['weight'] is set at least
			if ($product['x'] > $box['x'] || $product['y'] > $box['y'] || $product['z'] > $box['z']) {
           return false;
       } 

       if ($product['volume'] <= $box['remaining_volume']) {
           if ($box['max_weight'] == 0 || ($box['current_weight'] + $product['weight'] <= $box['max_weight'])) {
               return true;
           }
       }
       return false;
   }

   //***********************************
   function putProductInBox($product, $box) {
       $box['remaining_volume'] -= $product['volume'];
       $box['products'][] = $product;
       $box['current_weight'] += $product['weight'];
			$box['price'] += $product['final_price'];
       return $box;
   } 
   //*********************    
   function fitProductsInBox($productsRemaining, $emptyBox, $packedBoxesArray, $box_no, $index_of_largest_box) { 
       $currentBox = $emptyBox;
       $productsRemainingSkipped = array();
       $productsRemainingNotSkipped = array();
       $largest_box_in_skipped_products = -1;
       // keep apart products that will not fit this box anyway
       for ($p = 0; $p < count($productsRemaining); $p++) {
         if ($productsRemaining[$p]['largest_box_it_will_fit'] < $box_no) {
           $productsRemainingSkipped[] = $productsRemaining[$p];
           // check on skipped products: if they will not fit in the largest box
           // the $index_of_largest_box should be the one they *will* fit
           // otherwise the packing algorithm gets stuck in a loop
           if ($productsRemaining[$p]['largest_box_it_will_fit'] > $largest_box_in_skipped_products) {
             $largest_box_in_skipped_products = $productsRemaining[$p]['largest_box_it_will_fit'];
           }
         } else {
           $productsRemainingNotSkipped[] = $productsRemaining[$p];
         }
       }

       unset($productsRemaining);
       $productsRemaining = $productsRemainingNotSkipped;
       unset($productsRemainingNotSkipped);
       if (count($productsRemaining) == 0) {
         // products remaining are the ones that will not fit this box (productsRemaimingSkipped)
           $result_array = array('remaining' => $productsRemainingSkipped, 'box_no' => $box_no, 'packed_boxes' => $packedBoxesArray, 'index_of_largest_box_to_use' => $largest_box_in_skipped_products);
           return ($result_array);
       }

       //Try to fit each product that can fit in box
       for ($p = 0; $p < count($productsRemaining); $p++) {
           if ($this->fitsInBox($productsRemaining[$p], $currentBox)) {
               //It fits. Put it in the box.
               $currentBox = $this->putProductInBox($productsRemaining[$p], $currentBox);
               if ($p == count($productsRemaining) - 1) {
                   $packedBoxesArray[] = $currentBox;
                   $productsRemaining = array_slice($productsRemaining, $p + 1);
                   $productsRemaining = array_merge($productsRemaining, $productsRemainingSkipped);

                   $result_array = array('remaining' => $productsRemaining, 'box_no' => $box_no, 'packed_boxes' => $packedBoxesArray);
                   return ($result_array);
               }
           } else {
               if ($box_no == $index_of_largest_box) {
                   //We're at the largest box already, and it's full. Keep what we've packed so far and get another box.
                   $packedBoxesArray[] = $currentBox;
                   $productsRemaining = array_slice($productsRemaining, $p);
                   $productsRemaining = array_merge($productsRemaining, $productsRemainingSkipped);
                   $result_array = array('remaining' => $productsRemaining, 'box_no' => $box_no, 'packed_boxes' => $packedBoxesArray);
                   return ($result_array);
               }
               // Not all of them fit. Stop packing remaining products and try next box.
               $productsRemaining = array_merge($productsRemaining, $productsRemainingSkipped);
               $result_array = array('remaining' => $productsRemaining, 'box_no' => $box_no, 'packed_boxes' => $packedBoxesArray);
               return ($result_array);
           } // end else
       } // end for ($p = 0; $p < count($productsRemaining); $p++)
   } // end function

   //*********************
   function _upsGetQuote($vendors_id='1') {

       // Create the access request
       $accessRequestHeader =
       "<?xml version=\"1.0\"?>\n".
       "<AccessRequest xml:lang=\"en-US\">\n".
       "   <AccessLicenseNumber>". $this->access_key($vendors_id) ."</AccessLicenseNumber>\n".
       "   <UserId>". $this->access_username($vendors_id) ."</UserId>\n".
       "   <Password>". $this->access_password($vendors_id) ."</Password>\n".
       "</AccessRequest>\n";

       $ratingServiceSelectionRequestHeader =
       "<?xml version=\"1.0\"?>\n".
       "<RatingServiceSelectionRequest xml:lang=\"en-US\">\n".
       "   <Request>\n".
       "       <TransactionReference>\n".
       "           <CustomerContext>Rating and Service</CustomerContext>\n".
       "           <XpciVersion>". $this->xpci_version ."</XpciVersion>\n".
       "       </TransactionReference>\n".
       "       <RequestAction>Rate</RequestAction>\n".
       "       <RequestOption>shop</RequestOption>\n".
       "   </Request>\n";
       // according to UPS the CustomerClassification and PickupType containers should
       // not be present when the origin country is non-US see:
       // http://forums.oscommerce.com/index.php?s=&...st&p=730947
       if ($this->origin_country == 'US') {
       $ratingServiceSelectionRequestHeader .=
       "   <PickupType>\n".
       "       <Code>". $this->pickup_methods[$this->pickup_method($vendors_id)] ."</Code>\n".
       "   </PickupType>\n";
       "   <CustomerClassification>\n".
       "       <Code>". $this->customer_classification ."</Code>\n".
       "   </CustomerClassification>\n";
       }
       $ratingServiceSelectionRequestHeader .=
       "   <Shipment>\n".
       "       <Shipper>\n";
       if ($this->use_negotiated_rates == 'True') {
       $ratingServiceSelectionRequestHeader .=
       "         <ShipperNumber>" . $this->access_account_number . "</ShipperNumber>\n";
			}
       $ratingServiceSelectionRequestHeader .=
       "           <Address>\n".
       "               <City>". $this->_upsOriginCity ."</City>\n".
       "               <StateProvinceCode>". $this->_upsOriginStateProv ."</StateProvinceCode>\n".
       "               <CountryCode>". $this->_upsOriginCountryCode ."</CountryCode>\n".
       "               <PostalCode>". $this->_upsOriginPostalCode ."</PostalCode>\n".
       "           </Address>\n".
       "       </Shipper>\n".
       "       <ShipTo>\n".
       "           <Address>\n".
       "               <City>". $this->_upsDestCity ."</City>\n".
       "               <StateProvinceCode>". $this->_upsDestStateProv ."</StateProvinceCode>\n".
       "               <CountryCode>". $this->_upsDestCountryCode ."</CountryCode>\n".
       "               <PostalCode>". $this->_upsDestPostalCode ."</PostalCode>\n".
       ($this->quote_type($vendors_id) == "Residential" ? "<ResidentialAddressIndicator/>\n" : "") .
       "           </Address>\n".
       "       </ShipTo>\n";
       for ($i = 0; $i < $this->items_qty; $i++) {

           $ratingServiceSelectionRequestPackageContent .=
           "       <Package>\n".
           "           <PackagingType>\n".
           "               <Code>". $this->package_types[$this->package_type($vendors_id)] ."</Code>\n".
           "           </PackagingType>\n";
           if ($this->dimensions_support > 0 && ($this->item_length[$i] > 0 ) && ($this->item_width[$i] > 0 ) && ($this->item_height[$i] > 0)) {

               $ratingServiceSelectionRequestPackageContent .=
               "           <Dimensions>\n".
               "               <UnitOfMeasurement>\n".
               "                   <Code>". $this->unit_length($vendors_id) ."</Code>\n".
               "               </UnitOfMeasurement>\n".
               "               <Length>". $this->item_length[$i] ."</Length>\n".
               "               <Width>". $this->item_width[$i] ."</Width>\n".
               "               <Height>". $this->item_height[$i] ."</Height>\n".
               "           </Dimensions>\n";
           }

           $ratingServiceSelectionRequestPackageContent .=
           "           <PackageWeight>\n".
           "               <UnitOfMeasurement>\n".
           "                   <Code>". $this->unit_weight($vendors_id) ."</Code>\n".
           "               </UnitOfMeasurement>\n".
           "               <Weight>". $this->item_weight[$i] ."</Weight>\n".
           "           </PackageWeight>\n".
           "           <PackageServiceOptions>\n".
           //"               <COD>\n".
           //"                   <CODFundsCode>0</CODFundsCode>\n".
           //"                   <CODCode>3</CODCode>\n".
           //"                   <CODAmount>\n".
           //"                       <CurrencyCode>USD</CurrencyCode>\n".
           //"                       <MonetaryValue>1000</MonetaryValue>\n".
           //"                   </CODAmount>\n".
           //"               </COD>\n".
           "               <InsuredValue>\n".
           "                   <CurrencyCode>".MODULE_SHIPPING_UPSXML_CURRENCY_CODE."</CurrencyCode>\n".
           "                   <MonetaryValue>".$this->item_price[$i]."</MonetaryValue>\n".
           "               </InsuredValue>\n".
           "           </PackageServiceOptions>\n".
           "       </Package>\n";
       }

       $ratingServiceSelectionRequestFooter = '';
       //"   <ShipmentServiceOptions/>\n".
          if ($this->use_negotiated_rates == 'True') {
       $ratingServiceSelectionRequestFooter .=
           "       <RateInformation>\n".
           "         <NegotiatedRatesIndicator/>\n".
           "       </RateInformation>\n";
          }
       $ratingServiceSelectionRequestFooter .=
       "   </Shipment>\n";
       // according to UPS the CustomerClassification and PickupType containers should
       // not be present when the origin country is non-US see:
       // http://forums.oscommerce.com/index.php?s=&...st&p=730947
       if ($this->origin_country == 'US') {
       $ratingServiceSelectionRequestFooter .=
             "   <CustomerClassification>\n".
       "       <Code>". $this->customer_classification($vendors_id) ."</Code>\n".
             "   </CustomerClassification>\n";
       }
       $ratingServiceSelectionRequestFooter .=
       "</RatingServiceSelectionRequest>\n";

       $xmlRequest = $accessRequestHeader .
       $ratingServiceSelectionRequestHeader .
       $ratingServiceSelectionRequestPackageContent .
       $ratingServiceSelectionRequestFooter;

                               $host = $this->host($vendors_id);

       //post request $strXML;
       $xmlResult = $this->_post($this->protocol, $host, $this->port, $this->path, $this->version, $this->timeout, $xmlRequest);
       // BOF testing with a response from UPS saved as a text file
       // needs commenting out the line above: $xmlResult = $this->_post($this->protocol, etcetera
/*        $filename = '/Library/WebServer/Documents/nytest/includes/modules/shipping/example_response.xml';
       $fp = fopen($filename, "r") or die("couldn't open file");
       $xmlResult = "";
       while (! feof($fp)) {
         $xmlResult .= fgets($fp, 1024);
       } 
       // EOF testing with a text file */
       return $this->_parseResult($xmlResult, $vendors_id);
   }

   //******************************************************************
   function _post($protocol, $host, $port, $path, $version, $timeout, $xmlRequest) {
       $url = $protocol."://".$host.":".$port.$path;
       if ($this->logfile) {
           error_log("------------------------------------------\n", 3, $this->logfile);
           error_log("DATE AND TIME: ".date('Y-m-d H:i:s')."\n", 3, $this->logfile);
           error_log("UPS URL: " . $url . "\n", 3, $this->logfile);
       }
       if (function_exists('exec') && $this->use_exec == '1' ) {
           exec('which curl', $curl_output);
           if ($curl_output) {
               $curl_path = $curl_output[0];
           } else {
               $curl_path = 'curl'; // change this if necessary
           }
           if ($this->logfile) {
               error_log("UPS REQUEST using exec(): " . $xmlRequest . "\n", 3, $this->logfile);
           }
           // add option -k to the statement: $command = "".$curl_path." -k -d \"". etcetera if you get
           // curl error 60: error setting certificate verify locations
           // using addslashes was the only way to avoid UPS returning the 1001 error: The XML document is not well formed
           $command = "".$curl_path." -d \"".addslashes($xmlRequest)."\" ".$url."";
           exec($command, $xmlResponse);
           if ( empty($xmlResponse) && $this->logfile) { // using exec no curl errors can be retrieved
               error_log("Error from cURL using exec() since there is no \$xmlResponse\n", 3, $this->logfile);
           }
           if ($this->logfile) {
               error_log("UPS RESPONSE using exec(): " . $xmlResponse[0] . "\n", 3, $this->logfile);
           }
       } elseif ($this->use_exec == '1') { // if NOT (function_exists('exec') && $this->use_exec == '1'
           if ($this->logfile) {
               error_log("Sorry, exec() cannot be called\n", 3, $this->logfile);
           }
       } else { // default behavior: cURL is assumed to be compiled in PHP
           $ch = curl_init();
           curl_setopt($ch, CURLOPT_URL, $url);
           // uncomment the next line if you get curl error 60: error setting certificate verify locations
           //curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
           // uncommenting the next line is most likely not necessary in case of error 60
           // curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
           curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
           curl_setopt($ch, CURLOPT_HEADER, 0);
           curl_setopt($ch, CURLOPT_POST, 1);
           curl_setopt($ch, CURLOPT_POSTFIELDS, $xmlRequest);
           curl_setopt($ch, CURLOPT_TIMEOUT, (int)$timeout);

           if ($this->logfile) {
               error_log("UPS REQUEST: " . $xmlRequest . "\n", 3, $this->logfile);
           }
           $xmlResponse = curl_exec ($ch);
           if (curl_errno($ch) && $this->logfile) {
               $error_from_curl = sprintf('Error [%d]: %s', curl_errno($ch), curl_error($ch));
               error_log("Error from cURL: " . $error_from_curl . "\n", 3, $this->logfile);
           }
           // send email if enabled in the admin section
           if (curl_errno($ch) && $this->email_errors) {
               $error_from_curl = sprintf('Error [%d]: %s', curl_errno($ch), curl_error($ch));
               error_log("Error from cURL: " . $error_from_curl . " experienced by customer with id " . $_SESSION['customer_id'] . " on " . date('Y-m-d H:i:s'), 1, STORE_OWNER_EMAIL_ADDRESS);
           }
           // log errors to file ups_error.log when set
           if (curl_errno($ch) && $this->ups_error_file) {
               $error_from_curl = sprintf('Error [%d]: %s', curl_errno($ch), curl_error($ch));
               error_log(date('Y-m-d H:i:s')."\tcURL\t" . $error_from_curl . "\t" . $_SESSION['customer_id']."\n", 3, $this->ups_error_file);    
           }
           if ($this->logfile) {
               error_log("UPS RESPONSE: " . $xmlResponse . "\n", 3, $this->logfile);
           }
           curl_close ($ch);
       }

       if(!$xmlResponse || strstr(strtolower(substr($xmlResponse, 0, 120)), "bad request"))  {
           /* Sometimes the UPS server responds with an HTML message (differing depending on whether the test server
              or the production server is used) but both have in the title tag "Bad request".
              Parsing this response will result in a fatal error:
              Call to a member function on a non-object in /blabla/includes/classes/xmldocument.php on line 57
              It only results in not showing Estimated Delivery Dates to the customer so avoiding the fatal error should do.
           */
           $xmlResponse = "<?xml version=\"1.0\"?>\n".
           "<RatingServiceSelectionResponse>\n".
           "   <Response>\n".
           "       <TransactionReference>\n".
           "           <CustomerContext>Rating and Service</CustomerContext>\n".
           "           <XpciVersion>1.0001</XpciVersion>\n".
           "       </TransactionReference>\n".
           "       <ResponseStatusCode>0</ResponseStatusCode>\n".
           "       <ResponseStatusDescription>". MODULE_SHIPPING_UPSXML_RATES_TEXT_COMM_UNKNOWN_ERROR ."</ResponseStatusDescription>\n".
           "   </Response>\n".
           "</RatingServiceSelectionResponse>\n";
           return $xmlResponse;
       }
       if ($this->use_exec == '1') {
           return $xmlResponse[0]; // $xmlResponse is an array in this case
       } else {
           return $xmlResponse;
       }
   }

   //*****************************
   function _parseResult($xmlResult, $vendors_id) {
       // Parse XML message returned by the UPS post server.
       $doc = new XMLDocument();
       $xp = new XMLParser();
       $xp->setDocument($doc);
       $xp->parse($xmlResult);
       $doc = $xp->getDocument();
       // Get version. Must be xpci version 1.0001 or this might not work.
       $responseVersion = $doc->getValueByPath('RatingServiceSelectionResponse/Response/TransactionReference/XpciVersion');
       if ($this->xpci_version != $responseVersion) {
           $message = MODULE_SHIPPING_UPSXML_RATES_TEXT_COMM_VERSION_ERROR;
           return $message;
       }
       // Get response code. 1 = SUCCESS, 0 = FAIL
       $responseStatusCode = $doc->getValueByPath('RatingServiceSelectionResponse/Response/ResponseStatusCode');
       if ($responseStatusCode != '1') {
           $errorMsg = $doc->getValueByPath('RatingServiceSelectionResponse/Response/Error/ErrorCode');
           $errorMsg .= ": ";
           $errorMsg .= $doc->getValueByPath('RatingServiceSelectionResponse/Response/Error/ErrorDescription');
           // send email if enabled in the admin section
           if ($this->email_errors) {
               error_log("UPSXML Rates Error: " . $errorMsg . " experienced by customer with id " . $_SESSION['customer_id'] . " on " . date('Y-m-d H:i:s'), 1, STORE_OWNER_EMAIL_ADDRESS);
           }
           // log errors to file ups_error.log when set
           if ($this->ups_error_file) {
               error_log(date('Y-m-d H:i:s')."\tRates\t" . $errorMsg . "\t" . $_SESSION['customer_id']."\n", 3, $this->ups_error_file);    
           }
               return $errorMsg;
       }
       $root = $doc->getRoot();
       $ratedShipments = $root->getElementsByName("RatedShipment");
       $aryProducts = false;
       for ($i = 0; $i < count($ratedShipments); $i++) {
           $serviceCode = $ratedShipments[$i]->getValueByPath("/Service/Code");
					if ($this->use_negotiated_rates == 'True') {
						$totalCharge = $ratedShipments[$i]->getValueByPath("/NegotiatedRates/NetSummaryCharges/GrandTotal/MonetaryValue");
               if (!($serviceCode && $totalCharge)) {
               continue;
               }
					} else {
             $totalCharge = $ratedShipments[$i]->getValueByPath("/TotalCharges/MonetaryValue");
               if (!($serviceCode && $totalCharge)) {
               continue;
               }
					}
           $ratedPackages = $ratedShipments[$i]->getElementsByName("RatedPackage");
           $this->boxCount = count($ratedPackages);
           $gdaysToDelivery = $ratedShipments[$i]->getValueByPath("/GuaranteedDaysToDelivery");
           $scheduledTime = $ratedShipments[$i]->getValueByPath("/ScheduledDeliveryTime");
           $title = '';
           $title = $this->service_codes[$this->origin($vendors_id)][$serviceCode];

           /* we don't want to use this, it may conflict with time estimation
                 if ($gdaysToDelivery) {
                     $title .= ' (';
                     $title .= $gdaysToDelivery . " Business Days";
                     if ($scheduledTime) {
                         $title .= ' @ ' . $scheduledTime;
                     }
                     $title .= ')';
                 } elseif ($this->timeintransit > 0) {
                     $title .= ' (';
                     $title .= $this->timeintransit . " Business Days";
                     $title .= ')';
                 }
           */
           $aryProducts[$i] = array($title => $totalCharge);
       }
       return $aryProducts;
   }

   // BOF Time In Transit

   // GM 11-15-2004: renamed from _upsGetTime()

   //********************
   function _upsGetTimeServices($vendors_id) {

     if (constant('SHIPPING_DAYS_DELAY_' . $vendors_id) > 0) {
       $shipdate = date("Ymd", time()+(86400*constant('SHIPPING_DAYS_DELAY_' . $vendors_id)));
       } else {
           $shipdate = $this->today;
       }

       // Create the access request
       $accessRequestHeader =
       "<?xml version=\"1.0\"?>\n".
       "<AccessRequest xml:lang=\"en-US\">\n".
       "   <AccessLicenseNumber>". $this->access_key($vendors_id) ."</AccessLicenseNumber>\n".
       "   <UserId>". $this->access_username($vendors_id) ."</UserId>\n".
       "   <Password>". $this->access_password($vendors_id) ."</Password>\n".
       "</AccessRequest>\n";

       $timeintransitSelectionRequestHeader =
       "<?xml version=\"1.0\"?>\n".
       "<TimeInTransitRequest xml:lang=\"en-US\">\n".
       "   <Request>\n".
       "       <TransactionReference>\n".
       "           <CustomerContext>Time in Transit</CustomerContext>\n".
       "           <XpciVersion>". $this->transitxpci_version ."</XpciVersion>\n".
       "       </TransactionReference>\n".
       "       <RequestAction>TimeInTransit</RequestAction>\n".
       "   </Request>\n".
       "   <TransitFrom>\n".
       "       <AddressArtifactFormat>\n".
       "           <PoliticalDivision2>". $this->origin_city($vendors_id) ."</PoliticalDivision2>\n".
       "           <PoliticalDivision1>". $this->origin_stateprov($vendors_id) ."</PoliticalDivision1>\n".
       "           <CountryCode>". $this->_upsOriginCountryCode ."</CountryCode>\n".
       "           <PostcodePrimaryLow>". $this->origin_postalcode($vendors_id) ."</PostcodePrimaryLow>\n".
       "       </AddressArtifactFormat>\n".
       "   </TransitFrom>\n".
       "   <TransitTo>\n".
       "       <AddressArtifactFormat>\n".
       "           <PoliticalDivision2>". $this->_upsDestCity ."</PoliticalDivision2>\n".
       "           <PoliticalDivision1>". $this->_upsDestStateProv ."</PoliticalDivision1>\n".
       "           <CountryCode>". $this->_upsDestCountryCode ."</CountryCode>\n".
       "           <PostcodePrimaryLow>". $this->_upsDestPostalCode ."</PostcodePrimaryLow>\n".
       "           <PostcodePrimaryHigh>". $this->_upsDestPostalCode ."</PostcodePrimaryHigh>\n".
       "       </AddressArtifactFormat>\n".
       "   </TransitTo>\n".
       "   <ShipmentWeight>\n".
       "       <UnitOfMeasurement>\n".
       "           <Code>" . $this->unit_weight($vendors_id) . "</Code>\n".
       "       </UnitOfMeasurement>\n".
       "       <Weight>" . $this->weight_for_timeintransit . "</Weight>\n".
       "   </ShipmentWeight>\n".
       "   <InvoiceLineTotal>\n".
       "       <CurrencyCode>" . MODULE_SHIPPING_UPSXML_CURRENCY_CODE . "</CurrencyCode>\n".
       "       <MonetaryValue>" . $this->pkgvalue . "</MonetaryValue>\n".
       "   </InvoiceLineTotal>\n".
       "   <PickupDate>" . $shipdate . "</PickupDate>\n".
       "</TimeInTransitRequest>\n";

       $xmlTransitRequest = $accessRequestHeader .
       $timeintransitSelectionRequestHeader;

       //post request $strXML;
       $xmlTransitResult = $this->_post($this->protocol, $this->host($vendors_id), $this->port, $this->transitpath, $this->transitversion, $this->timeout, $xmlTransitRequest);
       return $this->_transitparseResult($xmlTransitResult);
   }

   //***************************************

   // GM 11-15-2004: modified to return array with time for each service, as
   //                opposed to single transit time for hardcoded "GND" code

   function _transitparseResult($xmlTransitResult) {
        $transitTime = array();

       // Parse XML message returned by the UPS post server.
       $doc = new XMLDocument();
       $xp = new XMLParser();
       $xp->setDocument($doc);
       $xp->parse($xmlTransitResult);
       $doc = $xp->getDocument();
       // Get version. Must be xpci version 1.0001 or this might not work.
       // 1.0001 and 1.0002 seem to be very similar, forget about this for the moment
       /*        $responseVersion = $doc->getValueByPath('TimeInTransitResponse/Response/TransactionReference/XpciVersion');
       if ($this->transitxpci_version != $responseVersion) {
           $message = MODULE_SHIPPING_UPSXML_RATES_TEXT_COMM_VERSION_ERROR;
           return $message;
       } */
       // Get response code. 1 = SUCCESS, 0 = FAIL
       $responseStatusCode = $doc->getValueByPath('TimeInTransitResponse/Response/ResponseStatusCode');
       if ($responseStatusCode != '1') {
           $errorMsg = $doc->getValueByPath('TimeInTransitResponse/Response/Error/ErrorCode');
           $errorMsg .= ": ";
           $errorMsg .= $doc->getValueByPath('TimeInTransitResponse/Response/Error/ErrorDescription');
           // send email if enabled in the admin section
           if ($this->email_errors) {
               error_log("UPSXML TimeInTransit Error: " . $errorMsg . " experienced by customer with id " . $_SESSION['customer_id'] . " on " . date('Y-m-d H:i:s'), 1, STORE_OWNER_EMAIL_ADDRESS);
           }
           // log errors to file ups_error.log when set
           if ($this->ups_error_file) {
               error_log(date('Y-m-d H:i:s')."\tTimeInTransit\t" . $errorMsg . "\t" . $_SESSION['customer_id'] ."\n", 3, $this->ups_error_file);    
           }
           return $errorMsg;
       }
       $root = $doc->getRoot();
       $rootChildren = $root->getChildren();
       for ($r = 0; $r < count($rootChildren); $r++) {
           $elementName = $rootChildren[$r]->getName();
           if ($elementName == "TransitResponse") {
               $transitResponse = $root->getElementsByName("TransitResponse");
               $serviceSummary = $transitResponse['0']->getElementsByName("ServiceSummary");
               $this->numberServices = count($serviceSummary);
               for ($s = 0; $s < $this->numberServices ; $s++) {
                   // index by Desc because that's all we can relate back to the service with
                   // (though it can probably return the code as well..)
                   $serviceDesc = $serviceSummary[$s]->getValueByPath("Service/Description");
                   // hack to get EDD for UPS Saver recognized (Time in Transit uses UPS Worldwide Saver
                   // but the service in Rates and Services is called UPS Saver)
                   if ($serviceDesc == "UPS Worldwide Saver") {
                     $serviceDesc = "UPS Saver";
                   }
                   $transitTime[$serviceDesc]["days"] = $serviceSummary[$s]->getValueByPath("EstimatedArrival/BusinessTransitDays");
                   $transitTime[$serviceDesc]["date"] = $serviceSummary[$s]->getValueByPath("EstimatedArrival/Date");
                   $transitTime[$serviceDesc]["guaranteed"] = $serviceSummary[$s]->getValueByPath("Guaranteed/Code");
               }
           }
       }
       if ($this->logfile) {
           error_log("------------------------------------------\n", 3, $this->logfile);
           foreach($transitTime as $desc => $time) {
               error_log("Business Transit: " . $desc ." = ". $time["date"] . "\n", 3, $this->logfile);
           }
       }
       return $transitTime;
   }

   //EOF Time In Transit
//  ***************************
 function exclude_choices($type, $vendors_id) {
   // used for exclusion of UPS shipping options, disallowed types are read from db
   $disallowed_types = explode(",", @constant('MODULE_SHIPPING_UPSXML_TYPES_' . $vendors_id));
   if (strstr($type, "UPS")) {
       // this will chop off "UPS" from the beginning of the line - typically something like UPS Next Day Air (1 Business Days)
       $type_minus_ups = explode("UPS", $type );
       $type_root = trim($type_minus_ups[1]);
   } // end if (strstr($type, "UPS"):
   else { // service description does not contain UPS (unlikely)
       $type_root = trim($type);
   }
   for ($za = 0; $za < count ($disallowed_types); $za++ ) {
       if ($type_root == trim($disallowed_types[$za])) {
           return true;
           exit;
       } // end if ($type_root == $disallowed_types[$za] ...
   }
   // if the type is not disallowed:
   return false;
 }

 function more_dimensions_to_productsArray($productsArray) {
$counter = 0;
  foreach ($productsArray as $key => $product) {
       // in case by accident or by choice length, width or height is not set
       // we will estimate it by using a set density and the product['weight'] variable
       // will only be used in the check for whether it fits the largest box
       // after that it will already be set, if product['weight'] is set at least
       if ($product['length'] == 0 || $product['width'] == 0 || $product['height'] == 0) {
           $density = 0.7;
           if ($this->unit_length == 'CM') {
               $product['length']=$product['width']=$product['height']= round(10*(pow($product['weight']/$density, 1/3)),1);
           } else {
               // non-metric: inches and pounds
               $product['length']=$product['width']=$product['height']= round(pow($product['weight']*27.67/$density, 1/3),1);
           }
       } // end if ($product['length'] == 0 || $product['width'] == 0 etc.
			// sort dimensions from low to high, used in the function fitsInBox
			   $dimensions = array($product['length'], $product['width'], $product['height']);
           sort($dimensions);
					foreach($dimensions as $key => $value) {
						if ($key == 0 ) { $productsArray[$counter]['x'] = $value; }
						if ($key == 1 ) { $productsArray[$counter]['y'] = $value; }
						if ($key == 2 ) { $productsArray[$counter]['z'] = $value; }
					}
       $productsArray[$counter]['volume'] = $product['length'] * $product['width'] * $product['height'];
			$counter++;
  } // end foreach ($productsArray as $key => $product)

return($productsArray);
 }

} // end class upsxml
// ******************************
function ready_to_shipCmp( $a, $b) {
   if ( $a['ready_to_ship'] == $b['ready_to_ship'] )
   return 0;
   if ( $a['ready_to_ship'] > $b['ready_to_ship'] )
   return -1;
   return 1;
}
?>

Share this post


Link to post
Share on other sites

Heres a more complete version of the above that installs fine, I need some people to test it out.

 


<?php
/*
   $Id: upsxml.php,v 1.1.4 2004/12/19 13:30:00 sgo Exp $
 Modified for MVS V1.0 2006/03/25 JCK/CWG
 osCommerce, Open Source E-Commerce Solutions
http://www.oscommerce.com

 Copyright © 2006 osCommerce

   Original copyright © 2003 Torin Walker
   This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License
   as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version.
   This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
   warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
   See the GNU General Public License for more details.
   You should have received a copy of the GNU General Public License along with this program;
   If not, you may obtain one by writing to and requesting one from:
   The Free Software Foundation, Inc.,
   59 Temple Place, Suite 330,
   Boston, MA 02111-1307 USA

   Written by Torin Walker.
   Some code/style borrowed from both Fritz Clapp's UPS Choice 1.7 Module,
   and Kelvin, Kenneth, and Tom St.Croix's Canada Post 3.1 Module.
   Insurance support by Joe McFrederick
*/

require ('includes/classes/xmldocument.php');
// if using the optional dimensional support, set to 1, otherwise leave as 0
// define('DIMENSIONS_SUPPORTED', 1);
// obsolete: is set in admin now

class upsxml {
   var $code, $title, $description, $icon, $enabled, $types, $boxcount;

   //***************
   function upsxml() {
       global $order;
       $this->code = 'upsxml';
       $this->title = MODULE_SHIPPING_UPSXML_RATES_TEXT_TITLE;
       $this->description = MODULE_SHIPPING_UPSXML_RATES_TEXT_DESCRIPTION;
//        $this->sort_order = MODULE_SHIPPING_UPSXML_RATES_SORT_ORDER;
       $this->icon = DIR_WS_ICONS . 'shipping_ups.gif';
//        $this->tax_class = MODULE_SHIPPING_UPSXML_RATES_TAX_CLASS;
//        $this->enabled = ((MODULE_SHIPPING_UPSXML_RATES_STATUS == 'True') ? true : false);
//        $this->access_key = MODULE_SHIPPING_UPSXML_RATES_ACCESS_KEY;
//        $this->access_username = MODULE_SHIPPING_UPSXML_RATES_USERNAME;
//        $this->access_password = MODULE_SHIPPING_UPSXML_RATES_PASSWORD;
//        $this->access_account_number = MODULE_SHIPPING_UPSXML_RATES_UPS_ACCOUNT_NUMBER;
//        $this->use_negotiated_rates = MODULE_SHIPPING_UPSXML_RATES_USE_NEGOTIATED_RATES;
//        $this->origin = MODULE_SHIPPING_UPSXML_RATES_ORIGIN;
//        $this->origin_city = MODULE_SHIPPING_UPSXML_RATES_CITY;
//        $this->origin_stateprov = MODULE_SHIPPING_UPSXML_RATES_STATEPROV;
//        $this->origin_country = MODULE_SHIPPING_UPSXML_RATES_COUNTRY;
//        $this->origin_postalcode = MODULE_SHIPPING_UPSXML_RATES_POSTALCODE;
//        $this->pickup_method = MODULE_SHIPPING_UPSXML_RATES_PICKUP_METHOD;
//        $this->package_type = MODULE_SHIPPING_UPSXML_RATES_PACKAGE_TYPE;
//        $this->unit_weight = MODULE_SHIPPING_UPSXML_RATES_UNIT_WEIGHT;
//        $this->unit_length = MODULE_SHIPPING_UPSXML_RATES_UNIT_LENGTH;
//        if (MODULE_SHIPPING_UPSXML_DIMENSIONS_SUPPORT == 'Ready-to-ship only') {
//          $this->dimensions_support = 1;
//        } elseif (MODULE_SHIPPING_UPSXML_DIMENSIONS_SUPPORT == 'With product dimensions') {
//          $this->dimensions_support = 2;
//        } else {
//          $this->dimensions_support = 0;
//        }
//        $this->email_errors = ((MODULE_SHIPPING_UPSXML_EMAIL_ERRORS == 'Yes') ? true : false);
//        $this->handling_type = MODULE_SHIPPING_UPSXML_HANDLING_TYPE;
//        $this->handling_fee = MODULE_SHIPPING_UPSXML_RATES_HANDLING;
//        $this->quote_type = MODULE_SHIPPING_UPSXML_RATES_QUOTE_TYPE;
//        $this->customer_classification = MODULE_SHIPPING_UPSXML_RATES_CUSTOMER_CLASSIFICATION_CODE;
       $this->protocol = 'https';
//        $this->host = ((MODULE_SHIPPING_UPSXML_RATES_MODE == 'Test') ? 'wwwcie.ups.com' : 'www.ups.com');
       $this->port = '443';
       $this->path = '/ups.app/xml/Rate';
       $this->transitpath = '/ups.app/xml/TimeInTransit';
       $this->version = 'UPSXML Rate 1.0001';
       $this->transitversion = 'UPSXML Time In Transit 1.0002';
       $this->timeout = '60';
       $this->xpci_version = '1.0001';
       $this->transitxpci_version = '1.0002';
       $this->items_qty = 0;
       $this->timeintransit = '0';
//        $this->timeInTransitView = MODULE_SHIPPING_UPSXML_RATES_TIME_IN_TRANSIT_VIEW;
//        $this->weight_for_timeintransit = '0';
//        $now_unix_time = mktime(date("H"), date("i"), date("s"), date("m"), date("d"), date("Y"));
//        $this->today_unix_time = $now_unix_time;
       $this->today = date("Ymd");
       // insurance addition.
       if (MODULE_SHIPPING_UPSXML_INSURE == 'False')
         { $this->pkgvalue = 100; }
       if (MODULE_SHIPPING_UPSXML_INSURE == 'True')
         { $this->pkgvalue = ceil($order->info['subtotal']); }
       // end insurance addition
       // to enable logging, create an empty "upsxml.log" file at the location you set below, give it write permissions (777) and uncomment the next line
       //   $this->logfile = '/var/www/html/3/vendor-files/upsxml.log';

       // to enable logging of just the errors, do as above but call the file upsxml_error.log
       //      $this->ups_error_file = '/srv/www/htdocs/catalog/includes/modules/shipping/upsxml_error.log';
       // when cURL is not compiled into PHP (Windows users, some Linux users)
       // you can set the next variable to "1" and then exec(curl -d $xmlRequest, $xmlResponse)
       // will be used
       $this->use_exec = '0';

       if (($this->enabled == true) && ((int)MODULE_SHIPPING_UPSXML_RATES_ZONE > 0)) {
           $check_flag = false;
           $check_query = tep_db_query("select zone_id from " . TABLE_ZONES_TO_GEO_ZONES . " where geo_zone_id = '" . MODULE_SHIPPING_UPSXML_RATES_ZONE . "' and zone_country_id = '" . $order->delivery['country']['id'] . "' order by zone_id");
           while ($check = tep_db_fetch_array($check_query)) {
               if ($check['zone_id'] < 1) {
                   $check_flag = true;
                   break;
               } elseif ($check['zone_id'] == $order->delivery['zone_id']) {
                   $check_flag = true;
                   break;
               }
           }
           if ($check_flag == false) {
               $this->enabled = false;
           }
       }

       // Available pickup types - set in admin
       $this->pickup_methods = array(
           'Daily Pickup' => '01',
           'Customer Counter' => '03',
           'One Time Pickup' => '06',
           'On Call Air Pickup' => '07',
           'Suggested Retail Rates (UPS Store)' => '11',
           'Letter Center' => '19',
           'Air Service Center' => '20'
       );

       // Available package types
       $this->package_types = array(
           'UPS Letter' => '01',
           'Package' => '02',
           'UPS Tube' => '03',
           'UPS Pak' => '04',
           'UPS Express Box' => '21',
           'UPS 25kg Box' => '24',
           'UPS 10kg Box' => '25'
       );

       // Human-readable Service Code lookup table. The values returned by the Rates and Service "shop" method are numeric.
       // Using these codes, and the administratively defined Origin, the proper human-readable service name is returned.
       // Note: The origin specified in the admin configuration affects only the product name as displayed to the user.
       $this->service_codes = array(
           // US Origin
           'US Origin' => array(
               '01' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_01,
               '02' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_02,
               '03' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_03,
               '07' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_07,
               '08' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_08,
               '11' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_11,
               '12' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_12,
               '13' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_13,
               '14' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_14,
               '54' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_54,
               '59' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_59,
               '65' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_US_ORIGIN_65
           ),
           // Canada Origin
           'Canada Origin' => array(
               '01' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_CANADA_ORIGIN_01,
               '02' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_CANADA_ORIGIN_02,
               '07' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_CANADA_ORIGIN_07,
               '08' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_CANADA_ORIGIN_08,
               '11' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_CANADA_ORIGIN_11,
               '12' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_CANADA_ORIGIN_12,
               '13' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_CANADA_ORIGIN_13,
               '14' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_CANADA_ORIGIN_14,
               // service code 54 gone after January 2, 2007
               '54' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_CANADA_ORIGIN_54,
               '65' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_CANADA_ORIGIN_65
           ),
           // European Union Origin
           'European Union Origin' => array(
               '07' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_EU_ORIGIN_07,
               '11' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_EU_ORIGIN_11,
               '54' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_EU_ORIGIN_54,
               '65' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_EU_ORIGIN_65,
               // next three services Poland domestic only (Stolica)
               '82' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_EU_ORIGIN_82,
               '83' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_EU_ORIGIN_83,
               '84' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_EU_ORIGIN_84
           ),
           // Puerto Rico Origin
           'Puerto Rico Origin' => array(
               '01' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_PR_ORIGIN_01,
               '02' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_PR_ORIGIN_02,
               '03' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_PR_ORIGIN_03,
               '07' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_PR_ORIGIN_07,
               '08' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_PR_ORIGIN_08,
               '14' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_PR_ORIGIN_14,
               '54' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_PR_ORIGIN_54,
               '65' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_PR_ORIGIN_65
           ),
           // Mexico Origin
           'Mexico Origin' => array(
               '07' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_MEXICO_ORIGIN_07,
               '08' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_MEXICO_ORIGIN_08,
               '54' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_MEXICO_ORIGIN_54,
               '65' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_MEXICO_ORIGIN_65
           ),
           // All other origins
           'All other origins' => array(
               // service code 7 seems to be gone after January 2, 2007
               '07' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_OTHER_ORIGIN_07,
               '08' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_OTHER_ORIGIN_08,
               '54' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_OTHER_ORIGIN_54,
               '65' => MODULE_SHIPPING_UPSXML_SERVICE_CODE_PR_ORIGIN_65
           )
       );
   }

   function access_key($vendors_id='1') {
     $this->access_key = constant('MODULE_SHIPPING_UPSXML_RATES_ACCESS_KEY_' . $vendors_id);
     return $this->access_key;
   }

   function access_username($vendors_id='1') {
     $this->access_username = constant('MODULE_SHIPPING_UPSXML_RATES_USERNAME_' . $vendors_id);
     return $this->access_username;
   }

   function access_password($vendors_id='1') {
     $this->access_password = constant('MODULE_SHIPPING_UPSXML_RATES_PASSWORD_' . $vendors_id);
     return $this->access_password;
   }

   function origin($vendors_id='1') {
     $this->origin = constant('MODULE_SHIPPING_UPSXML_RATES_ORIGIN_' . $vendors_id);
     return $this->origin;
   }

   function origin_city($vendors_id='1') {
     $this->origin_city = constant('MODULE_SHIPPING_UPSXML_RATES_CITY_' . $vendors_id);
     return $this->origin_city;
   }

   function origin_stateprov($vendors_id='1') {
     $this->origin_stateprov = constant('MODULE_SHIPPING_UPSXML_RATES_STATEPROV_' . $vendors_id);
     return $this->origin_stateprov;
   }

   function origin_country($vendors_id='1') {
     $this->origin_country = constant('MODULE_SHIPPING_UPSXML_RATES_COUNTRY_' . $vendors_id);
     return $this->origin_country;
   }

   function origin_postalcode($vendors_id='1') {
     $this->origin_postalcode = constant('MODULE_SHIPPING_UPSXML_RATES_POSTALCODE_' . $vendors_id);
     return $this->origin_postalcode;
   }

   function pickup_method($vendors_id='1') {
     $this->pickup_method = constant('MODULE_SHIPPING_UPSXML_RATES_PICKUP_METHOD_' . $vendors_id);
     return $this->pickup_method;
   }

   function package_type($vendors_id='1') {
     $this->package_type = constant('MODULE_SHIPPING_UPSXML_RATES_PACKAGE_TYPE_' . $vendors_id);
     return $this->package_type;
   }

   function unit_weight($vendors_id='1') {
     $this->unit_weight = constant('MODULE_SHIPPING_UPSXML_RATES_UNIT_WEIGHT_' . $vendors_id);
     return $this->unit_weight;
   }

   function unit_length($vendors_id='1') {
     $this->unit_length = constant('MODULE_SHIPPING_UPSXML_RATES_UNIT_LENGTH_' . $vendors_id);
     return $this->unit_length;
   }

   function handling_fee($vendors_id='1') {
     $this->handling_fee = constant('MODULE_SHIPPING_UPSXML_RATES_HANDLING_' . $vendors_id);
     return $this->handling_fee;
   }

   function quote_type($vendors_id='1') {
     $this->quote_type = constant('MODULE_SHIPPING_UPSXML_RATES_QUOTE_TYPE_' . $vendors_id);
     return $this->quote_type;
   }

   function customer_classification($vendors_id='1') {
     $this->customer_classification = constant('MODULE_SHIPPING_UPSXML_RATES_CUSTOMER_CLASSIFICATION_CODE_' . $vendors_id);
     return $this->customer_classification;
   }

   function host($vendors_id) {
     $this->host = @constant('MODULE_SHIPPING_UPSXML_RATES_TEST_MODE_' . $vendors_id) == 'Test' ? 'wwwcie.ups.com' : 'wwwcie.ups.com';
     return $this->host;
   }

  function sort_order($vendors_id='1') {
    $sort_order = @constant ('MODULE_SHIPPING_UPSXML_RATES_SORT_ORDER_' . $vendors_id);
    if (isset ($sort_order)) {
      $this->sort_order = $sort_order;
    } else {
      $this->sort_order = '-';
    }
    return $this->sort_order;
  }

   function tax_class($vendors_id='1') {
     $this->tax_class = constant('MODULE_SHIPPING_UPSXML_RATES_TAX_CLASS_' . $vendors_id);
     return $this->tax_class;
   }

   function enabled($vendors_id='1') {
     $this->enabled = false;
     $status = @constant('MODULE_SHIPPING_UPSXML_RATES_STATUS_' . $vendors_id);
                       if (isset ($status) && $status != '') {
       $this->enabled = (($status == 'True') ? true : false);
     }
     if ( ($this->enabled == true) && ((int)constant('MODULE_SHIPPING_UPSXML_RATES_ZONE_' . $vendors_id) > 0) ) {
       $check_flag = false;
       $check_query = tep_db_query("select zone_id from " . TABLE_ZONES_TO_GEO_ZONES . " where geo_zone_id = '" . (int)constant('MODULE_SHIPPING_UPSXML_RATES_ZONE_' . $vendors_id) . "' and zone_country_id = '" . $this->delivery_country_id . "' order by zone_id");
       while ($check = tep_db_fetch_array($check_query)) {
         if ($check['zone_id'] < 1) {
           $check_flag = true;
           break;
         }
          elseif ($check['zone_id'] == $this->delivery_zone_id) {
           $check_flag = true;
           break;
           }
       }

       if ($check_flag == false) {
         $this->enabled = false;
       }//if
     }//if
     return $this->enabled;
   }

   function zones($vendors_id='1') {
     if ( ($this->enabled == true) && ((int)constant('MODULE_SHIPPING_UPSXML_RATES_ZONE_' . $vendors_id) > 0) ) {
       $check_flag = false;
       $check_query = tep_db_query("select zone_id from " . TABLE_ZONES_TO_GEO_ZONES . " where geo_zone_id = '" . (int)constant('MODULE_SHIPPING_UPSXML_RATES_ZONE_' . $vendors_id) . "' and zone_country_id = '" . $this->delivery_zone_id . "' order by zone_id");
       while ($check = tep_db_fetch_array($check_query)) {
         if ($check['zone_id'] < 1) {
           $check_flag = true;
           break;
         } elseif ($check['zone_id'] == $this->delivery_zone_id) {
           $check_flag = true;
           break;
         } //if
       }//while

       if ($check_flag == false) {
         $this->enabled = false;
       }//if
     }//if
     return $this->enabled;
   } // end function upsxml

   // class methods
   function quote($method = '', $module = '', $vendors_id = '1') {
       global $HTTP_POST_VARS, $order, $shipping_weight, $shipping_num_boxes, $total_weight, $boxcount, $cart;
       // UPS purports that if the origin is left out, it defaults to the account's location. Yeah, right.
       $state = $order->delivery['state'];
       $zone_query = tep_db_query("select zone_code from " . TABLE_ZONES . " where zone_name = '" .  $order->delivery['state'] . "'");
       if (tep_db_num_rows($zone_query)) {
           $zone = tep_db_fetch_array($zone_query);
           $state = $zone['zone_code'];
       }
       $this->_upsOrigin(constant('MODULE_SHIPPING_UPSXML_RATES_CITY_' . $vendors_id), constant('MODULE_SHIPPING_UPSXML_RATES_STATEPROV_' . $vendors_id), constant('MODULE_SHIPPING_UPSXML_RATES_COUNTRY_' . $vendors_id), constant('MODULE_SHIPPING_UPSXML_RATES_POSTALCODE_' . $vendors_id));
       $this->_upsDest($order->delivery['city'], $state, $order->delivery['country']['iso_code_2'], $order->delivery['postcode']);
       if (method_exists($cart, 'get_products_for_packaging') ) {
         $productsArray = $cart->get_products_for_packaging();
       } else {
         $productsArray = $cart->get_products();
       }
			if ($this->dimensions_support > 0) {
				$productsArray = $this->more_dimensions_to_productsArray($productsArray);
			}

       if ($this->dimensions_support == '2') {
           // sort $productsArray according to ready-to-ship (first) and not-ready-to-ship (last)
           usort($productsArray, ready_to_shipCmp);
           // Use packing algoritm to return the number of boxes we'll ship
           $boxesToShip = $this->packProducts($productsArray);
           // Quote for the number of boxes
           for ($i = 0; $i < count($boxesToShip); $i++) {
               $this->_addItem($boxesToShip[$i]['length'], $boxesToShip[$i]['width'], $boxesToShip[$i]['height'], $boxesToShip[$i]['current_weight'], $boxesToShip[$i]['price']);
               $totalWeight += $boxesToShip[$i]['current_weight'];
           }
       } elseif ($this->dimensions_support == '1') {
           $totalWeight = 0;
           $total_non_ready_to_ship_weight = 0;
					$total_non_ready_to_ship_value = 0;
           // sort $productsArray according to ready-to-ship (first) and not-ready-to-ship (last)
           usort($productsArray, ready_to_shipCmp);
           $non_ready_to_shipArray = array();
           // walk through the productsArray, separate the items ready-to-ship and add them to
           // the items (boxes) list, add the weight to the totalWeight
           // and add the other items to a separate array
           for ($i = 0; $i < count($productsArray); $i++) {
               if ($productsArray[$i]['ready_to_ship'] == '1') {
                   for ($z = 0 ; $z < $productsArray[$i]['quantity']; $z++) {
                       $this->_addItem($productsArray[$i]['length'], $productsArray[$i]['width'], $productsArray[$i]['height'], $productsArray[$i]['weight'], (MODULE_SHIPPING_UPSXML_INSURE == 'False' ? $this->pkgvalue : $productsArray[$i]['final_price']));
                       $totalWeight += $productsArray[$i]['weight'];
                   } // end for ($z = 0 ; $z < $productsArray[$i]['quantity']; $z++)
               } // end if($productsArray['ready_to_ship'] == '1')
               else {
                   $non_ready_to_shipArray[] = $productsArray[$i];
               }
           } // end for ($i = 0; $i < count($productsArray); $i++)
           // Ready_to_ship items out of the way, now assess remaining weight and remaining value of products

           for ($x = 0 ; $x < count($non_ready_to_shipArray) ; $x++) {
               $total_non_ready_to_ship_weight += ($non_ready_to_shipArray[$x]['weight'] * $non_ready_to_shipArray[$x]['quantity']);
							$total_non_ready_to_ship_value += ($non_ready_to_shipArray[$x]['final_price'] * $non_ready_to_shipArray[$x]['quantity']);
           } // end for ($x = 0 ; count($non_ready_to_shipArray) ; $x++)

           if (tep_not_null($non_ready_to_shipArray)) {
               // adapted code from includes/classes/shipping.php
               $shipping_non_ready_to_ship_boxes = 1;
               $shipping_non_ready_to_ship_weight = $total_non_ready_to_ship_weight;
               if (SHIPPING_BOX_WEIGHT >= $total_non_ready_to_ship_weight*SHIPPING_BOX_PADDING/100) {
                 $total_non_ready_to_ship_weight = $total_non_ready_to_ship_weight+SHIPPING_BOX_WEIGHT;
               } else {
                 $total_non_ready_to_ship_weight += $total_non_ready_to_ship_weight*SHIPPING_BOX_PADDING/100;
               }
               if ($total_non_ready_to_ship_weight > SHIPPING_MAX_WEIGHT) { // Split into many boxes
                   $shipping_non_ready_to_ship_boxes = ceil($total_non_ready_to_ship_weight/SHIPPING_MAX_WEIGHT);
                   $shipping_non_ready_to_ship_weight = round($total_non_ready_to_ship_weight/$shipping_non_ready_to_ship_boxes,1);
               }
               // end adapted code from includes/classes/shipping.php
               // weight and number of boxes of non-read-to-ship is determined, now add them to the items list
               for ($y = 0; $y < $shipping_non_ready_to_ship_boxes ; $y++) {
                   $this->_addItem(0, 0, 0, $shipping_non_ready_to_ship_weight, (MODULE_SHIPPING_UPSXML_INSURE == 'False' ? $this->pkgvalue : number_format(($total_non_ready_to_ship_value/$shipping_non_ready_to_ship_boxes), 2, '.', '')) );
                   $totalWeight += $shipping_non_ready_to_ship_weight;
               } // end for ($y = 0; $y < $shipping_non_ready_to_ship_boxes ; $y++)
           } // end if (tep_not_null($non_ready_to_shipArray))
       } else {
           // The old method. Let osCommerce tell us how many boxes, plus the weight of each (or total? - might be sw/num boxes)
           $this->items_qty = 0; //reset quantities
					if (MODULE_SHIPPING_UPSXML_INSURE == 'False') {
              for ($i = 0; $i < $shipping_num_boxes; $i++) {
                 $this->_addItem(0, 0, 0, $shipping_weight, $this->pkgvalue);
              }
					} else {
						// $this->pkgvalue has been set as order subtotal around line 86, it will cause overcharging
						// of insurance if not divided by the number of boxes
						 for ($i = 0; $i < $shipping_num_boxes; $i++) {
                 $this->_addItem(0, 0, 0, $shipping_weight, number_format(($this->pkgvalue/$shipping_num_boxes), 2, '.', ''));
              }
					} // end if/else  (MODULE_SHIPPING_UPSXML_INSURE == 'False')
       }

       // BOF Time In Transit: comment out this section if you don't want/need to have
       // expected delivery dates
       if ($this->dimensions_support > 0) {
           $this->weight_for_timeintransit = round($totalWeight,1);
       } else {
           $this->weight_for_timeintransit = round($shipping_num_boxes * $shipping_weight,1);
       }
       // Added to workaround time in transit error 270033 if total weight of packages is over 150lbs or 70kgs
       if (($this->weight_for_timeintransit > 150) && ($this->unit_weight == "LBS")) {
         $this->weight_for_timeintransit = 150;          
       } else if (($this->weight_for_timeintransit > 70) && ($this->unit_weight == "KGS")) {
         $this->weight_for_timeintransit = 70;          
       }
       // debug only:
       /*  echo '<pre>Packages and variables:<br />';
        print_r($this);
        echo '<br />';
        exit; */
       $this->servicesTimeintransit = $this->_upsGetTimeServices($vendors_id);
       if ($this->logfile) {
           error_log("------------------------------------------\n", 3, $this->logfile);
           error_log("Time in Transit: " . $this->timeintransit . "\n", 3, $this->logfile);
       }
       // EOF Time In Transit

       $upsQuote = $this->_upsGetQuote($vendors_id);
       if ((is_array($upsQuote)) && (sizeof($upsQuote) > 0)) {
           if ($this->dimensions_support > 0) {
               $this->quotes = array('id' => $this->code, 'module' => $this->title . ' (' . $this->boxCount . ($this->boxCount > 1 ? ' pkg(s), ' : ' pkg, ') . round($totalWeight,0) . ' ' . strtolower($this->unit_weight) . ' total)');
           } else {
               $this->quotes = array('id' => $this->code, 'module' => $this->title . ' (' . $shipping_num_boxes . ($this->boxCount > 1 ? ' pkg(s) x ' : ' pkg x ') . round($shipping_weight,0) . ' ' . strtolower($this->unit_weight) . ' total)');
           }
           $methods = array();
           for ($i=0; $i < sizeof($upsQuote); $i++) {
               list($type, $cost) = each($upsQuote[$i]);
               // BOF limit choices, behaviour changed from versions < 1.2
               if (!exclude_choices($type, $vendors_id)) continue;
               // EOF limit choices
               if ( $method == '' || $method == $type ) {
                   $_type = $type;

                   if ($this->timeInTransitView == "Raw") {
                     if (isset($this->servicesTimeintransit[$type])) {
                       $_type = $_type . ", ".$this->servicesTimeintransit[$type]["date"];
                     }        
                   } else {
                     if (isset($this->servicesTimeintransit[$type])) {
                       $eta_array = explode("-", $this->servicesTimeintransit[$type]["date"]);
                       $months = array (" ", "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December");
                       $eta_arrival_date = $months[(int)$eta_array[1]]." ".$eta_array[2].", ".$eta_array[0];
                       $_type .= ", <acronym title='Estimated Delivery Date'>EDD</acronym>: ".$eta_arrival_date;
                     }          
                   }                    

                   // changed to make handling percentage based
                   if ($this->handling_type == "Percentage") {
                       $methods[] = array('id' => $type, 'title' => $_type, 'cost' => ((($this->handling_fee * $cost)/100) + $cost));
                   } else {
                       $methods[] = array('id' => $type, 'title' => $_type, 'cost' => ($this->handling_fee + $cost));
                   }
               }
           }
           if ($this->tax_class($vendors_id) > 0) {
          $this->quotes['tax'] = tep_get_tax_rate($this->tax_class($vendors_id), $order->delivery['country']['id'], $order->delivery['zone_id']);
           }
           $this->quotes['methods'] = $methods;
       } else {
           if ( $upsQuote != false ) {
               $errmsg = $upsQuote;
           } else {
               $errmsg = MODULE_SHIPPING_UPSXML_RATES_TEXT_UNKNOWN_ERROR;
           }
           $errmsg .= '<br>' . MODULE_SHIPPING_UPSXML_RATES_TEXT_IF_YOU_PREFER . ' ' . STORE_NAME.' via <a href="mailto:'.STORE_OWNER_EMAIL_ADDRESS.'"><u>Email</U></a>.';
           $this->quotes = array('module' => $this->title, 'error' => $errmsg);
       }
       if (tep_not_null($this->icon)) {
           $this->quotes['icon'] = tep_image($this->icon, $this->title);
       }
       return $this->quotes;
   }

   //**************
   function check($vendors_id='1') {
       if (!isset($this->_check)) {
       $check_query = tep_db_query("select configuration_value from " . TABLE_VENDOR_CONFIGURATION . " where vendors_id = '". $vendors_id ."' and configuration_key = 'MODULE_SHIPPING_UPSXML_RATES_STATUS_" . $vendors_id . "'");
           $this->_check = tep_db_num_rows($check_query);
       }
       return $this->_check;
   }

   //**************
   function install($vendors_id='1') {
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Enable UPS Shipping', 'MODULE_SHIPPING_UPSXML_RATES_STATUS_" . $vendors_id . "', 'True', 'Do you want to offer UPS shipping?', '6', '0', 'tep_cfg_select_option(array(\'True\', \'False\'), ', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added, vendors_id) values ('UPS Rates Access Key', 'MODULE_SHIPPING_UPSXML_RATES_ACCESS_KEY_" . $vendors_id . "', '', 'Enter the XML rates access key assigned to you by UPS.', '6', '1', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added, vendors_id) values ('UPS Rates Username', 'MODULE_SHIPPING_UPSXML_RATES_USERNAME_" . $vendors_id . "', '', 'Enter your UPS Services account username.', '6', '2', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added, vendors_id) values ('UPS Rates Password', 'MODULE_SHIPPING_UPSXML_RATES_PASSWORD_" . $vendors_id . "', '', 'Enter your UPS Services account password.', '6', '3', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Pickup Method', 'MODULE_SHIPPING_UPSXML_RATES_PICKUP_METHOD_" . $vendors_id . "', 'Daily Pickup', 'How do you give packages to UPS (only used when origin is US)?', '6', '4', 'tep_cfg_select_option(array(\'Daily Pickup\', \'Customer Counter\', \'One Time Pickup\', \'On Call Air Pickup\', \'Letter Center\', \'Air Service Center\', \'Suggested Retail Rates (UPS Store)\'), ', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Packaging Type', 'MODULE_SHIPPING_UPSXML_RATES_PACKAGE_TYPE_" . $vendors_id . "', 'Package', 'What kind of packaging do you use?', '6', '5', 'tep_cfg_select_option(array(\'Customer Package\', \'UPS Letter\', \'UPS Tube\', \'UPS Pak\', \'UPS Express Box\', \'UPS 25kg Box\', \'UPS 10kg box\'), ', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added, vendors_id) values ('Customer Classification Code', 'MODULE_SHIPPING_UPSXML_RATES_CUSTOMER_CLASSIFICATION_CODE_" . $vendors_id . "', '01', '01 - If you are billing to a UPS account and have a daily UPS pickup, 03 - If you do not have a UPS account or you are billing to a UPS account but do not have a daily pickup, 04 - If you are shipping from a retail outlet', '6', '6', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Shipping Origin', 'MODULE_SHIPPING_UPSXML_RATES_ORIGIN_" . $vendors_id . "', 'US Origin', 'What origin point should be used (this setting affects only what UPS product names are shown to the user)', '6', '7', 'tep_cfg_select_option(array(\'US Origin\', \'Canada Origin\', \'European Union Origin\', \'Puerto Rico Origin\', \'Mexico Origin\', \'All other origins\'), ', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added, vendors_id) values ('Origin City', 'MODULE_SHIPPING_UPSXML_RATES_CITY_" . $vendors_id . "', '', 'Enter the name of the origin city.', '6', '8', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added, vendors_id) values ('Origin State/Province', 'MODULE_SHIPPING_UPSXML_RATES_STATEPROV_" . $vendors_id . "', '', 'Enter the two-letter code for your origin state/province.', '6', '9', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added, vendors_id) values ('Origin Country', 'MODULE_SHIPPING_UPSXML_RATES_COUNTRY_" . $vendors_id . "', '', 'Enter the two-letter code for your origin country.', '6', '10', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added, vendors_id) values ('Origin Zip/Postal Code', 'MODULE_SHIPPING_UPSXML_RATES_POSTALCODE_" . $vendors_id . "', '', 'Enter your origin zip/postcode.', '6', '11', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Test or Production Mode', 'MODULE_SHIPPING_UPSXML_RATES_MODE_" . $vendors_id . "', 'Test', 'Use this module in Test or Production mode?', '6', '12', 'tep_cfg_select_option(array(\'Test\', \'Production\'), ', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Unit Weight', 'MODULE_SHIPPING_UPSXML_RATES_UNIT_WEIGHT_" . $vendors_id . "', 'LBS', 'By what unit are your packages weighed?', '6', '13', 'tep_cfg_select_option(array(\'LBS\', \'KGS\'), ', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Unit Length', 'MODULE_SHIPPING_UPSXML_RATES_UNIT_LENGTH_" . $vendors_id . "', 'IN', 'By what unit are your packages sized?', '6', '14', 'tep_cfg_select_option(array(\'IN\', \'CM\'), ', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Dimensions Support', 'MODULE_SHIPPING_UPSXML_DIMENSIONS_SUPPORT_" . $vendors_id . "', 'No', 'Do you use the additional dimensions support (read dimensions.txt in the package)?', '6', '23', 'tep_cfg_select_option(array(\'No\', \'Ready-to-ship only\', \'With product dimensions\'), ', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Quote Type', 'MODULE_SHIPPING_UPSXML_RATES_QUOTE_TYPE_" . $vendors_id . "', 'Commercial', 'Quote for Residential or Commercial Delivery', '6', '15', 'tep_cfg_select_option(array(\'Commercial\', \'Residential\'), ', now(), '" . $vendors_id . "')");
       // next two keys added to be able to use negotiated rates (available from UPS since about July 2006)
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Negotiated rates', 'MODULE_SHIPPING_UPSXML_RATES_USE_NEGOTIATED_RATES_" . $vendors_id . "', 'False', 'Do you receive discounted rates from UPS and want to use these for shipping quotes? <b>Note:</b>  You need to enter your UPS account number below.', '6', '25', 'tep_cfg_select_option(array(\'True\', \'False\'), ', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added, vendors_id) values ('UPS Account Number', 'MODULE_SHIPPING_UPSXML_RATES_UPS_ACCOUNT_NUMBER_" . $vendors_id . "', '', 'Enter your UPS Account number when you have and want to use negotiated rates.', '6', '26', now(), '" . $vendors_id . "')");				
       // added for handling type selection
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Handling Type', 'MODULE_SHIPPING_UPSXML_HANDLING_TYPE_" . $vendors_id . "', 'Flat Fee', 'Handling type for this shipping method.', '6', '16', 'tep_cfg_select_option(array(\'Flat Fee\', \'Percentage\'), ', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added, vendors_id) values ('Handling Fee', 'MODULE_SHIPPING_UPSXML_RATES_HANDLING_" . $vendors_id . "', '0', 'Handling fee for this shipping method.', '6', '16', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added, vendors_id) values ('UPS Currency Code', 'MODULE_SHIPPING_UPSXML_CURRENCY_CODE_" . $vendors_id . "', '', 'Enter the 3 letter currency code for your country of origin. United States (USD)', '6', '2', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Enable Insurance', 'MODULE_SHIPPING_UPSXML_INSURE_" . $vendors_id . "', 'True', 'Do you want to insure packages shipped by UPS?', '6', '22', 'tep_cfg_select_option(array(\'True\', \'False\'), ', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, use_function, set_function, date_added, vendors_id) values ('Tax Class', 'MODULE_SHIPPING_UPSXML_RATES_TAX_CLASS_" . $vendors_id . "', '0', 'Use the following tax class on the shipping fee.', '6', '17', 'tep_get_tax_class_title', 'tep_cfg_pull_down_tax_classes(', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, use_function, set_function, date_added, vendors_id) values ('Shipping Zone', 'MODULE_SHIPPING_UPSXML_RATES_ZONE_" . $vendors_id . "', '0', 'If a zone is selected, only enable this shipping method for that zone.', '6', '18', 'tep_get_zone_class_title', 'tep_cfg_pull_down_zone_classes(', now(), '" . $vendors_id . "')");
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added, vendors_id) values ('Sort order of display.', 'MODULE_SHIPPING_UPSXML_RATES_SORT_ORDER_" . $vendors_id . "', '0', 'Sort order of display. Lowest is displayed first.', '6', '19', now(), '" . $vendors_id . "')");
       // add key for disallowed shipping methods
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " ( configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Disallowed Shipping Methods', 'MODULE_SHIPPING_UPSXML_TYPES_" . $vendors_id . "', '', 'Select the UPS services <span style=\'color: red; font-weight: bold\'>not</span> to be offered.', '6', '20', 'tep_cfg_select_multioption(array(\'Next Day Air\', \'2nd Day Air\', \'Ground\', \'Worldwide Express\', \'Worldwide Express Plus\', \'Worldwide Expedited\', \'Express\', \'Standard\', \'3 Day Select\', \'Next Day Air Saver\', \'Next Day Air Early A.M.\', \'Expedited\', \'2nd Day Air A.M.\', \'Saver\', \'Express Early A.M.\', \'Express Plus\'), ',  now(), '" . $vendors_id . "')");
       // add key for shipping delay
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, last_modified, date_added, use_function, set_function, vendors_id) values ('Shipping Delay', 'SHIPPING_DAYS_DELAY_" . $vendors_id . "', '1', 'How many days from when an order is placed to when you ship it (Decimals are allowed). Arrival date estimations are based on this value.', '6', '21', NULL, now(), NULL, NULL, '" . $vendors_id . "')");
       // add key for enabling email error messages
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Email UPS errors', 'MODULE_SHIPPING_UPSXML_EMAIL_ERRORS_" . $vendors_id . "', 'Yes', 'Do you want to receive UPS errors by email?', '6', '24', 'tep_cfg_select_option(array(\'Yes\', \'No\'), ', now(), '" . $vendors_id . "')");
       // add key for time in transit view type
       tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Time in Transit View Type', 'MODULE_SHIPPING_UPSXML_RATES_TIME_IN_TRANSIT_VIEW_" . $vendors_id . "', 'Raw', 'How the module should display the time in transit to the customer.', '6', '16', 'tep_cfg_select_option(array(\'Raw\', \'Detailed\'), ', now(), '" . $vendors_id . "')");
   }

   //****************
   function remove($vendors_id) {
     tep_db_query("delete from " . TABLE_VENDOR_CONFIGURATION . " where vendors_id = '". $vendors_id ."' and configuration_key in ('" . implode("', '", $this->keys($vendors_id)) . "')");
   }

   //*************
   function keys($vendors_id) {
       // add MODULE_SHIPPING_UPSXML_TYPES to end of array for selectable shipping methods
       return array('MODULE_SHIPPING_UPSXML_RATES_STATUS_' . $vendors_id,
                    'MODULE_SHIPPING_UPSXML_RATES_ACCESS_KEY_' . $vendors_id,
                    'MODULE_SHIPPING_UPSXML_RATES_USERNAME_' . $vendors_id,
                    'MODULE_SHIPPING_UPSXML_RATES_PASSWORD_' . $vendors_id,
                    'MODULE_SHIPPING_UPSXML_RATES_PICKUP_METHOD_' . $vendors_id,
                    'MODULE_SHIPPING_UPSXML_RATES_PACKAGE_TYPE_' . $vendors_id,
                    'MODULE_SHIPPING_UPSXML_RATES_CUSTOMER_CLASSIFICATION_CODE_' . $vendors_id,
                    'MODULE_SHIPPING_UPSXML_RATES_ORIGIN_' . $vendors_id,
                    'MODULE_SHIPPING_UPSXML_RATES_CITY_' . $vendors_id,
                    'MODULE_SHIPPING_UPSXML_RATES_STATEPROV_' . $vendors_id,
                    'MODULE_SHIPPING_UPSXML_RATES_COUNTRY_' . $vendors_id,
                    'MODULE_SHIPPING_UPSXML_RATES_POSTALCODE_' . $vendors_id,
                    'MODULE_SHIPPING_UPSXML_RATES_MODE_' . $vendors_id,
                    'MODULE_SHIPPING_UPSXML_RATES_UNIT_WEIGHT_' . $vendors_id,
                    'MODULE_SHIPPING_UPSXML_RATES_UNIT_LENGTH_' . $vendors_id,
	     'MODULE_SHIPPING_UPSXML_DIMENSIONS_SUPPORT_' . $vendors_id,
                    'MODULE_SHIPPING_UPSXML_RATES_QUOTE_TYPE_' . $vendors_id,
	     'MODULE_SHIPPING_UPSXML_RATES_USE_NEGOTIATED_RATES_' . $vendors_id,
	     'MODULE_SHIPPING_UPSXML_RATES_UPS_ACCOUNT_NUMBER_' . $vendors_id,
	     'MODULE_SHIPPING_UPSXML_HANDLING_TYPE_' . $vendors_id,
	     'MODULE_SHIPPING_UPSXML_RATES_HANDLING_' . $vendors_id,
	     'MODULE_SHIPPING_UPSXML_INSURE_' . $vendors_id,
	     'MODULE_SHIPPING_UPSXML_CURRENCY_CODE_' . $vendors_id,
	     'MODULE_SHIPPING_UPSXML_RATES_TAX_CLASS_' . $vendors_id,
	     'MODULE_SHIPPING_UPSXML_RATES_ZONE_' . $vendors_id,
	     'MODULE_SHIPPING_UPSXML_RATES_SORT_ORDER_' . $vendors_id,
	     'MODULE_SHIPPING_UPSXML_TYPES_' . $vendors_id,
	     'SHIPPING_DAYS_DELAY_' . $vendors_id,
	     'MODULE_SHIPPING_UPSXML_EMAIL_ERRORS_' . $vendors_id,
	     'MODULE_SHIPPING_UPSXML_RATES_TIME_IN_TRANSIT_VIEW_' . $vendors_id,);
   }

   //***********************
   function _upsProduct($prod, $vendors_id){
       $this->_upsProductCode = $prod;
   }

   //**********************************************
   function _upsOrigin($city, $stateprov, $country, $postal){
       $this->_upsOriginCity = $city;
       $this->_upsOriginStateProv = $stateprov;
       $this->_upsOriginCountryCode = $country;
       $postal = str_replace(' ', '', $postal);
       if ($country == 'US') {
           $this->_upsOriginPostalCode = substr($postal, 0, 5);
       } else {
           $this->_upsOriginPostalCode = $postal;
       }
   }

   //**********************************************
   function _upsDest($city, $stateprov, $country, $postal) {
       $this->_upsDestCity = $city;
       $this->_upsDestStateProv = $stateprov;
       $this->_upsDestCountryCode = $country;
       $postal = str_replace(' ', '', $postal);
       if ($country == 'US') {
           $this->_upsDestPostalCode = substr($postal, 0, 5);
       } else {
           $this->_upsDestPostalCode = $postal;
       }
   }

   //************************
   function _upsAction($action, $vendors_id) {
       // rate - Single Quote; shop - All Available Quotes
       $this->_upsActionCode = $action;
   }

   //********************************************
	// default value of 100 added for insurance (100 shouldn't trigger costs for insurance)
   function _addItem($length, $width, $height, $weight, $price = 0 ) {
       // Add box or item to shipment list. Round weights to 1 decimal places.
       if ((float)$weight < 1.0) {
           $weight = 1;
       } else {
           $weight = round($weight, 1);
       }
       $index = $this->items_qty;
       $this->item_length[$index] = ($length ? (string)$length : '0' );
       $this->item_width[$index] = ($width ? (string)$width : '0' );
       $this->item_height[$index] = ($height ? (string)$height : '0' );
       $this->item_weight[$index] = ($weight ? (string)$weight : '0' );
			$this->item_price[$index] = $price;
       $this->items_qty++;
   }

   //********************
   function getPackagesByVol() {
       $packages = array();
       $packages_query = tep_db_query("select *, (package_length * package_width * package_height) as volume from " . TABLE_PACKAGING . " order by volume;");
			$counter = 0;
       while ($package = tep_db_fetch_array($packages_query)) {
           $packages[] = array(
           'id' => $package['package_id'],
           'name' => $package['package_name'],
           'description' => $package['package_description'],
           'length' => $package['package_length'],
           'width' => $package['package_width'],
           'height' => $package['package_height'],
           'empty_weight' => $package['package_empty_weight'],
           'max_weight' => $package['package_max_weight'],
           'volume' => $package['volume']);
// sort dimensions from low to high, used in the function fitsInBox
           $dimensions = array($package['package_length'], $package['package_width'], $package['package_height']);
           sort($dimensions);
					foreach($dimensions as $key => $value) {
						if ($key == 0 ) { $packages[$counter]['x'] = $value; }
						if ($key == 1 ) { $packages[$counter]['y'] = $value; }
						if ($key == 2 ) { $packages[$counter]['z'] = $value; }
					}
					$counter++;
       }
       return $packages;
   }

   //********************************
   function packProducts($productsArray) {
       $definedPackages = $this->getPackagesByVol();
	/*		echo '<pre>';
			print_r($definedPackages);
			exit; */
       $emptyBoxesArray = array();
       for ($i = 0; $i < count($definedPackages); $i++) {
           $definedBox = $definedPackages[$i];
           $definedBox['remaining_volume'] = $definedBox['volume'];
           $definedBox['current_weight'] = $definedBox['empty_weight'];
           $emptyBoxesArray[] = $definedBox;
       }
         if (count($emptyBoxesArray) == 0) {
            print("ERROR: No boxes to ship unpackaged product<br />\n");
            break;
         }

       $packedBoxesArray = array();
       $currentBox = NULL;
       $index_of_largest_box = count($emptyBoxesArray)-1;
       // Get the product array and expand multiple qty items.
       $productsRemaining = array();
       for ($i = 0; $i < count($productsArray); $i++) {
           $product = $productsArray[$i];
           // sanity checks on the product, should not be done for ready-to-ship times
           if ((int)$product['ready_to_ship'] == 0) {
					  $product['ready_to_ship'] = '1';
					  for ($x = 0; $x <= $index_of_largest_box; $x++) {
						  if ($this->fitsInBox($product, $emptyBoxesArray[$x])) {
                 $product['ready_to_ship'] = '0';
                 $product['largest_box_it_will_fit'] = $x;
               } 
					  } // end for ($x = 0; $x <= $index_of_largest_box; $x++) 
           } // end if ($product['ready_to_ship'] == '0')

           for ($j = 0; $j < $productsArray[$i]['quantity']; $j++) {
               $productsRemaining[] = $product;
           }
       } // end for ($i = 0; $i < count($productsArray); $i++)
       // make sure the products that did not fit the largest box and are now set as ready-to-ship
       // are out of the way as soon as possible
       usort($productsRemaining, ready_to_shipCmp);
       // Worst case, you'll need as many boxes as products ordered.
       $index_of_largest_box_to_use = count($emptyBoxesArray) -1;
       while (count($productsRemaining)) {
           // Immediately set aside products that are already packed and ready.
           if ($productsRemaining[0]['ready_to_ship'] == '1') {
               $packedBoxesArray[] = array (
               'length' => $productsRemaining[0]['length'],
               'width' => $productsRemaining[0]['width'],
               'height' => $productsRemaining[0]['height'],
               'current_weight' => $productsRemaining[0]['weight'],
               'price' => $productsRemaining[0]['final_price']);
               $productsRemaining = array_slice($productsRemaining, 1);
               continue;
           }
           // Cycle through boxes, increasing box size if all doesn't fit.
           // but if the remaining products only fit in a box of smaller size, use that one to pack it away
           for ($b = 0; $b < count($emptyBoxesArray) && tep_not_null($productsRemaining); $b++) {
               $result = $this->fitProductsInBox($productsRemaining, $emptyBoxesArray[$b], $packedBoxesArray, $b, $index_of_largest_box_to_use);
               $packedBoxesArray = $result['packed_boxes'];
               $productsRemaining = $result['remaining'];
               if (isset($result['index_of_largest_box_to_use']) && $result['index_of_largest_box_to_use'] >= 0 ) {
                 $index_of_largest_box_to_use = $result['index_of_largest_box_to_use'];
               }
           }
       } // end while

       return $packedBoxesArray;
   }

   //*****************************
   function fitsInBox($product, $box) {
       // in case by accident or by choice length, width or height is not set
       // we will estimate it by using a set density and the product['weight'] variable
       // will only be used in the check for whether it fits the largest box
       // after that it will already be set, if product['weight'] is set at least
			if ($product['x'] > $box['x'] || $product['y'] > $box['y'] || $product['z'] > $box['z']) {
           return false;
       } 

       if ($product['volume'] <= $box['remaining_volume']) {
           if ($box['max_weight'] == 0 || ($box['current_weight'] + $product['weight'] <= $box['max_weight'])) {
               return true;
           }
       }
       return false;
   }

   //***********************************
   function putProductInBox($product, $box) {
       $box['remaining_volume'] -= $product['volume'];
       $box['products'][] = $product;
       $box['current_weight'] += $product['weight'];
			$box['price'] += $product['final_price'];
       return $box;
   } 
   //*********************    
   function fitProductsInBox($productsRemaining, $emptyBox, $packedBoxesArray, $box_no, $index_of_largest_box) { 
       $currentBox = $emptyBox;
       $productsRemainingSkipped = array();
       $productsRemainingNotSkipped = array();
       $largest_box_in_skipped_products = -1;
       // keep apart products that will not fit this box anyway
       for ($p = 0; $p < count($productsRemaining); $p++) {
         if ($productsRemaining[$p]['largest_box_it_will_fit'] < $box_no) {
           $productsRemainingSkipped[] = $productsRemaining[$p];
           // check on skipped products: if they will not fit in the largest box
           // the $index_of_largest_box should be the one they *will* fit
           // otherwise the packing algorithm gets stuck in a loop
           if ($productsRemaining[$p]['largest_box_it_will_fit'] > $largest_box_in_skipped_products) {
             $largest_box_in_skipped_products = $productsRemaining[$p]['largest_box_it_will_fit'];
           }
         } else {
           $productsRemainingNotSkipped[] = $productsRemaining[$p];
         }
       }

       unset($productsRemaining);
       $productsRemaining = $productsRemainingNotSkipped;
       unset($productsRemainingNotSkipped);
       if (count($productsRemaining) == 0) {
         // products remaining are the ones that will not fit this box (productsRemaimingSkipped)
           $result_array = array('remaining' => $productsRemainingSkipped, 'box_no' => $box_no, 'packed_boxes' => $packedBoxesArray, 'index_of_largest_box_to_use' => $largest_box_in_skipped_products);
           return ($result_array);
       }

       //Try to fit each product that can fit in box
       for ($p = 0; $p < count($productsRemaining); $p++) {
           if ($this->fitsInBox($productsRemaining[$p], $currentBox)) {
               //It fits. Put it in the box.
               $currentBox = $this->putProductInBox($productsRemaining[$p], $currentBox);
               if ($p == count($productsRemaining) - 1) {
                   $packedBoxesArray[] = $currentBox;
                   $productsRemaining = array_slice($productsRemaining, $p + 1);
                   $productsRemaining = array_merge($productsRemaining, $productsRemainingSkipped);

                   $result_array = array('remaining' => $productsRemaining, 'box_no' => $box_no, 'packed_boxes' => $packedBoxesArray);
                   return ($result_array);
               }
           } else {
               if ($box_no == $index_of_largest_box) {
                   //We're at the largest box already, and it's full. Keep what we've packed so far and get another box.
                   $packedBoxesArray[] = $currentBox;
                   $productsRemaining = array_slice($productsRemaining, $p);
                   $productsRemaining = array_merge($productsRemaining, $productsRemainingSkipped);
                   $result_array = array('remaining' => $productsRemaining, 'box_no' => $box_no, 'packed_boxes' => $packedBoxesArray);
                   return ($result_array);
               }
               // Not all of them fit. Stop packing remaining products and try next box.
               $productsRemaining = array_merge($productsRemaining, $productsRemainingSkipped);
               $result_array = array('remaining' => $productsRemaining, 'box_no' => $box_no, 'packed_boxes' => $packedBoxesArray);
               return ($result_array);
           } // end else
       } // end for ($p = 0; $p < count($productsRemaining); $p++)
   } // end function

   //*********************
   function _upsGetQuote($vendors_id='1') {

       // Create the access request
       $accessRequestHeader =
       "<?xml version=\"1.0\"?>\n".
       "<AccessRequest xml:lang=\"en-US\">\n".
       "   <AccessLicenseNumber>". $this->access_key($vendors_id) ."</AccessLicenseNumber>\n".
       "   <UserId>". $this->access_username($vendors_id) ."</UserId>\n".
       "   <Password>". $this->access_password($vendors_id) ."</Password>\n".
       "</AccessRequest>\n";

       $ratingServiceSelectionRequestHeader =
       "<?xml version=\"1.0\"?>\n".
       "<RatingServiceSelectionRequest xml:lang=\"en-US\">\n".
       "   <Request>\n".
       "       <TransactionReference>\n".
       "           <CustomerContext>Rating and Service</CustomerContext>\n".
       "           <XpciVersion>". $this->xpci_version ."</XpciVersion>\n".
       "       </TransactionReference>\n".
       "       <RequestAction>Rate</RequestAction>\n".
       "       <RequestOption>shop</RequestOption>\n".
       "   </Request>\n";
       // according to UPS the CustomerClassification and PickupType containers should
       // not be present when the origin country is non-US see:
       // http://forums.oscommerce.com/index.php?s=&...st&p=730947
       if ($this->origin_country == 'US') {
       $ratingServiceSelectionRequestHeader .=
       "   <PickupType>\n".
       "       <Code>". $this->pickup_methods[$this->pickup_method($vendors_id)] ."</Code>\n".
       "   </PickupType>\n";
       "   <CustomerClassification>\n".
       "       <Code>". $this->customer_classification ."</Code>\n".
       "   </CustomerClassification>\n";
       }
       $ratingServiceSelectionRequestHeader .=
       "   <Shipment>\n".
       "       <Shipper>\n";
       if ($this->use_negotiated_rates == 'True') {
       $ratingServiceSelectionRequestHeader .=
       "         <ShipperNumber>" . $this->access_account_number . "</ShipperNumber>\n";
			}
       $ratingServiceSelectionRequestHeader .=
       "           <Address>\n".
       "               <City>". $this->_upsOriginCity ."</City>\n".
       "               <StateProvinceCode>". $this->_upsOriginStateProv ."</StateProvinceCode>\n".
       "               <CountryCode>". $this->_upsOriginCountryCode ."</CountryCode>\n".
       "               <PostalCode>". $this->_upsOriginPostalCode ."</PostalCode>\n".
       "           </Address>\n".
       "       </Shipper>\n".
       "       <ShipTo>\n".
       "           <Address>\n".
       "               <City>". $this->_upsDestCity ."</City>\n".
       "               <StateProvinceCode>". $this->_upsDestStateProv ."</StateProvinceCode>\n".
       "               <CountryCode>". $this->_upsDestCountryCode ."</CountryCode>\n".
       "               <PostalCode>". $this->_upsDestPostalCode ."</PostalCode>\n".
       ($this->quote_type($vendors_id) == "Residential" ? "<ResidentialAddressIndicator/>\n" : "") .
       "           </Address>\n".
       "       </ShipTo>\n";
       for ($i = 0; $i < $this->items_qty; $i++) {

           $ratingServiceSelectionRequestPackageContent .=
           "       <Package>\n".
           "           <PackagingType>\n".
           "               <Code>". $this->package_types[$this->package_type($vendors_id)] ."</Code>\n".
           "           </PackagingType>\n";
           if ($this->dimensions_support > 0 && ($this->item_length[$i] > 0 ) && ($this->item_width[$i] > 0 ) && ($this->item_height[$i] > 0)) {

               $ratingServiceSelectionRequestPackageContent .=
               "           <Dimensions>\n".
               "               <UnitOfMeasurement>\n".
               "                   <Code>". $this->unit_length($vendors_id) ."</Code>\n".
               "               </UnitOfMeasurement>\n".
               "               <Length>". $this->item_length[$i] ."</Length>\n".
               "               <Width>". $this->item_width[$i] ."</Width>\n".
               "               <Height>". $this->item_height[$i] ."</Height>\n".
               "           </Dimensions>\n";
           }

           $ratingServiceSelectionRequestPackageContent .=
           "           <PackageWeight>\n".
           "               <UnitOfMeasurement>\n".
           "                   <Code>". $this->unit_weight($vendors_id) ."</Code>\n".
           "               </UnitOfMeasurement>\n".
           "               <Weight>". $this->item_weight[$i] ."</Weight>\n".
           "           </PackageWeight>\n".
           "           <PackageServiceOptions>\n".
           //"               <COD>\n".
           //"                   <CODFundsCode>0</CODFundsCode>\n".
           //"                   <CODCode>3</CODCode>\n".
           //"                   <CODAmount>\n".
           //"                       <CurrencyCode>USD</CurrencyCode>\n".
           //"                       <MonetaryValue>1000</MonetaryValue>\n".
           //"                   </CODAmount>\n".
           //"               </COD>\n".
           "               <InsuredValue>\n".
           "                   <CurrencyCode>".MODULE_SHIPPING_UPSXML_CURRENCY_CODE."</CurrencyCode>\n".
           "                   <MonetaryValue>".$this->item_price[$i]."</MonetaryValue>\n".
           "               </InsuredValue>\n".
           "           </PackageServiceOptions>\n".
           "       </Package>\n";
       }

       $ratingServiceSelectionRequestFooter = '';
       //"   <ShipmentServiceOptions/>\n".
          if ($this->use_negotiated_rates == 'True') {
       $ratingServiceSelectionRequestFooter .=
           "       <RateInformation>\n".
           "         <NegotiatedRatesIndicator/>\n".
           "       </RateInformation>\n";
          }
       $ratingServiceSelectionRequestFooter .=
       "   </Shipment>\n";
       // according to UPS the CustomerClassification and PickupType containers should
       // not be present when the origin country is non-US see:
       // http://forums.oscommerce.com/index.php?s=&...st&p=730947
       if ($this->origin_country == 'US') {
       $ratingServiceSelectionRequestFooter .=
             "   <CustomerClassification>\n".
       "       <Code>". $this->customer_classification($vendors_id) ."</Code>\n".
             "   </CustomerClassification>\n";
       }
       $ratingServiceSelectionRequestFooter .=
       "</RatingServiceSelectionRequest>\n";

       $xmlRequest = $accessRequestHeader .
       $ratingServiceSelectionRequestHeader .
       $ratingServiceSelectionRequestPackageContent .
       $ratingServiceSelectionRequestFooter;

                               $host = $this->host($vendors_id);

       //post request $strXML;
       $xmlResult = $this->_post($this->protocol, $host, $this->port, $this->path, $this->version, $this->timeout, $xmlRequest);
       // BOF testing with a response from UPS saved as a text file
       // needs commenting out the line above: $xmlResult = $this->_post($this->protocol, etcetera
/*        $filename = '/Library/WebServer/Documents/nytest/includes/modules/shipping/example_response.xml';
       $fp = fopen($filename, "r") or die("couldn't open file");
       $xmlResult = "";
       while (! feof($fp)) {
         $xmlResult .= fgets($fp, 1024);
       } 
       // EOF testing with a text file */
       return $this->_parseResult($xmlResult, $vendors_id);
   }

   //******************************************************************
   function _post($protocol, $host, $port, $path, $version, $timeout, $xmlRequest) {
       $url = $protocol."://".$host.":".$port.$path;
       if ($this->logfile) {
           error_log("------------------------------------------\n", 3, $this->logfile);
           error_log("DATE AND TIME: ".date('Y-m-d H:i:s')."\n", 3, $this->logfile);
           error_log("UPS URL: " . $url . "\n", 3, $this->logfile);
       }
       if (function_exists('exec') && $this->use_exec == '1' ) {
           exec('which curl', $curl_output);
           if ($curl_output) {
               $curl_path = $curl_output[0];
           } else {
               $curl_path = 'curl'; // change this if necessary
           }
           if ($this->logfile) {
               error_log("UPS REQUEST using exec(): " . $xmlRequest . "\n", 3, $this->logfile);
           }
           // add option -k to the statement: $command = "".$curl_path." -k -d \"". etcetera if you get
           // curl error 60: error setting certificate verify locations
           // using addslashes was the only way to avoid UPS returning the 1001 error: The XML document is not well formed
           $command = "".$curl_path." -d \"".addslashes($xmlRequest)."\" ".$url."";
           exec($command, $xmlResponse);
           if ( empty($xmlResponse) && $this->logfile) { // using exec no curl errors can be retrieved
               error_log("Error from cURL using exec() since there is no \$xmlResponse\n", 3, $this->logfile);
           }
           if ($this->logfile) {
               error_log("UPS RESPONSE using exec(): " . $xmlResponse[0] . "\n", 3, $this->logfile);
           }
       } elseif ($this->use_exec == '1') { // if NOT (function_exists('exec') && $this->use_exec == '1'
           if ($this->logfile) {
               error_log("Sorry, exec() cannot be called\n", 3, $this->logfile);
           }
       } else { // default behavior: cURL is assumed to be compiled in PHP
           $ch = curl_init();
           curl_setopt($ch, CURLOPT_URL, $url);
           // uncomment the next line if you get curl error 60: error setting certificate verify locations
           //curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
           // uncommenting the next line is most likely not necessary in case of error 60
           // curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
           curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
           curl_setopt($ch, CURLOPT_HEADER, 0);
           curl_setopt($ch, CURLOPT_POST, 1);
           curl_setopt($ch, CURLOPT_POSTFIELDS, $xmlRequest);
           curl_setopt($ch, CURLOPT_TIMEOUT, (int)$timeout);

           if ($this->logfile) {
               error_log("UPS REQUEST: " . $xmlRequest . "\n", 3, $this->logfile);
           }
           $xmlResponse = curl_exec ($ch);
           if (curl_errno($ch) && $this->logfile) {
               $error_from_curl = sprintf('Error [%d]: %s', curl_errno($ch), curl_error($ch));
               error_log("Error from cURL: " . $error_from_curl . "\n", 3, $this->logfile);
           }
           // send email if enabled in the admin section
           if (curl_errno($ch) && $this->email_errors) {
               $error_from_curl = sprintf('Error [%d]: %s', curl_errno($ch), curl_error($ch));
               error_log("Error from cURL: " . $error_from_curl . " experienced by customer with id " . $_SESSION['customer_id'] . " on " . date('Y-m-d H:i:s'), 1, STORE_OWNER_EMAIL_ADDRESS);
           }
           // log errors to file ups_error.log when set
           if (curl_errno($ch) && $this->ups_error_file) {
               $error_from_curl = sprintf('Error [%d]: %s', curl_errno($ch), curl_error($ch));
               error_log(date('Y-m-d H:i:s')."\tcURL\t" . $error_from_curl . "\t" . $_SESSION['customer_id']."\n", 3, $this->ups_error_file);    
           }
           if ($this->logfile) {
               error_log("UPS RESPONSE: " . $xmlResponse . "\n", 3, $this->logfile);
           }
           curl_close ($ch);
       }

       if(!$xmlResponse || strstr(strtolower(substr($xmlResponse, 0, 120)), "bad request"))  {
           /* Sometimes the UPS server responds with an HTML message (differing depending on whether the test server
              or the production server is used) but both have in the title tag "Bad request".
              Parsing this response will result in a fatal error:
              Call to a member function on a non-object in /blabla/includes/classes/xmldocument.php on line 57
              It only results in not showing Estimated Delivery Dates to the customer so avoiding the fatal error should do.
           */
           $xmlResponse = "<?xml version=\"1.0\"?>\n".
           "<RatingServiceSelectionResponse>\n".
           "   <Response>\n".
           "       <TransactionReference>\n".
           "           <CustomerContext>Rating and Service</CustomerContext>\n".
           "           <XpciVersion>1.0001</XpciVersion>\n".
           "       </TransactionReference>\n".
           "       <ResponseStatusCode>0</ResponseStatusCode>\n".
           "       <ResponseStatusDescription>". MODULE_SHIPPING_UPSXML_RATES_TEXT_COMM_UNKNOWN_ERROR ."</ResponseStatusDescription>\n".
           "   </Response>\n".
           "</RatingServiceSelectionResponse>\n";
           return $xmlResponse;
       }
       if ($this->use_exec == '1') {
           return $xmlResponse[0]; // $xmlResponse is an array in this case
       } else {
           return $xmlResponse;
       }
   }

   //*****************************
   function _parseResult($xmlResult, $vendors_id) {
       // Parse XML message returned by the UPS post server.
       $doc = new XMLDocument();
       $xp = new XMLParser();
       $xp->setDocument($doc);
       $xp->parse($xmlResult);
       $doc = $xp->getDocument();
       // Get version. Must be xpci version 1.0001 or this might not work.
       $responseVersion = $doc->getValueByPath('RatingServiceSelectionResponse/Response/TransactionReference/XpciVersion');
       if ($this->xpci_version != $responseVersion) {
           $message = MODULE_SHIPPING_UPSXML_RATES_TEXT_COMM_VERSION_ERROR;
           return $message;
       }
       // Get response code. 1 = SUCCESS, 0 = FAIL
       $responseStatusCode = $doc->getValueByPath('RatingServiceSelectionResponse/Response/ResponseStatusCode');
       if ($responseStatusCode != '1') {
           $errorMsg = $doc->getValueByPath('RatingServiceSelectionResponse/Response/Error/ErrorCode');
           $errorMsg .= ": ";
           $errorMsg .= $doc->getValueByPath('RatingServiceSelectionResponse/Response/Error/ErrorDescription');
           // send email if enabled in the admin section
           if ($this->email_errors) {
               error_log("UPSXML Rates Error: " . $errorMsg . " experienced by customer with id " . $_SESSION['customer_id'] . " on " . date('Y-m-d H:i:s'), 1, STORE_OWNER_EMAIL_ADDRESS);
           }
           // log errors to file ups_error.log when set
           if ($this->ups_error_file) {
               error_log(date('Y-m-d H:i:s')."\tRates\t" . $errorMsg . "\t" . $_SESSION['customer_id']."\n", 3, $this->ups_error_file);    
           }
               return $errorMsg;
       }
       $root = $doc->getRoot();
       $ratedShipments = $root->getElementsByName("RatedShipment");
       $aryProducts = false;
       for ($i = 0; $i < count($ratedShipments); $i++) {
           $serviceCode = $ratedShipments[$i]->getValueByPath("/Service/Code");
					if ($this->use_negotiated_rates == 'True') {
						$totalCharge = $ratedShipments[$i]->getValueByPath("/NegotiatedRates/NetSummaryCharges/GrandTotal/MonetaryValue");
               if (!($serviceCode && $totalCharge)) {
               continue;
               }
					} else {
             $totalCharge = $ratedShipments[$i]->getValueByPath("/TotalCharges/MonetaryValue");
               if (!($serviceCode && $totalCharge)) {
               continue;
               }
					}
           $ratedPackages = $ratedShipments[$i]->getElementsByName("RatedPackage");
           $this->boxCount = count($ratedPackages);
           $gdaysToDelivery = $ratedShipments[$i]->getValueByPath("/GuaranteedDaysToDelivery");
           $scheduledTime = $ratedShipments[$i]->getValueByPath("/ScheduledDeliveryTime");
           $title = '';
           $title = $this->service_codes[$this->origin($vendors_id)][$serviceCode];

           /* we don't want to use this, it may conflict with time estimation
                 if ($gdaysToDelivery) {
                     $title .= ' (';
                     $title .= $gdaysToDelivery . " Business Days";
                     if ($scheduledTime) {
                         $title .= ' @ ' . $scheduledTime;
                     }
                     $title .= ')';
                 } elseif ($this->timeintransit > 0) {
                     $title .= ' (';
                     $title .= $this->timeintransit . " Business Days";
                     $title .= ')';
                 }
           */
           $aryProducts[$i] = array($title => $totalCharge);
       }
       return $aryProducts;
   }

   // BOF Time In Transit

   // GM 11-15-2004: renamed from _upsGetTime()

   //********************
   function _upsGetTimeServices($vendors_id) {

     if (constant('SHIPPING_DAYS_DELAY_' . $vendors_id) > 0) {
       $shipdate = date("Ymd", time()+(86400*constant('SHIPPING_DAYS_DELAY_' . $vendors_id)));
       } else {
           $shipdate = $this->today;
       }

       // Create the access request
       $accessRequestHeader =
       "<?xml version=\"1.0\"?>\n".
       "<AccessRequest xml:lang=\"en-US\">\n".
       "   <AccessLicenseNumber>". $this->access_key($vendors_id) ."</AccessLicenseNumber>\n".
       "   <UserId>". $this->access_username($vendors_id) ."</UserId>\n".
       "   <Password>". $this->access_password($vendors_id) ."</Password>\n".
       "</AccessRequest>\n";

       $timeintransitSelectionRequestHeader =
       "<?xml version=\"1.0\"?>\n".
       "<TimeInTransitRequest xml:lang=\"en-US\">\n".
       "   <Request>\n".
       "       <TransactionReference>\n".
       "           <CustomerContext>Time in Transit</CustomerContext>\n".
       "           <XpciVersion>". $this->transitxpci_version ."</XpciVersion>\n".
       "       </TransactionReference>\n".
       "       <RequestAction>TimeInTransit</RequestAction>\n".
       "   </Request>\n".
       "   <TransitFrom>\n".
       "       <AddressArtifactFormat>\n".
       "           <PoliticalDivision2>". $this->origin_city($vendors_id) ."</PoliticalDivision2>\n".
       "           <PoliticalDivision1>". $this->origin_stateprov($vendors_id) ."</PoliticalDivision1>\n".
       "           <CountryCode>". $this->_upsOriginCountryCode ."</CountryCode>\n".
       "           <PostcodePrimaryLow>". $this->origin_postalcode($vendors_id) ."</PostcodePrimaryLow>\n".
       "       </AddressArtifactFormat>\n".
       "   </TransitFrom>\n".
       "   <TransitTo>\n".
       "       <AddressArtifactFormat>\n".
       "           <PoliticalDivision2>". $this->_upsDestCity ."</PoliticalDivision2>\n".
       "           <PoliticalDivision1>". $this->_upsDestStateProv ."</PoliticalDivision1>\n".
       "           <CountryCode>". $this->_upsDestCountryCode ."</CountryCode>\n".
       "           <PostcodePrimaryLow>". $this->_upsDestPostalCode ."</PostcodePrimaryLow>\n".
       "           <PostcodePrimaryHigh>". $this->_upsDestPostalCode ."</PostcodePrimaryHigh>\n".
       "       </AddressArtifactFormat>\n".
       "   </TransitTo>\n".
       "   <ShipmentWeight>\n".
       "       <UnitOfMeasurement>\n".
       "           <Code>" . $this->unit_weight($vendors_id) . "</Code>\n".
       "       </UnitOfMeasurement>\n".
       "       <Weight>" . $this->weight_for_timeintransit . "</Weight>\n".
       "   </ShipmentWeight>\n".
       "   <InvoiceLineTotal>\n".
       "       <CurrencyCode>" . MODULE_SHIPPING_UPSXML_CURRENCY_CODE . "</CurrencyCode>\n".
       "       <MonetaryValue>" . $this->pkgvalue . "</MonetaryValue>\n".
       "   </InvoiceLineTotal>\n".
       "   <PickupDate>" . $shipdate . "</PickupDate>\n".
       "</TimeInTransitRequest>\n";

       $xmlTransitRequest = $accessRequestHeader .
       $timeintransitSelectionRequestHeader;

       //post request $strXML;
       $xmlTransitResult = $this->_post($this->protocol, $this->host($vendors_id), $this->port, $this->transitpath, $this->transitversion, $this->timeout, $xmlTransitRequest);
       return $this->_transitparseResult($xmlTransitResult);
   }

   //***************************************

   // GM 11-15-2004: modified to return array with time for each service, as
   //                opposed to single transit time for hardcoded "GND" code

   function _transitparseResult($xmlTransitResult) {
        $transitTime = array();

       // Parse XML message returned by the UPS post server.
       $doc = new XMLDocument();
       $xp = new XMLParser();
       $xp->setDocument($doc);
       $xp->parse($xmlTransitResult);
       $doc = $xp->getDocument();
       // Get version. Must be xpci version 1.0001 or this might not work.
       // 1.0001 and 1.0002 seem to be very similar, forget about this for the moment
       /*        $responseVersion = $doc->getValueByPath('TimeInTransitResponse/Response/TransactionReference/XpciVersion');
       if ($this->transitxpci_version != $responseVersion) {
           $message = MODULE_SHIPPING_UPSXML_RATES_TEXT_COMM_VERSION_ERROR;
           return $message;
       } */
       // Get response code. 1 = SUCCESS, 0 = FAIL
       $responseStatusCode = $doc->getValueByPath('TimeInTransitResponse/Response/ResponseStatusCode');
       if ($responseStatusCode != '1') {
           $errorMsg = $doc->getValueByPath('TimeInTransitResponse/Response/Error/ErrorCode');
           $errorMsg .= ": ";
           $errorMsg .= $doc->getValueByPath('TimeInTransitResponse/Response/Error/ErrorDescription');
           // send email if enabled in the admin section
           if ($this->email_errors) {
               error_log("UPSXML TimeInTransit Error: " . $errorMsg . " experienced by customer with id " . $_SESSION['customer_id'] . " on " . date('Y-m-d H:i:s'), 1, STORE_OWNER_EMAIL_ADDRESS);
           }
           // log errors to file ups_error.log when set
           if ($this->ups_error_file) {
               error_log(date('Y-m-d H:i:s')."\tTimeInTransit\t" . $errorMsg . "\t" . $_SESSION['customer_id'] ."\n", 3, $this->ups_error_file);    
           }
           return $errorMsg;
       }
       $root = $doc->getRoot();
       $rootChildren = $root->getChildren();
       for ($r = 0; $r < count($rootChildren); $r++) {
           $elementName = $rootChildren[$r]->getName();
           if ($elementName == "TransitResponse") {
               $transitResponse = $root->getElementsByName("TransitResponse");
               $serviceSummary = $transitResponse['0']->getElementsByName("ServiceSummary");
               $this->numberServices = count($serviceSummary);
               for ($s = 0; $s < $this->numberServices ; $s++) {
                   // index by Desc because that's all we can relate back to the service with
                   // (though it can probably return the code as well..)
                   $serviceDesc = $serviceSummary[$s]->getValueByPath("Service/Description");
                   // hack to get EDD for UPS Saver recognized (Time in Transit uses UPS Worldwide Saver
                   // but the service in Rates and Services is called UPS Saver)
                   if ($serviceDesc == "UPS Worldwide Saver") {
                     $serviceDesc = "UPS Saver";
                   }
                   $transitTime[$serviceDesc]["days"] = $serviceSummary[$s]->getValueByPath("EstimatedArrival/BusinessTransitDays");
                   $transitTime[$serviceDesc]["date"] = $serviceSummary[$s]->getValueByPath("EstimatedArrival/Date");
                   $transitTime[$serviceDesc]["guaranteed"] = $serviceSummary[$s]->getValueByPath("Guaranteed/Code");
               }
           }
       }
       if ($this->logfile) {
           error_log("------------------------------------------\n", 3, $this->logfile);
           foreach($transitTime as $desc => $time) {
               error_log("Business Transit: " . $desc ." = ". $time["date"] . "\n", 3, $this->logfile);
           }
       }
       return $transitTime;
   }

   //EOF Time In Transit
//  ***************************
 function exclude_choices($type, $vendors_id) {
   // used for exclusion of UPS shipping options, disallowed types are read from db
   $disallowed_types = explode(",", @constant('MODULE_SHIPPING_UPSXML_TYPES_' . $vendors_id));
   if (strstr($type, "UPS")) {
       // this will chop off "UPS" from the beginning of the line - typically something like UPS Next Day Air (1 Business Days)
       $type_minus_ups = explode("UPS", $type );
       $type_root = trim($type_minus_ups[1]);
   } // end if (strstr($type, "UPS"):
   else { // service description does not contain UPS (unlikely)
       $type_root = trim($type);
   }
   for ($za = 0; $za < count ($disallowed_types); $za++ ) {
       if ($type_root == trim($disallowed_types[$za])) {
           return true;
           exit;
       } // end if ($type_root == $disallowed_types[$za] ...
   }
   // if the type is not disallowed:
   return false;
 }

 function more_dimensions_to_productsArray($productsArray) {
$counter = 0;
  foreach ($productsArray as $key => $product) {
       // in case by accident or by choice length, width or height is not set
       // we will estimate it by using a set density and the product['weight'] variable
       // will only be used in the check for whether it fits the largest box
       // after that it will already be set, if product['weight'] is set at least
       if ($product['length'] == 0 || $product['width'] == 0 || $product['height'] == 0) {
           $density = 0.7;
           if ($this->unit_length == 'CM') {
               $product['length']=$product['width']=$product['height']= round(10*(pow($product['weight']/$density, 1/3)),1);
           } else {
               // non-metric: inches and pounds
               $product['length']=$product['width']=$product['height']= round(pow($product['weight']*27.67/$density, 1/3),1);
           }
       } // end if ($product['length'] == 0 || $product['width'] == 0 etc.
			// sort dimensions from low to high, used in the function fitsInBox
			   $dimensions = array($product['length'], $product['width'], $product['height']);
           sort($dimensions);
					foreach($dimensions as $key => $value) {
						if ($key == 0 ) { $productsArray[$counter]['x'] = $value; }
						if ($key == 1 ) { $productsArray[$counter]['y'] = $value; }
						if ($key == 2 ) { $productsArray[$counter]['z'] = $value; }
					}
       $productsArray[$counter]['volume'] = $product['length'] * $product['width'] * $product['height'];
			$counter++;
  } // end foreach ($productsArray as $key => $product)

return($productsArray);
 }

} // end class upsxml
// ******************************
function ready_to_shipCmp( $a, $b) {
   if ( $a['ready_to_ship'] == $b['ready_to_ship'] )
   return 0;
   if ( $a['ready_to_ship'] > $b['ready_to_ship'] )
   return -1;
   return 1;
}
?>

Share this post


Link to post
Share on other sites

Is there anyway to get a copy of the email that's going to the vendor? I tried adding a second address the the Vendors Email field (with space and with a comma) but that just caused an email error. I really want a copy of what gets sent to the vendor for my records.

 

RonR

Share this post


Link to post
Share on other sites

Just tried your demo and when I check out I get the following errors...

 

Warning: Invalid argument supplied for foreach() in /home/bcsales/public_html/mvs/checkout_process.php on line 125

 

Warning: Cannot modify header information - headers already sent by (output started at /home/bcsales/public_html/mvs/checkout_process.php:125) in /home/bcsales/public_html/mvs/includes/functions/general.php on line 33

 

and when I get the email through it looks like this (sorry dont have time to read 151 pages incase this has already been mentioned)

 

MVS0

------------------------------------------------------

Order Number: 34

Detailed Invoice: http://blucollarsales.com/mvs/account_hist...php?order_id=34

Date Ordered: Thursday 31 May, 2007

 

Products

------------------------------------------------------

There will be at least 0 packages shipped.

------------------------------------------------------

Sub-Total: $330.97

Flat Rate (Best Way): $5.00

Total: $335.97

 

Delivery Address

------------------------------------------------------

fred fred

32 test

test, tS6666F

test, United Kingdom

 

Billing Address

------------------------------------------------------

fred fred

32 test

test, tS6666F

test, United Kingdom

 

Payment Method

------------------------------------------------------

Cash on Delivery

Edited by zentac

Share this post


Link to post
Share on other sites

Hi All,

 

Sorry, didn't get a chance to post here yesterday. Got called away by a client.

 

To anyone that may have missed it, I uploaded an updated version of the upsxml module that supports both MVS and dimensional support to the MVS 1.0 contrib page yesterday. It is based off of the upsxml version 1.2.6.

 

If you have any problems with it, please let us know here. I have installed it on a highly modified MVS store and a clean MVS install and both are working just fine.

 

Katrina

Share this post


Link to post
Share on other sites
Hi All,

 

Sorry, didn't get a chance to post here yesterday. Got called away by a client.

 

To anyone that may have missed it, I uploaded an updated version of the upsxml module that supports both MVS and dimensional support to the MVS 1.0 contrib page yesterday. It is based off of the upsxml version 1.2.6.

 

If you have any problems with it, please let us know here. I have installed it on a highly modified MVS store and a clean MVS install and both are working just fine.

 

Katrina

 

 

Hey Katrina,

 

Thanks You so much for uploading that, I was trying to convert version 1.2.8 but was running into issues.

 

I installed your version but there seems to be some issues with what seems to stem from insurance and product dimensions. I get this error in checkout_shipping.php

 

Warning: constant() [function.constant]: Couldn't find constant MODULE_SHIPPING_UPSXML_INSURE_ in /home/grdstore/public_html/includes/modules/vendors_shipping/upsxml.php on line 420

 

I can get the errors to go away if I change my admin option for product dimensions support from ready to ship to product dimension. In admin the line Account Number doesn't appear above the input box for reduced rates.

 

Also if I am trying to checkout with products from multiple vendors it doesn't divided the amount of packages and the weight between vendors, it shows the total amount for both on each vendor quote.

 

Any help would be greatly appreciated with this, Thanks in advance.

 

-Jeff

Share this post


Link to post
Share on other sites

In the base MVS includes/modules/vendors_shipping directory, there are two UPS modules, ups.php and upsxml.php. I have one site that uses the ups.php within MVS and it works great. Estimates are nearly perfect-unlike the upsxml.php module.

 

I have another non-MVS site that I would like to incorporate the ups.php module into instead of the upsxml.php bu I cannot find the contrib for it in the list. Could anyone tell me where this comes from (ups.php) for MVS? Was it developered for MVS only or is it available for a non-MVS site?

Share this post


Link to post
Share on other sites
Just tried your demo and when I check out I get the following errors...

 

Warning: Invalid argument supplied for foreach() in /home/bcsales/public_html/mvs/checkout_process.php on line 125

 

Warning: Cannot modify header information - headers already sent by (output started at /home/bcsales/public_html/mvs/checkout_process.php:125) in /home/bcsales/public_html/mvs/includes/functions/general.php on line 33

 

and when I get the email through it looks like this (sorry dont have time to read 151 pages incase this has already been mentioned)

 

MVS0

------------------------------------------------------

Order Number: 34

Detailed Invoice: http://blucollarsales.com/mvs/account_hist...php?order_id=34

Date Ordered: Thursday 31 May, 2007

 

Products

------------------------------------------------------

There will be at least 0 packages shipped.

------------------------------------------------------

Sub-Total: $330.97

Flat Rate (Best Way): $5.00

Total: $335.97

 

Delivery Address

------------------------------------------------------

fred fred

32 test

test, tS6666F

test, United Kingdom

 

Billing Address

------------------------------------------------------

fred fred

32 test

test, tS6666F

test, United Kingdom

 

Payment Method

------------------------------------------------------

Cash on Delivery

I had been working with some modifications and hadn't updated it back to functioning yet. My bad, will try to get it back to normal again by the weekend.

 

Craig :)


Happy Coding!

Craig Garrison Sr

Anything worth having, is worth working for.

Multi Vendor Shipping V1.1 Demo Catalog

3 Vendors, each category, "buy" a product from each category to see how MVS works during checkout.

Multi Vendor Shipping V1.1 Demo Admin

login: webmaster@blucollarsales.com

pass: mvs_demo

MVS Thread:

Multi-Vendor Shipping

My contribs:

Download Multi Vendor Shipping V1.1

Vendor Email

Vendor Info in easypopulate

EZ Price Updater

And more to come!

Share this post


Link to post
Share on other sites
In the base MVS includes/modules/vendors_shipping directory, there are two UPS modules, ups.php and upsxml.php. I have one site that uses the ups.php within MVS and it works great. Estimates are nearly perfect-unlike the upsxml.php module.

 

I have another non-MVS site that I would like to incorporate the ups.php module into instead of the upsxml.php bu I cannot find the contrib for it in the list. Could anyone tell me where this comes from (ups.php) for MVS? Was it developered for MVS only or is it available for a non-MVS site?

That was UPS Choice from the contributions page. That is a very old module, and I am a bit surprised it still functions at all. But I use that one my self, and agree that for some reason, the rates it returns do seem to be more accurate.

 

Craig :)


Happy Coding!

Craig Garrison Sr

Anything worth having, is worth working for.

Multi Vendor Shipping V1.1 Demo Catalog

3 Vendors, each category, "buy" a product from each category to see how MVS works during checkout.

Multi Vendor Shipping V1.1 Demo Admin

login: webmaster@blucollarsales.com

pass: mvs_demo

MVS Thread:

Multi-Vendor Shipping

My contribs:

Download Multi Vendor Shipping V1.1

Vendor Email

Vendor Info in easypopulate

EZ Price Updater

And more to come!

Share this post


Link to post
Share on other sites
<snip> Could anyone tell me where this comes from (ups.php) for MVS? Was it developered for MVS only or is it available for a non-MVS site?

The UPS module in the MVS distribution is a modified version of the UPS Choice Contribution.

 

Regards

Jim


See my profile for a list of my addons and ways to get support.

Share this post


Link to post
Share on other sites
That was UPS Choice from the contributions page. That is a very old module, and I am a bit surprised it still functions at all. But I use that one my self, and agree that for some reason, the rates it returns do seem to be more accurate.

 

Craig :)

Are you using a modified version beyond the latest release of UPS Choice?

Share this post


Link to post
Share on other sites
Are you using a modified version beyond the latest release of UPS Choice?

As far back as I can remember, it was the latest release that we used.

 

It is still functional, and works very nicely. What I would like to do ultimately is re-write the UPS and FedEx modules to be a bit more effiecient, but that will take a lot of time, and I have been short on time.

 

Good luck with your shop, Craig :)


Happy Coding!

Craig Garrison Sr

Anything worth having, is worth working for.

Multi Vendor Shipping V1.1 Demo Catalog

3 Vendors, each category, "buy" a product from each category to see how MVS works during checkout.

Multi Vendor Shipping V1.1 Demo Admin

login: webmaster@blucollarsales.com

pass: mvs_demo

MVS Thread:

Multi-Vendor Shipping

My contribs:

Download Multi Vendor Shipping V1.1

Vendor Email

Vendor Info in easypopulate

EZ Price Updater

And more to come!

Share this post


Link to post
Share on other sites

Multi Vendor Shipping not applying tax.

 

Has anyone else experienced a problem with the multivendor shipp applying tax? I am trying to get it to work for a UK site, but whilst it applies different shipping methods for different vendors etc, it will not take any notice of the 'vat' tax I am trying to apply to it. I don't really know what the best method would be to start investigating this issue. Any help would be appreciated.

 

Thanks.

Share this post


Link to post
Share on other sites
Multi Vendor Shipping not applying tax.

 

Has anyone else experienced a problem with the multivendor shipp applying tax? I am trying to get it to work for a UK site, but whilst it applies different shipping methods for different vendors etc, it will not take any notice of the 'vat' tax I am trying to apply to it. I don't really know what the best method would be to start investigating this issue. Any help would be appreciated.

 

Thanks.

Have you assigned a tax class to each shipping module?

Do, or do not. There is no try.

 

Order Editor 5.0.6 "Ultra Violet" is now available!

For support or to post comments, suggestions, etc, please visit the Order Editor support thread.

Share this post


Link to post
Share on other sites

HI,

 

Yes I have assigned Vat class in the individual mopdules for each vendor - e.g. vendor A has table rate as his method of shipping and that has the VAT class set on it. When I get to chckout in the store however, the tax does not apply to0 the shipping.

 

So to try and investigate - so far I have tried the following.

 

install MVS on afresh install of the shop - to be sure that I could get it working with tax as it should - it worked. I am making the assumption this will cut out any issues potentially caused by php version of suchlike.

 

compare of all files in my shop with the files in the latest release to confirm all code is in the right place - it is.

 

this effectively leaves me with any contributions already added that may be causing conflist - however, no code on any of the other modules is in the same place as MVS so I haven't had any code issues with putting the files together. so basically I am stuck.

 

For reference the contributions I have installed thus far are - seperate pricing per customer (though this did cause me an issue so I removed it), short description in product list, featured products, header tags, improved attribute manager and family products.

 

Hope someone can help! Thanks! J

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×