uc_fedex.module in FedEx Shipping 6.2
Same filename and directory in other branches
FedEx Web Services Rate / Available Services Quote.
Shipping quote module that interfaces with the FedEx Web Services API to get rates for small package shipments. Implements a SOAP Web Service client.
@author Tim Rohaly. <http://drupal.org/user/202830>
File
uc_fedex.moduleView source
<?php
/**
* @file
* FedEx Web Services Rate / Available Services Quote.
*
* Shipping quote module that interfaces with the FedEx Web Services API
* to get rates for small package shipments. Implements a SOAP Web Service
* client.
*
* @author Tim Rohaly. <http://drupal.org/user/202830>
*/
/** Maximum shipping weight for FedEx (non-Freight services) */
define('PACKAGE_WEIGHT_LIMIT_LBS', 150.0);
// 150lbs
// Set to 0 to disable caching of SOAP WSDL when developing your WSDL
ini_set("soap.wsdl_cache_enabled", "1");
/******************************************************************************
* Drupal Hooks *
******************************************************************************/
/**
* Implements hook_menu().
*/
function uc_fedex_menu() {
$items = array();
$items['admin/store/settings/quotes/methods/fedex'] = array(
'title' => 'FedEx',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'uc_fedex_admin_settings',
),
'access arguments' => array(
'configure quotes',
),
'type' => MENU_LOCAL_TASK,
'file' => 'uc_fedex.admin.inc',
);
$items['admin/store/orders/%uc_order/shipments/fedex'] = array(
'title' => 'FedEx shipment',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'uc_fedex_confirm_shipment',
3,
),
'access arguments' => array(
'fulfill orders',
),
'type' => MENU_CALLBACK,
'file' => 'uc_fedex.ship.inc',
);
$items['admin/store/orders/%uc_order/shipments/labels/fedex'] = array(
'page callback' => 'uc_fedex_label_image',
'access arguments' => array(
'fulfill orders',
),
'type' => MENU_CALLBACK,
'file' => 'uc_fedex.ship.inc',
);
return $items;
}
/**
* Implements hook_init().
*
* Used to load module includes and CSS. This is the wrong place to
* add CSS because the CSS will be included on every page, not just on
* the pages where it is needed. However, this is currently the
* Ubercart-recommended place for adding shipping quotes CSS.
*/
function uc_fedex_init() {
drupal_add_css(drupal_get_path('module', 'uc_fedex') . '/uc_fedex.css');
// Can we conditionally load the address validation code based on
// variable_get('uc_fedex_address_validation', FALSE) ?
// Address Validation routines
module_load_include('inc', 'uc_fedex', 'uc_fedex.aval');
}
/**
* Implements hook_cron().
*
* Deletes FedEx shipping labels from the file system automatically
* on a periodic basis. Default period is 1 week.
*/
function uc_fedex_cron() {
$current_time = time();
$cutoff = $current_time - variable_get('uc_fedex_label_lifetime', 604800);
if ($cutoff == $current_time) {
// Label lifetime is set to 0, meaning never delete
return;
}
$path = file_create_path('fedex_labels');
if (is_dir($path)) {
$files = array_diff(scandir($path), array(
'.',
'..',
));
if ($files) {
// Loop over label files in sites/default/files/fedex_labels
// and test creation date against 'uc_fedex_label_lifetime'
foreach ($files as $file) {
if ($cutoff > filectime($path . '/' . $file)) {
unlink($path . '/' . $file);
}
}
}
}
}
/**
* Implements hook_theme().
*/
function uc_fedex_theme() {
return array(
'uc_fedex_option_label' => array(
'file' => 'uc_fedex.module',
'arguments' => array(
'product' => NULL,
'packages' => NULL,
),
),
'uc_fedex_confirm_shipment' => array(
'file' => 'uc_fedex.ship.inc',
'arguments' => array(
'form' => NULL,
),
),
);
}
/******************************************************************************
* Conditional Actions Hooks *
******************************************************************************/
/**
* Implements hook_ca_predicate().
*
* Connect the FedEx quote Action and Event.
*/
function uc_fedex_ca_predicate() {
$enabled = variable_get('uc_quote_enabled', array() + array(
'fedex_ground' => FALSE,
'fedex' => FALSE,
'fedex_freight' => FALSE,
));
$predicates = array(
'uc_fedex_get_ground_quote' => array(
'#title' => t('Shipping quote from FedEx Ground'),
'#trigger' => 'get_quote_from_fedex_ground',
'#class' => 'uc_fedex',
'#status' => $enabled['fedex_ground'],
'#actions' => array(
array(
'#name' => 'uc_quote_action_get_quote',
'#title' => t('Fetch a shipping quote'),
'#argument_map' => array(
'order' => 'order',
'method' => 'method',
),
),
),
),
'uc_fedex_get_quote' => array(
'#title' => t('Shipping quote from FedEx'),
'#trigger' => 'get_quote_from_fedex',
'#class' => 'uc_fedex',
'#status' => $enabled['fedex'],
'#actions' => array(
array(
'#name' => 'uc_quote_action_get_quote',
'#title' => t('Fetch a shipping quote'),
'#argument_map' => array(
'order' => 'order',
'method' => 'method',
),
),
),
),
'uc_fedex_get_freight_quote' => array(
'#title' => t('Shipping quote from FedEx Freight'),
'#trigger' => 'get_quote_from_fedex_freight',
'#class' => 'uc_fedex',
'#status' => $enabled['fedex_freight'],
'#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 Ubercart's hook_shipping_type().
*
* @return
* Array of package types for FedEx shipping method.
*/
function uc_fedex_shipping_type() {
$weight = variable_get('uc_quote_type_weight', array(
'small_package' => 0,
));
$types = array(
'small_package' => array(
'id' => 'small_package',
'title' => t('Small Package'),
'weight' => $weight['small_package'],
),
);
return $types;
}
/**
* Implements Ubercart's hook_shipping_method().
*
* @return
* Array of FedEx shipping services.
*/
function uc_fedex_shipping_method() {
$enabled = variable_get('uc_quote_enabled', array()) + array(
'fedex_ground' => FALSE,
'fedex' => FALSE,
'fedex_freight' => FALSE,
);
$weight = variable_get('uc_quote_method_weight', array()) + array(
'fedex_ground' => 0,
'fedex' => 1,
'fedex_freight' => 2,
);
$methods = array(
'fedex_ground' => array(
'id' => 'fedex_ground',
'module' => 'uc_fedex',
'title' => t('FedEx (Ground)'),
'quote' => array(
'type' => 'small_package',
'callback' => 'uc_fedex_quote',
'accessorials' => _uc_fedex_ground_services(),
),
'ship' => array(
'type' => 'small_package',
'callback' => 'uc_fedex_fulfill_order',
'file' => 'uc_fedex.ship.inc',
'pkg_types' => _uc_fedex_package_types(),
),
'enabled' => $enabled['fedex_ground'],
'weight' => $weight['fedex_ground'],
),
'fedex' => array(
'id' => 'fedex',
'module' => 'uc_fedex',
'title' => t('FedEx (Express)'),
'quote' => array(
'type' => 'small_package',
'callback' => 'uc_fedex_quote',
'accessorials' => _uc_fedex_express_services(),
),
'ship' => array(
'type' => 'small_package',
'callback' => 'uc_fedex_fulfill_order',
'file' => 'uc_fedex.ship.inc',
'pkg_types' => _uc_fedex_package_types(),
),
'enabled' => $enabled['fedex'],
'weight' => $weight['fedex'],
),
'fedex_freight' => array(
'id' => 'fedex_freight',
'module' => 'uc_fedex',
'title' => t('FedEx (Freight)'),
'quote' => array(
'type' => 'small_package',
'callback' => 'uc_fedex_quote',
'accessorials' => _uc_fedex_freight_services(),
),
'enabled' => $enabled['fedex_freight'],
'weight' => $weight['fedex_freight'],
),
);
return $methods;
}
/**
* Implements Ubercart's hook_store_status().
*
* Lets the administrator know if the FedEx account information has not been
* filled out.
*
* @return
* Array of error or status messages from configuration of FedEx module.
*/
function uc_fedex_store_status() {
$messages = array();
$key = variable_get('uc_fedex_user_credential_key', 0);
$password = variable_get('uc_fedex_user_credential_password', 0);
$account = variable_get('uc_fedex_account_number', 0);
$meter = variable_get('uc_fedex_meter_number', 0);
if ($key && $password && $account && $meter) {
$messages[] = array(
'status' => 'ok',
'title' => t('FedEx Ship Manager'),
'desc' => t('Information needed to access FedEx Ship Manager has been entered.'),
);
}
else {
$messages[] = array(
'status' => 'error',
'title' => t('FedEx Ship Manager'),
'desc' => t('More information is needed to access FedEx Ship Manager. Please enter it !link.', array(
'!link' => l('here', 'admin/store/settings/quotes/methods/fedex'),
)),
);
}
return $messages;
}
/******************************************************************************
* Module Functions *
******************************************************************************/
/**
* Callback for retrieving a FedEx shipping quote.
*
* Requests a quote of all available FedEx services. Quote returned
* from the FedEx server is parsed and only the selected services are
* presented to the user.
*
* @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_fedex_quote($products, $details, $method) {
// The uc_quote AJAX query can fire before the customer has completely
// filled out the destination address, so check to see whether the address
// is complete. If not, abort.
$destination = (object) $details;
if (empty($destination->zone) || empty($destination->postal_code) || empty($destination->country)) {
// Skip this shipping method.
return array();
}
// Assign products to one or more packages for quoting
$packages = _uc_fedex_package_products($products);
if (!count($packages)) {
// If _uc_fedex_package_products() returned no packages,
// then at least one item must be too heavy to ship via FedEx
// Skip this shipping method.
return array();
}
// Create and fill object with info needed about origin
$origin = variable_get('uc_quote_store_default_address', new stdClass());
$country = db_query("SELECT * FROM {uc_countries} WHERE country_id = %d", $origin->country);
$country_data = db_fetch_object($country);
$origin->country_iso_code_2 = $country_data->country_iso_code_2;
// Create and fill object with info needed about destination
$destination = (object) $details;
if ($origin->country == $destination->country) {
// Try to save a DB query
$destination->country_iso_code_2 = $origin->country_iso_code_2;
}
else {
$country = db_query("SELECT * FROM {uc_countries} WHERE country_id = %d", $destination->country);
$country_data = db_fetch_object($country);
$destination->country_iso_code_2 = $country_data->country_iso_code_2;
}
$debug = user_access('configure quotes') && variable_get('uc_quote_display_debug', FALSE);
// Determine if address is Residential or Commercial
// If Address Validation API is not used or fails, default to store default
$destination->residential = uc_fedex_address_is_residential($destination, variable_get('uc_fedex_residential_quotes', 1), $debug);
// Call the method that does the actual SOAP request to the FedEx Server
// Response contains all available services and rates
$response = uc_fedex_rate_request($packages, $origin, $destination);
// Construct an array containing only those services that the
// store admin has allowed in admin/store/settings/quotes/edit
$fedex_services = array();
switch ($method['id']) {
case 'fedex_ground':
$fedex_services = array_filter(variable_get('uc_fedex_ground_services', _uc_fedex_ground_services()));
break;
case 'fedex':
$fedex_services = array_filter(variable_get('uc_fedex_express_services', _uc_fedex_express_services()));
break;
case 'fedex_freight':
$fedex_services = array_filter(variable_get('uc_fedex_freight_services', _uc_fedex_freight_services()));
break;
}
// Initialize return array
$quotes = array();
if (!isset($response->RateReplyDetails)) {
// Memphis, we have a problem ...
// Error returned from FedEx server - will print in $message box
// Don't even try to extract a quote from the response, just return
// empty quote array.
return $quotes;
}
// Test responses to see if we are interested in that service
foreach ($response->RateReplyDetails as $options) {
$service = $options->ServiceType;
if (in_array($service, $fedex_services)) {
// Check to see if we're quoting ACCOUNT or LIST rates
if (variable_get('uc_fedex_quote_type', 'list') == 'list') {
// LIST rate
// LIST quotes return both ACCOUNT rates and LIST rates:
// Order not guaranteed.
// RatedShipmentDetails[0] = PAYOR_ACCOUNT
// RatedShipmentDetails[1] = RATED_ACCOUNT
// RatedShipmentDetails[2] = PAYOR_LIST
// RatedShipmentDetails[3] = RATED_LIST
foreach ($options->RatedShipmentDetails as $ratedetail) {
if ($ratedetail->ShipmentRateDetail->RateType == 'PAYOR_LIST') {
break;
}
}
}
else {
// ACCOUNT rate
// ACCOUNT quotes may return either ACCOUNT rates only OR
// ACCOUNT rates and LIST rates. Check.
if (is_array($options->RatedShipmentDetails)) {
foreach ($options->RatedShipmentDetails as $ratedetail) {
if ($ratedetail->ShipmentRateDetail->RateType == 'PAYOR_ACCOUNT') {
break;
}
}
}
else {
$ratedetail = $options->RatedShipmentDetails;
}
}
// need to handle dimensional rates, other modifiers.
// Markup rate before customer sees it
$rate = uc_fedex_rate_markup($ratedetail->ShipmentRateDetail->TotalNetCharge->Amount);
$quotes[$service] = array(
'rate' => $rate,
'format' => uc_currency_format($rate),
'option_label' => theme('uc_fedex_option_label', $method['quote']['accessorials'][$service], $packages),
);
}
}
if ($debug) {
// $quotes['data']['debug'] = htmlentities($response).'<br />';
}
// Sort rate quotes in order of increasing price
uasort($quotes, 'uc_quote_price_sort');
return $quotes;
}
/**
* Constructs and executes a SOAP RateAvailabilityService request.
* Obtains Rate and Available Services information needed for
* shipping quote.
*
* SOAP call parameters are set in the order they appear in the WSDL file
* Associative array of DOM returned.
*
* @param $packages
* Array of packages received from the cart.
* @param $origin
* Delivery origin address information.
* @param $destination
* Delivery destination address information.
*
* @return
* Associative array mirroring contents of SOAP object returned from server.
*/
function uc_fedex_rate_request($packages, $origin, $destination) {
// Set up SOAP call.
// Allow tracing so details of request can be retrieved for error logging
$client = new SoapClient(drupal_get_path('module', 'uc_fedex') . '/wsdl-' . variable_get('uc_fedex_server_role', 'testing') . '/RateService_v10.wsdl', array(
'trace' => 1,
));
// FedEx user key and password filled in by user on admin form
$request['WebAuthenticationDetail'] = array(
'UserCredential' => array(
'Key' => variable_get('uc_fedex_user_credential_key', 0),
'Password' => variable_get('uc_fedex_user_credential_password', 0),
),
);
// FedEx account and meter number filled in by user on admin form
$request['ClientDetail'] = array(
'AccountNumber' => variable_get('uc_fedex_account_number', 0),
'MeterNumber' => variable_get('uc_fedex_meter_number', 0),
);
// Optional parameter, contains anything
$request['TransactionDetail'] = array(
'CustomerTransactionId' => '*** Rate/Available Services Request v10 from Ubercart ***',
);
// Rate Services Availability Request v10.0.0.
$request['Version'] = array(
'ServiceId' => 'crs',
'Major' => '10',
'Intermediate' => '0',
'Minor' => '0',
);
// Grab details of sender origin - not necessarily package origin
$request['RequestedShipment']['Shipper'] = array(
'Address' => array(
'PostalCode' => $origin->postal_code,
'CountryCode' => $origin->country_iso_code_2,
),
);
// Grab details of package destination
$request['RequestedShipment']['Recipient'] = array(
'Address' => array(
'PostalCode' => $destination->postal_code,
'CountryCode' => $destination->country_iso_code_2,
'Residential' => $destination->residential,
),
);
// Currency for quote
$request['RequestedShipment']['CurrencyType'] = variable_get('uc_currency_code', 'USD');
// Set Pickup/Dropoff type
$request['RequestedShipment']['DropoffType'] = variable_get('uc_fedex_dropoff_type', 'REGULAR_PICKUP');
// Note that ACCOUNT rates *require* a valid account number
// and return accurate answers on the production server
$request['RequestedShipment']['RateRequestTypes'] = strtoupper(variable_get('uc_fedex_quote_type', 'list'));
// When the package is going to ship
// have to think about this -
// cutoff times, commits store owner to exact ship date, etc.
// Probably have to make an admin menu option with cutoff time, after
// which ShipDate becomes "tomorrow" unless of course tomorrow is a
// weekend when you're closed... But this shouldn't affect the rate
// Drupal 6.x version of format_date() doesn't support 'c', so until
// then we use date() directly. date() doesn't take care of site
// timezone, though.
//$request['RequestedShipment']['ShipTimestamp'] = format_date(time(), 'custom', 'c');
$request['RequestedShipment']['ShipTimestamp'] = date('c');
//
// Packaging type - need this to be settable for each package rather
// than one site-wide setting?
//
//$request['RequestedShipment']['PackagingType'] = variable_get('uc_fedex_package_type', 'YOUR_PACKAGING');
$request['RequestedShipment']['PackageDetail'] = 'INDIVIDUAL_PACKAGES';
$request['RequestedShipment']['PackageCount'] = count($packages);
$request['RequestedShipment']['RequestedPackageLineItems'] = array();
// Determine weight and length units to send to FedEx
// FedEx supports only LB and KG; if store units are set to
// something else then determine conversion factor to apply
// to package weights
$weight_units = strtoupper(variable_get('uc_weight_unit', 'LB'));
$weight_conversion_factor = 1;
if ($weight_units != 'LB' && $weight_units != 'KG') {
$weight_conversion_factor = uc_weight_conversion($weight_units, 'LB');
$weight_units = 'LB';
}
// FedEx supports only IN and CM; if store units are set to something
// else then determine conversion factor to apply to package lengths
$length_units = strtoupper(variable_get('uc_length_unit', 'IN'));
$length_conversion_factor = 1;
if ($length_units != 'IN' && $length_units != 'CM') {
$length_conversion_factor = uc_length_conversion($length_units, 'IN');
$length_units = 'IN';
}
// Iterate over $packages to account for multi-package shipments
$sequence = 0;
foreach ($packages as $package) {
$sequence++;
$package_properties = array(
'SequenceNumber' => $sequence,
// New for v10 - what's the proper way to use this?
'GroupPackageCount' => 1,
// Weights must be rounded up to nearest integer value.
'Weight' => array(
'Value' => ceil($package->shipweight * $weight_conversion_factor),
'Units' => $weight_units,
),
// Lengths must be rounded up to nearest integer value
// Package size hardwired to 1" x 1" x 1" to force weight-based rates
'Dimensions' => array(
'Length' => ceil(1.0 * $length_conversion_factor),
'Width' => ceil(1.0 * $length_conversion_factor),
'Height' => ceil(1.0 * $length_conversion_factor),
'Units' => $length_units,
),
);
// Add Insurance if requested
if (variable_get('uc_fedex_insurance', FALSE)) {
$package_properties['InsuredValue'] = array(
'Amount' => $package->price,
'Currency' => variable_get('uc_currency_code', 'USD'),
);
}
// Fill in SOAP request with $package_properties
$request['RequestedShipment']['RequestedPackageLineItems'][] = $package_properties;
// Grab package origin - not necessarily the same as shipper
// $request['RequestedShipment']['RequestedPackageLineItems']['Origin'] = array(
// 'Address' => array(
// 'PostalCode' => $origin->postal_code,
// 'CountryCode' => $origin->country_iso_code_2,
// )
// );
}
//
// Send the SOAP request to the FedEx server
//
try {
$response = $client
->getRates($request);
if ($response->HighestSeverity != 'FAILURE' && $response->HighestSeverity != 'ERROR') {
print_request_response($client);
}
else {
drupal_set_message(t('Error in processing FedEx Shipping Quote transaction.'), 'error');
foreach ($response->Notifications as $notification) {
if (is_array($response->Notifications)) {
drupal_set_message($notification->Severity . ': ' . $notification->Message, 'error');
}
else {
drupal_set_message($notification, 'error');
}
}
}
return $response;
} catch (SoapFault $exception) {
drupal_set_message('<h2>Fault</h2><br /><b>Code:</b>' . $exception->faultcode . '<br /><b>String:</b>' . $exception->faultstring . '<br />', 'error');
// what else needs to be done here if FedEx quote fails? What to display
// to customer?
}
}
/**
* Modifies the rate received from FedEx before displaying to the customer.
*
* @param $rate
* Shipping rate without any rate markup.
*
* @return
* Shipping rate after markup.
*/
function uc_fedex_rate_markup($rate) {
$markup = trim(variable_get('uc_fedex_rate_markup', '0'));
$type = variable_get('uc_fedex_rate_markup_type', 'percentage');
if (is_numeric($markup)) {
switch ($type) {
case 'percentage':
return $rate + $rate * floatval($markup) / 100;
case 'multiplier':
return $rate * floatval($markup);
case 'currency':
return $rate + floatval($markup);
}
}
else {
return $rate;
}
}
/**
* Modifies the weight of shipment before sending to FedEx for a quote.
*
* @param $weight
* Shipping weight without any weight markup.
*
* @return
* Shipping weight after markup.
*/
function uc_fedex_weight_markup($weight) {
$markup = trim(variable_get('uc_fedex_weight_markup', '0'));
$type = variable_get('uc_fedex_weight_markup_type', 'percentage');
if (is_numeric($markup)) {
switch ($type) {
case 'percentage':
return $weight + $weight * floatval($markup) / 100;
case 'multiplier':
return $weight * floatval($markup);
case 'mass':
return $weight + floatval($markup);
}
}
else {
return $weight;
}
}
/**
* Packages products into boxes subject to the FedEx weight limit, corrected
* for any weight markup imposed by the administrator.
*
* $package object returned from this routine contains the following members:
* - quantity: Number of items in package.
* - price: Value (sales price, in store currency) of items in package.
* - weight: Actual weight of items in package, in store weight units.
* - weight_units: Set to store default, taken from uc_weight_unit variable.
* - shipweight: Computed weight of package, including markup.
*
* Store weight units are used internally for computation of package weights.
* Each product may have its own weight units; these are converted to store
* units and the package shipweight is returned in terms of the store weight
* units. The store weight units are saved in the $package object for
* completeness.
*
* @param $products
* An array of nodes of type product.
*
* @return
* An array of package objects, each containing one or more of the products.
*/
function _uc_fedex_package_products($products) {
$packages = array();
// Determine maximum weight of products we can put into one package
// while staying below PACKAGE_WEIGHT_LIMIT_LBS. This number
// depends on the package weight markup set in the FedEx module
// administration menu.
$products_max_weight = PACKAGE_WEIGHT_LIMIT_LBS;
$zero_markup = uc_fedex_weight_markup(0);
if ($zero_markup == 0) {
// Weight markup is a multiplier, because 0 * multiplier = 0
// This handles percentage markup too
$products_max_weight = $products_max_weight / uc_fedex_weight_markup(1);
}
else {
// Weight markup is an additive factor , because 0 + factor = factor != 0
$products_max_weight = $products_max_weight - $zero_markup;
}
// Convert $products_max_weight (which is defined in LB units) into store
// default weight units so we can perform all calculations and return all
// results in store default weight units
$products_max_weight = $products_max_weight * uc_weight_conversion('LB', variable_get('uc_weight_unit', 'LB'));
if (variable_get('uc_fedex_all_in_one', TRUE)) {
// All products in one package, break by weight
// Create first package
$package = new stdClass();
$package->quantity = 0;
$package->price = 0.0;
$package->weight = 0.0;
$package->weight_units = variable_get('uc_weight_unit', 'LB');
// Loop over products
foreach ($products as $product) {
// Get item weight. Weight units are set on a per-product basis, so
// we convert as necessary in order to perform all calculations in the
// store weight units.
$item_weight = $product->weight * uc_weight_conversion($product->weight_units, variable_get('uc_weight_unit', 'LB'));
if ($item_weight >= $products_max_weight) {
// This product is too heavy to ship via FexEx Ground or FedEx Express -
// quit with error
return array();
}
// Loop over qty of each product
for ($item = 0; $item < $product->qty; $item++) {
// Test to see if putting this item into the current package put us
// over the weight limit
if ($package->weight + $item_weight < $products_max_weight) {
// No? Then update the package information and continue
$package->quantity += 1;
$package->price += $product->price;
$package->weight += $item_weight;
}
else {
// If weight >= maximum allowed weight, save current package to
// array and start a new package:
// First, markup weight of current package
$package->shipweight = uc_fedex_weight_markup($package->weight);
// Second, save current package to array
$packages[] = $package;
// Finally, start a new package
$package = new stdClass();
$package->quantity = 1;
$package->price = $product->price;
$package->weight = $item_weight;
$package->weight_units = variable_get('uc_weight_unit', 'LB');
}
}
}
// No more products left to package
// Take care of the partially-filled package we were working on
// First, markup weight of partially-filled package
$package->shipweight = uc_fedex_weight_markup($package->weight);
// Second, save the partially-filled package to the array and exit
$packages[] = $package;
}
else {
// variable_get('uc_fedex_all_in_one', TRUE) == FALSE
// Each product line item in its own package, subject only to pkg_qty
// Loop over products
foreach ($products as $product) {
// If pkg_qty == 0 we assume no limit on package quantity
if (!$product->pkg_qty) {
// Put all of this product line item into one package
$product->pkg_qty = $product->qty;
}
// Calculate number of full packages
$num_of_pkgs = (int) ($product->qty / $product->pkg_qty);
if ($num_of_pkgs) {
for ($i = 0; $i < $num_of_pkgs; $i++) {
// Create full packages
$package = new stdClass();
$package->quantity = $product->pkg_qty;
$package->price = $product->price * $product->pkg_qty;
$package->weight = $product->weight * $product->pkg_qty;
$package->weight_units = variable_get('uc_weight_unit', 'LB');
// Markup weight on a per-package basis
$package->shipweight = uc_fedex_weight_markup($package->weight);
// Save current package to array
$packages[] = $package;
}
}
// Deal with the remaining partially-full package
$remaining_qty = $product->qty % $product->pkg_qty;
if ($remaining_qty) {
// Create partially-full packages
$package = new stdClass();
$package->quantity = $remaining_qty;
$package->price = $product->price * $remaining_qty;
$package->weight = $product->weight * $remaining_qty;
$package->weight_units = variable_get('uc_weight_unit', 'LB');
// Markup weight on a per-package basis
$package->shipweight = uc_fedex_weight_markup($package->weight);
// Save package to array
$packages[] = $package;
}
}
}
return $packages;
}
/**
* Convenience function to get FedEx codes for their package types.
*
* @return
* An array of human-friendly names for the different FedEx package types.
*/
function _uc_fedex_package_types() {
return array(
'YOUR_PACKAGING' => t('Your Packaging'),
'FEDEX_ENVELOPE' => t('FedEx Envelope'),
'FEDEX_PAK' => t('FedEx Pak'),
'FEDEX_BOX' => t('FedEx Box'),
'FEDEX_TUBE' => t('FedEx Tube'),
'FEDEX_10KG_BOX' => t('FedEx 10kg Box'),
'FEDEX_25KG_BOX' => t('FedEx 25kg Box'),
);
}
/**
* Convenience function to get FedEx codes for their Ground services.
*
* This should probably be sucked out of the WSDL file, to be sure
* the options stay correct and up-to-date.
*
* @return
* An array of human-friendly names for the different FedEx service codes.
*/
function _uc_fedex_ground_services() {
return array(
'FEDEX_GROUND' => t('FedEx Ground'),
'GROUND_HOME_DELIVERY' => t('FedEx Home Delivery'),
);
}
/**
* Convenience function to get FedEx codes for their Express services.
*
* This should probably be sucked out of the WSDL file, to be sure
* the options stay correct and up-to-date.
*
* @return
* An array of human-friendly names for the different FedEx service codes.
*/
function _uc_fedex_express_services() {
return array(
'STANDARD_OVERNIGHT' => t('FedEx Standard Overnight'),
'PRIORITY_OVERNIGHT' => t('FedEx Priority Overnight'),
'FIRST_OVERNIGHT' => t('FedEx First Overnight'),
'FEDEX_2_DAY' => t('FedEx 2nd Day'),
'FEDEX_EXPRESS_SAVER' => t('FedEx Express Saver'),
'EUROPE_FIRST_INTERNATIONAL_PRIORITY' => t('FedEx Europe First International Priority'),
'INTERNATIONAL_ECONOMY' => t('FedEx International Economy'),
'INTERNATIONAL_ECONOMY_DISTRIBUTION' => t('FedEx International Economy Distribution'),
'INTERNATIONAL_PRIORITY' => t('FedEx International Priority'),
'INTERNATIONAL_PRIORITY_DISTRIBUTION' => t('FedEx International Priority Distribution'),
'INTERNATIONAL_FIRST' => t('FedEx International First'),
);
}
/**
* Convenience function to get FedEx codes for their Freight services.
*
* This should probably be sucked out of the WSDL file, to be sure
* the options stay correct and up-to-date.
*
* @return
* An array of human-friendly names for the different FedEx service codes.
*/
function _uc_fedex_freight_services() {
return array(
'FEDEX_1_DAY_FREIGHT' => t('FedEx 1-Day Freight'),
'FEDEX_2_DAY_FREIGHT' => t('FedEx 2-Day Freight'),
'FEDEX_3_DAY_FREIGHT' => t('FedEx 3-Day Freight'),
'INTERNATIONAL_ECONOMY_FREIGHT' => t('FedEx International Economy Freight'),
'INTERNATIONAL_PRIORITY_FREIGHT' => t('FedEx International Priority Freight'),
'INTERNATIONAL_DISTRIBUTION_FREIGHT' => t('FedEx International Distribution Freight'),
);
}
/**
* Convenience function to get FedEx codes for special services options.
*
* @return
* An array of human-friendly names for the different FedEx special services
* options codes.
*/
function _uc_fedex_shipment_special_types() {
return array(
'BROKER_SELECT_OPTION' => t('FedEx International First'),
'COD' => t('COD Shipment'),
'DANGEROUS_GOODS' => t('Dangerous Goods'),
'DRY_ICE' => t('Dry Ice'),
'EMAIL_NOTIFICATION' => t('E-Mail Notification'),
'FUTURE_DAY_SHIPMENT' => t('Future Day Shipment'),
'HOLD_AT_LOCATION' => t('FedEx International First'),
'HOLD_SATURDAY' => t('FedEx International First'),
'INSIDE_DELIVERY' => t('FedEx International First'),
'INSIDE_PICKUP' => t('FedEx International First'),
'PRIORITY_ALERT' => t('FedEx International First'),
'RETURN_SHIPMENT' => t('FedEx International First'),
'SATURDAY_DELIVERY' => t('Saturday Delivery'),
'SATURDAY_PICKUP' => t('Saturday Pickup'),
'THIRD_PARTY_CONSIGNEE' => t('FedEx International First'),
'WEEKDAY_DELIVERY' => t('Weekday delivery '),
);
}
/**
* Convenience function to get FedEx codes for dropoff and pickup.
*
* This should probably be sucked out of the WSDL file, to be sure
* the options stay correct and up-to-date.
*
* @return
* An array of human-friendly names for the different FedEx pickup/dropoff
* option codes.
*/
function _uc_fedex_dropoff_types() {
return array(
'BUSINESS_SERVICE_CENTER' => t('Dropoff at FedEx Business Service Center'),
'DROP_BOX' => t('Dropoff at FedEx Drop Box'),
'REGULAR_PICKUP' => t('Regularly scheduled Pickup from your location'),
'REQUEST_COURIER' => t('One-time Pickup request'),
'STATION' => t('Dropoff at FedEx Staffed Location'),
);
}
/**
* Prints SOAP request and response, iff allowed by user access permissions.
*
* To view transaction details, set display debug TRUE in
* admin/store/settings/quotes/edit
*
* @param $client
* SOAP client object containing transaction history.
*/
function print_request_response($client) {
if (user_access('configure quotes') && variable_get('uc_quote_display_debug', FALSE)) {
drupal_set_message('<h2>FedEx Transaction processed successfully.</h2>' . '<h3>Request: </h3><pre>' . check_plain($client
->__getLastRequest()) . '</pre>' . '<h3>Response: </h3><pre>' . check_plain($client
->__getLastResponse()) . '</pre>');
}
}
/**
* Theme function to format the FedEx service name and rate amount
* line-item shown to the customer.
*
* @param $service
* The FedEx service name.
* @param $packages
* Package information.
*
* @ingroup themeable
*/
function theme_uc_fedex_option_label($service, $packages) {
// Start with FedEx logo
$output = '<img class="fedex-logo" src="' . base_path() . drupal_get_path('module', 'uc_fedex') . '/uc_fedex_logo.gif" alt="FedEx Logo" />';
// Add FedEx service name, removing the first six characters
// (== 'FedEx ') because these replicate the logo image
$output .= substr($service, 6);
// Add package information
$output .= ' (' . format_plural(count($packages), '1 package', '@count packages') . ')';
return $output;
}
Functions
Name![]() |
Description |
---|---|
print_request_response | Prints SOAP request and response, iff allowed by user access permissions. |
theme_uc_fedex_option_label | Theme function to format the FedEx service name and rate amount line-item shown to the customer. |
uc_fedex_ca_predicate | Implements hook_ca_predicate(). |
uc_fedex_cron | Implements hook_cron(). |
uc_fedex_init | Implements hook_init(). |
uc_fedex_menu | Implements hook_menu(). |
uc_fedex_quote | Callback for retrieving a FedEx shipping quote. |
uc_fedex_rate_markup | Modifies the rate received from FedEx before displaying to the customer. |
uc_fedex_rate_request | Constructs and executes a SOAP RateAvailabilityService request. Obtains Rate and Available Services information needed for shipping quote. |
uc_fedex_shipping_method | Implements Ubercart's hook_shipping_method(). |
uc_fedex_shipping_type | Implements Ubercart's hook_shipping_type(). |
uc_fedex_store_status | Implements Ubercart's hook_store_status(). |
uc_fedex_theme | Implements hook_theme(). |
uc_fedex_weight_markup | Modifies the weight of shipment before sending to FedEx for a quote. |
_uc_fedex_dropoff_types | Convenience function to get FedEx codes for dropoff and pickup. |
_uc_fedex_express_services | Convenience function to get FedEx codes for their Express services. |
_uc_fedex_freight_services | Convenience function to get FedEx codes for their Freight services. |
_uc_fedex_ground_services | Convenience function to get FedEx codes for their Ground services. |
_uc_fedex_package_products | Packages products into boxes subject to the FedEx weight limit, corrected for any weight markup imposed by the administrator. |
_uc_fedex_package_types | Convenience function to get FedEx codes for their package types. |
_uc_fedex_shipment_special_types | Convenience function to get FedEx codes for special services options. |
Constants
Name![]() |
Description |
---|---|
PACKAGE_WEIGHT_LIMIT_LBS | Maximum shipping weight for FedEx (non-Freight services) |