uc_ups.module in Ubercart 6.2
Same filename and directory in other branches
Shipping quote module that interfaces with www.ups.com to get rates for small package shipments.
File
shipping/uc_ups/uc_ups.moduleView source
<?php
/**
* @file
* Shipping quote module that interfaces with www.ups.com to get rates for small
* package shipments.
*/
/******************************************************************************
* Drupal Hooks *
******************************************************************************/
/**
* Implements hook_menu().
*/
function uc_ups_menu() {
$items = array();
$items['admin/store/settings/quotes/methods/ups'] = array(
'title' => 'UPS',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'uc_ups_admin_settings',
),
'access arguments' => array(
'configure quotes',
),
'type' => MENU_LOCAL_TASK,
'file' => 'uc_ups.admin.inc',
);
$items['admin/store/orders/%uc_order/shipments/ups'] = array(
'title' => 'UPS shipment',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'uc_ups_confirm_shipment',
3,
),
'access arguments' => array(
'fulfill orders',
),
'type' => MENU_CALLBACK,
'file' => 'uc_ups.ship.inc',
);
$items['admin/store/orders/%uc_order/shipments/labels/ups'] = array(
'page callback' => 'theme',
'page arguments' => array(
'uc_ups_label_image',
),
'access arguments' => array(
'fulfill orders',
),
'type' => MENU_CALLBACK,
'file' => 'uc_ups.ship.inc',
);
return $items;
}
/**
* Implements hook_init().
*/
function uc_ups_init() {
drupal_add_css(drupal_get_path('module', 'uc_ups') . '/uc_ups.css');
}
/**
* Implements hook_cron().
*
* Deletes UPS shipping labels from the file system automatically
* on a periodic basis. Cron must be enabled for automatic deletion.
* Default is never delete the labels, keep them forever.
*/
function uc_ups_cron() {
$current_time = time();
$cutoff = $current_time - variable_get('uc_ups_label_lifetime', 0);
if ($cutoff == $current_time) {
// Label lifetime is set to 0, meaning never delete
return;
}
$path = file_create_path('ups_labels');
if (is_dir($path)) {
$files = array_diff(scandir($path), array(
'.',
'..',
));
if ($files) {
// Loop over label files in sites/default/files/ups_labels
// and test creation date against 'uc_ups_label_lifetime'
foreach ($files as $file) {
if ($cutoff > filectime($path . '/' . $file)) {
unlink($path . '/' . $file);
}
}
}
}
}
/**
* Implements hook_theme().
*/
function uc_ups_theme() {
return array(
'uc_ups_option_label' => array(
'arguments' => array(
'service' => NULL,
'packages' => NULL,
),
),
'uc_ups_confirm_shipment' => array(
'arguments' => array(
'form' => NULL,
),
'file' => 'uc_ups.ship.inc',
),
'uc_ups_label_image' => array(
'arguments' => array(),
'file' => 'uc_ups.ship.inc',
),
);
}
/**
* Implements hook_form_alter().
*
* Adds package type to products.
*
* @see uc_product_form()
* @see uc_ups_product_alter_validate()
*/
function uc_ups_form_alter(&$form, $form_state, $form_id) {
if (uc_product_is_product_form($form)) {
$node = $form['#node'];
$enabled = variable_get('uc_quote_enabled', array());
$weight = variable_get('uc_quote_method_weight', array(
'ups' => 0,
));
$ups = array(
'#type' => 'fieldset',
'#title' => t('UPS product description'),
'#collapsible' => TRUE,
'#collapsed' => $enabled['ups'] == FALSE || uc_product_get_shipping_type($node) != 'small_package',
'#weight' => $weight['ups'],
'#tree' => TRUE,
);
$ups['pkg_type'] = array(
'#type' => 'select',
'#title' => t('Package type'),
'#options' => _uc_ups_pkg_types(),
'#default_value' => isset($node->ups['pkg_type']) ? $node->ups['pkg_type'] : variable_get('uc_ups_pkg_type', '02'),
);
$form['shipping']['ups'] = $ups;
if ($enabled['ups']) {
$form['#validate'][] = 'uc_ups_product_alter_validate';
}
}
}
/**
* Validation handler for UPS product fields.
*
* @see uc_ups_form_alter()
*/
function uc_ups_product_alter_validate($form, &$form_state) {
$enabled = variable_get('uc_quote_enabled', array());
if ($form_state['values']['shippable'] && ($form_state['values']['shipping_type'] == 'small_package' || empty($form_state['values']['shipping_type']) && variable_get('uc_store_shipping_type', 'small_package') == 'small_package')) {
if ($form_state['values']['ups']['pkg_type'] == '02' && (empty($form_state['values']['dim_length']) || empty($form_state['values']['dim_width']) || empty($form_state['values']['dim_height']))) {
form_set_error('base][dimensions', t('Dimensions are required for custom packaging.'));
}
}
}
/**
* Implements hook_nodeapi().
*/
function uc_ups_nodeapi(&$node, $op) {
if (uc_product_is_product($node->type)) {
switch ($op) {
case 'insert':
case 'update':
if (isset($node->ups)) {
$ups_values = $node->ups;
if (!$node->revision) {
db_query("DELETE FROM {uc_ups_products} WHERE vid = %d", $node->vid);
}
db_query("INSERT INTO {uc_ups_products} (vid, nid, pkg_type) VALUES (%d, %d, '%s')", $node->vid, $node->nid, $ups_values['pkg_type']);
}
break;
case 'load':
if (uc_product_get_shipping_type($node) == 'small_package') {
return array(
'ups' => db_fetch_array(db_query("SELECT * FROM {uc_ups_products} WHERE vid = %d", $node->vid)),
);
}
break;
case 'delete':
db_query("DELETE FROM {uc_ups_products} WHERE nid = %d", $node->nid);
break;
case 'delete revision':
db_query("DELETE FROM {uc_ups_products} WHERE vid = %d", $node->vid);
break;
}
}
}
/******************************************************************************
* Conditional Actions Hooks *
******************************************************************************/
/**
* Implements hook_ca_predicate().
*
* Connect the UPS quote action and event.
*/
function uc_ups_ca_predicate() {
$enabled = variable_get('uc_quote_enabled', array());
$predicates = array(
'uc_ups_get_quote' => array(
'#title' => t('Shipping quote from UPS'),
'#trigger' => 'get_quote_from_ups',
'#class' => 'uc_ups',
'#status' => $enabled['ups'],
'#actions' => array(
array(
'#name' => 'uc_quote_action_get_quote',
'#title' => t('Fetch a shipping quote'),
'#argument_map' => array(
'order' => 'order',
'method' => 'method',
),
),
),
),
);
return $predicates;
}
/******************************************************************************
* Ubercart Hooks *
******************************************************************************/
/**
* Implements hook_shipping_type().
*/
function uc_ups_shipping_type() {
$weight = variable_get('uc_quote_type_weight', array(
'small_package' => 0,
));
$types = array();
$types['small_package'] = array(
'id' => 'small_package',
'title' => t('Small packages'),
'weight' => $weight['small_package'],
);
return $types;
}
/**
* Implements hook_shipping_method().
*/
function uc_ups_shipping_method() {
$methods = array();
$enabled = variable_get('uc_quote_enabled', array());
$weight = variable_get('uc_quote_method_weight', array(
'ups' => 0,
));
$methods['ups'] = array(
'id' => 'ups',
'module' => 'uc_ups',
'title' => t('UPS'),
'enabled' => $enabled['ups'],
'quote' => array(
'type' => 'small_package',
'callback' => 'uc_ups_quote',
'accessorials' => _uc_ups_service_list(),
),
'ship' => array(
'type' => 'small_package',
'callback' => 'uc_ups_fulfill_order',
'file' => 'uc_ups.ship.inc',
'pkg_types' => _uc_ups_pkg_types(),
),
'cancel' => 'uc_ups_void_shipment',
'weight' => $weight['ups'],
);
return $methods;
}
/**
* Implements hook_store_status().
*
* Let the administrator know that the UPS account information has not been
* filled out.
*/
function uc_ups_store_status() {
$messages = array();
$access = variable_get('uc_ups_access_license', '') != '';
$account = variable_get('uc_ups_shipper_number', '') != '';
$user = variable_get('uc_ups_user_id', '') != '';
$password = variable_get('uc_ups_password', '') != '';
if ($access && $account && $user && $password) {
$messages[] = array(
'status' => 'ok',
'title' => t('UPS Online Tools'),
'desc' => t('Information needed to access UPS Online Tools has been entered.'),
);
}
else {
$messages[] = array(
'status' => 'error',
'title' => t('UPS Online Tools'),
'desc' => t('More information is needed to access UPS Online Tools. Please enter it <a href="!url">here</a>.', array(
'!url' => url('admin/store/settings/quotes/methods/ups'),
)),
);
}
return $messages;
}
/******************************************************************************
* Module Functions *
******************************************************************************/
/**
* Return XML access request to be prepended to all requests to the
* UPS webservice.
*/
function uc_ups_access_request() {
$access = variable_get('uc_ups_access_license', '');
$user = variable_get('uc_ups_user_id', '');
$password = variable_get('uc_ups_password', '');
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<AccessRequest xml:lang=\"en-US\">\n <AccessLicenseNumber>{$access}</AccessLicenseNumber>\n <UserId>{$user}</UserId>\n <Password>{$password}</Password>\n</AccessRequest>\n";
}
/**
* Construct an XML quote request.
*
* @param $packages
* Array of packages received from the cart.
* @param $origin
* Delivery origin address information.
* @param $destination
* Delivery destination address information.
* @param $ups_service
* UPS service code (refers to UPS Ground, Next-Day Air, etc.).
*
* @return
* RatingServiceSelectionRequest XML document to send to UPS
*/
function uc_ups_shipping_quote($packages, $origin, $destination, $ups_service) {
$store['name'] = variable_get('uc_store_name', NULL);
$store['owner'] = variable_get('uc_store_owner', NULL);
$store['email'] = uc_store_email();
$store['email_from'] = uc_store_email();
$store['phone'] = variable_get('uc_store_phone', NULL);
$store['fax'] = variable_get('uc_store_fax', NULL);
$store['street1'] = variable_get('uc_store_street1', NULL);
$store['street2'] = variable_get('uc_store_street2', NULL);
$store['city'] = variable_get('uc_store_city', NULL);
$store['zone'] = variable_get('uc_store_zone', NULL);
$store['postal_code'] = variable_get('uc_store_postal_code', NULL);
$store['country'] = variable_get('uc_store_country', 840);
$account = variable_get('uc_ups_shipper_number', '');
$ua = explode(' ', $_SERVER['HTTP_USER_AGENT']);
$user_agent = $ua[0];
$services = _uc_ups_service_list();
$service = array(
'code' => $ups_service,
'description' => $services[$ups_service],
);
$pkg_types = _uc_ups_pkg_types();
$shipper_zone = uc_get_zone_code($store['zone']);
$shipper_country = uc_get_country_data(array(
'country_id' => $store['country'],
));
$shipper_country = $shipper_country[0]['country_iso_code_2'];
$shipper_zip = $store['postal_code'];
$shipto_zone = uc_get_zone_code($destination->zone);
$shipto_country = uc_get_country_data(array(
'country_id' => $destination->country,
));
$shipto_country = $shipto_country[0]['country_iso_code_2'];
$shipto_zip = $destination->postal_code;
$shipfrom_zone = uc_get_zone_code($origin->zone);
$shipfrom_country = uc_get_country_data(array(
'country_id' => $origin->country,
));
$shipfrom_country = $shipfrom_country[0]['country_iso_code_2'];
$shipfrom_zip = $origin->postal_code;
$ups_units = variable_get('uc_ups_unit_system', variable_get('uc_length_unit', 'in'));
switch ($ups_units) {
case 'in':
$units = 'LBS';
$unit_name = 'Pounds';
break;
case 'cm':
$units = 'KGS';
$unit_name = 'Kilograms';
break;
}
$shipment_weight = 0;
$package_schema = '';
foreach ($packages as $package) {
// Determine length conversion factor and weight conversion factor
// for this shipment
$length_factor = uc_length_conversion($package->length_units, variable_get('uc_ups_unit_system', variable_get('uc_length_unit', 'in')));
switch ($ups_units) {
case 'in':
$weight_factor = uc_weight_conversion($package->weight_units, 'lb');
break;
case 'cm':
$weight_factor = uc_weight_conversion($package->weight_units, 'kg');
break;
}
// Loop over quantity of packages in this shipment
$qty = $package->qty;
for ($i = 0; $i < $qty; $i++) {
// Build XML for this package
$package_type = array(
'code' => $package->pkg_type,
'description' => $pkg_types[$package->pkg_type],
);
$package_schema .= "<Package>";
$package_schema .= "<PackagingType>";
$package_schema .= "<Code>" . $package_type['code'] . "</Code>";
$package_schema .= "</PackagingType>";
if ($package->pkg_type == '02' && $package->length && $package->width && $package->height) {
if ($package->length < $package->width) {
list($package->length, $package->width) = array(
$package->width,
$package->length,
);
}
$package_schema .= "<Dimensions>";
$package_schema .= "<UnitOfMeasurement>";
$package_schema .= "<Code>" . strtoupper(variable_get('uc_ups_unit_system', variable_get('uc_length_unit', 'in'))) . "</Code>";
$package_schema .= "</UnitOfMeasurement>";
$package_schema .= "<Length>" . number_format($package->length * $length_factor, 2, '.', '') . "</Length>";
$package_schema .= "<Width>" . number_format($package->width * $length_factor, 2, '.', '') . "</Width>";
$package_schema .= "<Height>" . number_format($package->height * $length_factor, 2, '.', '') . "</Height>";
$package_schema .= "</Dimensions>";
}
$size = $package->length * $length_factor + 2 * $length_factor * ($package->width + $package->height);
$weight = max(1, $package->weight * $weight_factor);
$shipment_weight += $weight;
$package_schema .= "<PackageWeight>";
$package_schema .= "<UnitOfMeasurement>";
$package_schema .= "<Code>" . $units . "</Code>";
$package_schema .= "<Description>" . $unit_name . "</Description>";
$package_schema .= "</UnitOfMeasurement>";
$package_schema .= "<Weight>" . number_format($weight, 1, '.', '') . "</Weight>";
$package_schema .= "</PackageWeight>";
if ($size > 130 && $size <= 165) {
$package_schema .= "<LargePackageIndicator/>";
}
if (variable_get('uc_ups_insurance', TRUE)) {
$package_schema .= "<PackageServiceOptions>";
$package_schema .= "<InsuredValue>";
$package_schema .= "<CurrencyCode>" . variable_get('uc_currency_code', 'USD') . "</CurrencyCode>";
$package_schema .= "<MonetaryValue>" . $package->price . "</MonetaryValue>";
$package_schema .= "</InsuredValue>";
$package_schema .= "</PackageServiceOptions>";
}
$package_schema .= "</Package>";
}
}
$schema = uc_ups_access_request() . "\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<RatingServiceSelectionRequest xml:lang=\"en-US\">\n <Request>\n <TransactionReference>\n <CustomerContext>Complex Rate Request</CustomerContext>\n <XpciVersion>1.0001</XpciVersion>\n </TransactionReference>\n <RequestAction>Rate</RequestAction>\n <RequestOption>rate</RequestOption>\n </Request>\n <PickupType>\n <Code>" . variable_get('uc_ups_pickup_type', '01') . "</Code>\n </PickupType>\n <CustomerClassification>\n <Code>" . variable_get('uc_ups_classification', '04') . "</Code>\n </CustomerClassification>\n <Shipment>\n <Shipper>\n <ShipperNumber>" . variable_get('uc_ups_shipper_number', '') . "</ShipperNumber>\n <Address>\n <City>" . $store['city'] . "</City>\n <StateProvinceCode>{$shipper_zone}</StateProvinceCode>\n <PostalCode>{$shipper_zip}</PostalCode>\n <CountryCode>{$shipper_country}</CountryCode>\n </Address>\n </Shipper>\n <ShipTo>\n <Address>\n <StateProvinceCode>{$shipto_zone}</StateProvinceCode>\n <PostalCode>{$shipto_zip}</PostalCode>\n <CountryCode>{$shipto_country}</CountryCode>\n ";
if (variable_get('uc_ups_residential_quotes', 0)) {
$schema .= "<ResidentialAddressIndicator/>\n ";
}
$schema .= "</Address>\n </ShipTo>\n <ShipFrom>\n <Address>\n <StateProvinceCode>{$shipfrom_zone}</StateProvinceCode>\n <PostalCode>{$shipfrom_zip}</PostalCode>\n <CountryCode>{$shipfrom_country}</CountryCode>\n </Address>\n </ShipFrom>\n <ShipmentWeight>\n <UnitOfMeasurement>\n <Code>{$units}</Code>\n <Description>{$unit_name}</Description>\n </UnitOfMeasurement>\n <Weight>" . number_format($shipment_weight, 1, '.', '') . "</Weight>\n </ShipmentWeight>\n <Service>\n <Code>{$service['code']}</Code>\n <Description>{$service['description']}</Description>\n </Service>\n ";
$schema .= $package_schema;
if (variable_get('uc_ups_negotiated_rates', FALSE)) {
$schema .= "<RateInformation>\n <NegotiatedRatesIndicator/>\n </RateInformation>";
}
$schema .= "</Shipment>\n</RatingServiceSelectionRequest>";
return $schema;
}
/**
* Callback for retrieving a UPS shipping quote.
*
* Request a quote for each enabled UPS Service. Therefore, the quote will
* take longer to display to the user for each option the customer has
* available.
*
* @param $products
* Array of cart contents.
* @param $details
* Order details other than product information.
*
* @return
* JSON object containing rate, error, and debugging information.
*/
function uc_ups_quote($products, $details) {
$quotes = array();
$method = uc_ups_shipping_method();
$addresses = array(
(array) variable_get('uc_quote_store_default_address', new stdClass()),
);
$key = 0;
$last_key = 0;
$packages = array();
if (variable_get('uc_ups_all_in_one', TRUE) && count($products) > 1) {
foreach ($products as $product) {
if ($product->nid) {
// Packages are grouped by the address from which they will be
// shipped. We will keep track of the different addresses in an array
// and use their keys for the array of packages.
$address = (array) uc_quote_get_default_shipping_address($product->nid);
$key = array_search($address, $addresses);
if ($key === FALSE) {
// This is a new address. Increment the address counter $last_key
// instead of using [] so that it can be used in $packages and
// $addresses.
$addresses[++$last_key] = $address;
$key = $last_key;
}
}
// Add this product to the last package from the found address or start
// a new package.
if (isset($packages[$key]) && count($packages[$key])) {
$package = array_pop($packages[$key]);
}
else {
$package = _uc_ups_new_package();
}
$weight = $product->weight * $product->qty * uc_weight_conversion($product->weight_units, 'lb');
$package->weight += $weight;
$package->price += $product->price * $product->qty;
$length_factor = uc_length_conversion($product->length_units, 'in');
$package->length = max($product->length * $length_factor, $package->length);
$package->width = max($product->width * $length_factor, $package->width);
$package->height = max($product->height * $length_factor, $package->height);
$packages[$key][] = $package;
}
foreach ($packages as $addr_key => $shipment) {
foreach ($shipment as $key => $package) {
if (!$package->weight) {
unset($packages[$addr_key][$key]);
continue;
}
elseif ($package->weight > 150) {
// UPS has a weight limit on packages of 150 lbs. Pretend the
// products can be divided into enough packages.
$qty = floor($package->weight / 150) + 1;
$package->qty = $qty;
$package->weight /= $qty;
$package->price /= $qty;
}
}
}
}
else {
foreach ($products as $product) {
$key = 0;
if ($product->nid) {
$address = (array) uc_quote_get_default_shipping_address($product->nid);
$key = array_search($address, $addresses);
if ($key === FALSE) {
$addresses[++$last_key] = $address;
$key = $last_key;
}
}
if (!$product->pkg_qty) {
$product->pkg_qty = 1;
}
$num_of_pkgs = (int) ($product->qty / $product->pkg_qty);
if ($num_of_pkgs) {
$package = drupal_clone($product);
$package->description = $product->model;
$package->weight = $product->weight * $product->pkg_qty;
$package->price = $product->price * $product->pkg_qty;
$package->qty = $num_of_pkgs;
$package->pkg_type = $product->ups ? $product->ups['pkg_type'] : '02';
if ($package->weight) {
$packages[$key][] = $package;
}
}
$remaining_qty = $product->qty % $product->pkg_qty;
if ($remaining_qty) {
$package = drupal_clone($product);
$package->description = $product->model;
$package->weight = $product->weight * $remaining_qty;
$package->price = $product->price * $remaining_qty;
$package->qty = 1;
$package->pkg_type = $product->ups ? $product->ups['pkg_type'] : '02';
if ($package->weight) {
$packages[$key][] = $package;
}
}
}
}
if (!count($packages)) {
return array();
}
$dest = (object) $details;
foreach ($packages as $key => $ship_packages) {
$orig = (object) $addresses[$key];
$orig->email = uc_store_email();
foreach (array_keys(array_filter(variable_get('uc_ups_services', array()))) as $ups_service) {
$request = uc_ups_shipping_quote($ship_packages, $orig, $dest, $ups_service);
$resp = drupal_http_request(variable_get('uc_ups_connection_address', 'https://wwwcie.ups.com/ups.app/xml/') . 'Rate', array(), 'POST', $request);
if (user_access('configure quotes') && variable_get('uc_quote_display_debug', FALSE)) {
if (!isset($quotes[$usps_service]['debug'])) {
$quotes[$usps_service]['debug'] = '';
}
$quotes[$ups_service]['debug'] .= htmlentities($request) . ' <br /><br /> ' . htmlentities($resp->data);
}
$response = new SimpleXMLElement($resp->data);
if (isset($response->Response->Error)) {
foreach ($response->Response->Error as $error) {
if (user_access('configure quotes') && variable_get('uc_quote_display_debug', FALSE)) {
$quotes[$ups_service]['error'][] = (string) $error->ErrorSeverity . ' ' . (string) $error->ErrorCode . ': ' . (string) $error->ErrorDescription;
}
if (strpos((string) $error->ErrorSeverity, 'Hard') !== FALSE) {
// All or nothing quote. If some products can't be shipped by
// a certain service, no quote is given for that service. If
// that means no quotes are given at all, they'd better call in.
unset($quotes[$ups_service]['rate']);
}
}
}
// if NegotiatedRates exist, quote based on those, otherwise, use TotalCharges
if (isset($response->RatedShipment)) {
$charge = $response->RatedShipment->TotalCharges;
if (isset($response->RatedShipment->NegotiatedRates)) {
$charge = $response->RatedShipment->NegotiatedRates->NetSummaryCharges->GrandTotal;
}
if (!isset($charge->CurrencyCode) || (string) $charge->CurrencyCode == variable_get('uc_currency_code', "USD")) {
if (!isset($quotes[$ups_service]['rate'])) {
$quotes[$ups_service]['rate'] = 0;
}
$rate = uc_ups_markup((string) $charge->MonetaryValue);
$quotes[$ups_service]['rate'] += $rate;
}
}
}
}
uasort($quotes, 'uc_quote_price_sort');
$context = array(
'revision' => 'themed',
'type' => 'amount',
);
foreach ($quotes as $key => $quote) {
if (isset($quote['rate'])) {
$context['subject']['quote'] = $quote;
$context['revision'] = 'altered';
$quotes[$key]['rate'] = uc_price($quote['rate'], $context);
$context['revision'] = 'formatted';
$quotes[$key]['format'] = uc_price($quote['rate'], $context);
$quotes[$key]['option_label'] = theme('uc_ups_option_label', $method['ups']['quote']['accessorials'][$key], $packages);
}
}
/**
* Ugly hack to work around PHP bug, details here:
* http://bugs.php.net/bug.php?id=23220
* We strip out errors that look something like:
* warning: fread() [function.fread]: SSL fatal protocol error in...
* Copied from http://drupal.org/node/70915 and then improved by Lyle.
*/
$messages = drupal_set_message();
if (isset($messages['error'])) {
$errors = $messages['error'];
$total = count($errors);
for ($i = 0; $i < $total; $i++) {
if (strpos($errors[$i], 'SSL: fatal protocol error in')) {
unset($_SESSION['messages']['error'][$i]);
}
}
if (empty($_SESSION['messages']['error'])) {
unset($_SESSION['messages']['error']);
}
}
db_query("DELETE FROM {watchdog} WHERE type = 'php' AND variables LIKE '%%SSL: fatal protocol error%%'");
// End of ugly hack.
return $quotes;
}
/**
* Theme function to format the UPS service name and rate amount line-item
* shown to the customer.
*
* @param $service
* The UPS service name.
* @param $packages
* Package information.
*
* @ingroup themeable
*/
function theme_uc_ups_option_label($service, $packages) {
// Start with logo as required by the UPS terms of service.
$output = theme('image', drupal_get_path('module', 'uc_ups') . '/uc_ups_logo.gif', t('UPS logo'), '', array(
'class' => 'ups-logo',
));
// Add the UPS service name.
$output .= t('@service Rate', array(
'@service' => $service,
));
// Add package information
$output .= ' (' . format_plural(count($packages), '1 package', '@count packages') . ')';
return $output;
}
/**
* Constructs a void shipment request.
*
* @param $shipment_number
* The UPS shipment tracking number.
* @param $tracking_numbers
* Array of tracking numbers for individual packages in the shipment.
* Optional for shipments of only one package, as they have the same tracking
* number.
*
* @return
* XML VoidShipmentRequest message.
*/
function uc_ups_void_shipment_request($shipment_number, $tracking_numbers = array()) {
$schema = uc_ups_access_request();
$schema .= '<?xml version="1.0"?>';
$schema .= '<VoidShipmentRequest>';
$schema .= '<Request>';
$schema .= '<RequestAction>Void</RequestAction>';
$schema .= '<TransactionReference>';
$schema .= '<CustomerContext>';
$schema .= t('Void shipment @ship_number and tracking numbers @track_list', array(
'@ship_number' => $shipment_number,
'@track_list' => implode(', ', $tracking_numbers),
));
$schema .= '</CustomerContext>';
$schema .= '<XpciVersion>1.0</XpciVersion>';
$schema .= '</TransactionReference>';
$schema .= '</Request>';
$schema .= '<ExpandedVoidShipment>';
$schema .= '<ShipmentIdentificationNumber>' . $shipment_number . '</ShipmentIdentificationNumber>';
foreach ($tracking_numbers as $number) {
$schema .= '<TrackingNumber>' . $number . '</TrackingNumber>';
}
$schema .= '</ExpandedVoidShipment>';
$schema .= '</VoidShipmentRequest>';
return $schema;
}
/**
* Instructs UPS to cancel (in whole or in part) a shipment.
*
* @param $shipment_number
* The UPS shipment tracking number.
* @param $tracking_numbers
* Array of tracking numbers for individual packages in the shipment.
* Optional for shipments of only one package, as they have the same tracking
* number.
*
* @return
* TRUE if the shipment or packages were successfully voided.
*/
function uc_ups_void_shipment($shipment_number, $tracking_numbers = array()) {
$success = FALSE;
$request = uc_ups_void_shipment_request($shipment_number, $tracking_numbers);
$resp = drupal_http_request(variable_get('uc_ups_connection_address', 'https://wwwcie.ups.com/ups.app/xml/') . 'Void', array(), 'POST', $request);
$response = new SimpleXMLElement($resp->data);
if (isset($response->Response)) {
if (isset($response->Response->ResponseStatusCode)) {
$success = (string) $response->Response->ResponseStatusCode;
}
if (isset($response->Response->Error)) {
foreach ($response->Response->Error as $error) {
drupal_set_message((string) $error->ErrorSeverity . ' ' . (string) $error->ErrorCode . ': ' . (string) $error->ErrorDescription, 'error');
}
}
}
if (isset($response->Status)) {
if (isset($response->Status->StatusType)) {
$success = (string) $response->Status->StatusType->Code;
}
}
return (bool) $success;
}
/**
* Modifies the rate received from UPS before displaying to the customer.
*
* @param $rate
* Shipping rate without any rate markup.
*
* @return
* Shipping rate after markup.
*/
function uc_ups_markup($rate) {
$markup = variable_get('uc_ups_markup', '0');
$type = variable_get('uc_ups_markup_type', 'percentage');
if (is_numeric(trim($markup))) {
switch ($type) {
case 'percentage':
return $rate + $rate * floatval(trim($markup)) / 100;
case 'multiplier':
return $rate * floatval(trim($markup));
case 'currency':
return $rate + floatval(trim($markup));
}
}
else {
return $rate;
}
}
/**
* Convenience function to get UPS codes for their services.
*/
function _uc_ups_service_list() {
return array(
// Domestic services
'03' => t('UPS Ground'),
'01' => t('UPS Next Day Air'),
'13' => t('UPS Next Day Air Saver'),
'14' => t('UPS Next Day Early A.M.'),
'02' => t('UPS 2nd Day Air'),
'59' => t('UPS 2nd Day Air A.M.'),
'12' => t('UPS 3 Day Select'),
// International services
'11' => t('UPS Standard'),
'07' => t('UPS Worldwide Express'),
'08' => t('UPS Worldwide Expedited'),
'54' => t('UPS Worldwide Express Plus'),
'65' => t('UPS Worldwide Saver'),
);
}
/**
* Convenience function to get UPS codes for their package types.
*/
function _uc_ups_pkg_types() {
return array(
'01' => t('UPS Letter'),
'02' => t('Customer Supplied Package'),
'03' => t('Tube'),
'04' => t('PAK'),
'21' => t('UPS Express Box'),
'24' => t('UPS 25KG Box'),
'25' => t('UPS 10KG Box'),
'30' => t('Pallet'),
'2a' => t('Small Express Box'),
'2b' => t('Medium Express Box'),
'2c' => t('Large Express Box'),
);
}
/**
* Pseudo-constructor to set default values of a package.
*/
function _uc_ups_new_package() {
$package = new stdClass();
$package->weight = 0;
$package->price = 0;
$package->length = 0;
$package->width = 0;
$package->height = 0;
$package->length_units = 'in';
$package->weight_units = 'lb';
$package->qty = 1;
$package->pkg_type = '02';
return $package;
}
Functions
Name | Description |
---|---|
theme_uc_ups_option_label | Theme function to format the UPS service name and rate amount line-item shown to the customer. |
uc_ups_access_request | Return XML access request to be prepended to all requests to the UPS webservice. |
uc_ups_ca_predicate | Implements hook_ca_predicate(). |
uc_ups_cron | Implements hook_cron(). |
uc_ups_form_alter | Implements hook_form_alter(). |
uc_ups_init | Implements hook_init(). |
uc_ups_markup | Modifies the rate received from UPS before displaying to the customer. |
uc_ups_menu | Implements hook_menu(). |
uc_ups_nodeapi | Implements hook_nodeapi(). |
uc_ups_product_alter_validate | Validation handler for UPS product fields. |
uc_ups_quote | Callback for retrieving a UPS shipping quote. |
uc_ups_shipping_method | Implements hook_shipping_method(). |
uc_ups_shipping_quote | Construct an XML quote request. |
uc_ups_shipping_type | Implements hook_shipping_type(). |
uc_ups_store_status | Implements hook_store_status(). |
uc_ups_theme | Implements hook_theme(). |
uc_ups_void_shipment | Instructs UPS to cancel (in whole or in part) a shipment. |
uc_ups_void_shipment_request | Constructs a void shipment request. |
_uc_ups_new_package | Pseudo-constructor to set default values of a package. |
_uc_ups_pkg_types | Convenience function to get UPS codes for their package types. |
_uc_ups_service_list | Convenience function to get UPS codes for their services. |