uc_ups.module in Ubercart 5
Same filename and directory in other branches
Shipping quote module that interfaces with www.ups.com to get rates for small package shipments.
Coded by Lyle Mantooth
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.
*
* Coded by Lyle Mantooth
*/
/******************************************************************************
* Drupal Hooks *
******************************************************************************/
/**
* Implementation of hook_menu().
*/
function uc_ups_menu($may_cache) {
$items = array();
if ($may_cache) {
$items[] = array(
'path' => 'admin/store/settings/quotes/methods/ups',
'access' => user_access('configure quotes'),
'title' => t('UPS'),
'callback' => 'drupal_get_form',
'callback arguments' => 'uc_ups_admin_settings',
'type' => MENU_LOCAL_TASK,
);
}
else {
$items[] = array(
'path' => 'admin/store/orders/' . arg(3) . '/shipments/ups',
'access' => user_access('fulfill orders'),
'title' => t('UPS shipment'),
'callback' => 'drupal_get_form',
'callback arguments' => array(
'uc_ups_confirm_shipment',
arg(3),
),
'type' => MENU_CALLBACK,
);
$items[] = array(
'path' => 'admin/store/orders/' . arg(3) . '/shipments/labels/ups',
'access' => user_access('fulfill orders'),
'callback' => 'theme',
'callback arguments' => array(
'uc_ups_label_image',
),
'type' => MENU_CALLBACK,
);
drupal_add_css(drupal_get_path('module', 'uc_ups') . '/uc_ups.css');
}
return $items;
}
/**
* Implementation of hook_form_alter().
*
* Add package type to products.
*
* @see uc_product_form
* @see uc_ups_product_alte_validate
*/
function uc_ups_form_alter($form_id, &$form) {
$node = $form['#node'];
if (is_object($node) && $form_id == $node->type . '_node_form' && isset($form['base']['dimensions']) && in_array($node->type, module_invoke_all('product_types'))) {
$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' => $node->ups['pkg_type'] ? $node->ups['pkg_type'] : '02',
);
$form['shipping']['ups'] = $ups;
if ($enabled['ups']) {
$form['#validate']['uc_ups_product_alter_validate'] = array();
}
}
}
/**
* Validation handler for UPS product fields.
*/
function uc_ups_product_alter_validate($form_id, $form_values) {
$enabled = variable_get('uc_quote_enabled', array());
if ($form_values['shippable'] && ($form_values['shipping_type'] == 'small_package' || empty($form_values['shipping_type']) && variable_get('uc_store_shipping_type', 'small_package') == 'small_package')) {
if ($form_values['ups']['pkg_type'] == '02' && (empty($form_values['length']) || empty($form_values['width']) || empty($form_values['height']))) {
form_set_error('base][dimensions', t('Dimensions are required for custom packaging.'));
}
}
}
/**
* Implementation of hook_nodeapi().
*/
function uc_ups_nodeapi(&$node, $op) {
if (in_array($node->type, module_invoke_all('product_types'))) {
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;
}
}
}
/******************************************************************************
* Workflow-ng Hooks *
******************************************************************************/
/**
* Implementation of hook_configuration().
*
* Connect the UPS quote action and event.
*/
function uc_ups_configuration() {
$enabled = variable_get('uc_quote_enabled', array());
$configurations = array(
'uc_ups_get_quote' => array(
'#label' => t('Shipping quote from UPS'),
'#event' => 'get_quote_from_ups',
'#module' => 'uc_ups',
'#active' => $enabled['ups'],
),
);
$action = workflow_ng_use_action('uc_quote_action_get_quote', array(
'#label' => t('Fetch a shipping quote'),
));
$configurations['uc_ups_get_quote'] = workflow_ng_configure($configurations['uc_ups_get_quote'], $action);
return $configurations;
}
/******************************************************************************
* Übercart Hooks *
******************************************************************************/
/**
* Implementation of Übercart's 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;
}
/**
* Implementation of Übercart's 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',
'pkg_types' => _uc_ups_pkg_types(),
),
'cancel' => 'uc_ups_void_shipment',
'weight' => $weight['ups'],
);
return $methods;
}
/**
* Implementation of Übercart's 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;
}
/******************************************************************************
* Menu Callbacks *
******************************************************************************/
/**
* UPS Online Tool settings.
*
* Record UPS account information neccessary to use the service. Allow testing
* or production mode. Configure which UPS services are quoted to customers.
*
* @ingroup forms
* @see uc_admin_settings_validate
*/
function uc_ups_admin_settings() {
$form = array();
$form['uc_ups_access_license'] = array(
'#type' => 'textfield',
'#title' => t('UPS OnLine Tools XML Access Key'),
'#default_value' => variable_get('uc_ups_access_license', ''),
'#required' => true,
);
$form['uc_ups_shipper_number'] = array(
'#type' => 'textfield',
'#title' => t('UPS Shipper #'),
'#description' => t('The 6-character string identifying your UPS account as a shipper.'),
'#default_value' => variable_get('uc_ups_shipper_number', ''),
'#required' => true,
);
$form['uc_ups_user_id'] = array(
'#type' => 'textfield',
'#title' => t('UPS.com user ID'),
'#default_value' => variable_get('uc_ups_user_id', ''),
'#required' => true,
);
$form['uc_ups_password'] = array(
'#type' => 'password',
'#title' => t('Password'),
'#default_value' => variable_get('uc_ups_password', ''),
);
$form['uc_ups_connection_address'] = array(
'#type' => 'select',
'#title' => t('Mode'),
'#description' => t('Quotes and shipments requested in Testing mode will not be picked up or charged to your account.'),
'#options' => array(
'https://wwwcie.ups.com/ups.app/xml/' => t('Testing'),
'https://www.ups.com/ups.app/xml/' => t('Production'),
),
'#default_value' => variable_get('uc_ups_connection_address', 'https://wwwcie.ups.com/ups.app/xml/'),
);
$form['uc_ups_services'] = array(
'#type' => 'checkboxes',
'#title' => t('UPS services'),
'#default_value' => variable_get('uc_ups_services', _uc_ups_service_list()),
'#options' => _uc_ups_service_list(),
'#description' => t('Select the UPS services that are available to customers.'),
);
$form['uc_ups_pickup_type'] = array(
'#type' => 'select',
'#title' => t('Pickup type'),
'#options' => array(
'01' => 'Daily Pickup',
'03' => 'Customer Counter',
'06' => 'One Time Pickup',
'07' => 'On Call Air',
'11' => 'Suggested Retail Rates',
'19' => 'Letter Center',
'20' => 'Air Service Center',
),
'#default_value' => variable_get('uc_ups_pickup_type', '01'),
);
$form['uc_ups_classification'] = array(
'#type' => 'select',
'#title' => t('UPS Customer classification'),
'#options' => array(
'01' => t('Wholesale'),
'03' => t('Occasional'),
'04' => t('Retail'),
),
'#default_value' => variable_get('uc_ups_classification', '04'),
'#description' => t('The kind of customer you are to UPS. For daily pickups the default is wholesale; for customer counter pickups the default is retail; for other pickups the default is occasional.'),
);
$form['uc_ups_negotiated_rates'] = array(
'#type' => 'radios',
'#title' => t('Negotiated rates'),
'#default_value' => variable_get('uc_ups_negotiated_rates', 0),
'#options' => array(
1 => t('Yes'),
0 => t('No'),
),
'#description' => t('Is your UPS account receiving negotiated rates on shipments?'),
);
$form['uc_ups_residential_quotes'] = array(
'#type' => 'radios',
'#title' => t('Assume UPS shipping quotes will be delivered to'),
'#default_value' => variable_get('uc_ups_residential_quotes', 0),
'#options' => array(
0 => t('Business locations'),
1 => t('Residential locations (extra fees)'),
),
);
$form['uc_ups_markup_type'] = array(
'#type' => 'select',
'#title' => t('Markup type'),
'#default_value' => variable_get('uc_ups_markup_type', 'percentage'),
'#options' => array(
'percentage' => t('Percentage (%)'),
'multiplier' => t('Multiplier (×)'),
'currency' => t('Addition (!currency)', array(
'!currency' => variable_get('uc_currency_sign', '$'),
)),
),
);
$form['uc_ups_markup'] = array(
'#type' => 'textfield',
'#title' => t('Shipping rate markup'),
'#default_value' => variable_get('uc_ups_markup', '0'),
'#description' => t('Markup shipping rate quote by currency amount, percentage, or multiplier.'),
);
$form['uc_ups_all_in_one'] = array(
'#type' => 'radios',
'#title' => t('Product packages'),
'#default_value' => variable_get('uc_ups_all_in_one', 1),
'#options' => array(
0 => t('Each in its own package'),
1 => t('All in one'),
),
'#description' => t('Indicate whether each product is quoted as shipping separately or all in one package.'),
);
$form['uc_ups_unit_system'] = array(
'#type' => 'select',
'#title' => t('System of measurement'),
'#default_value' => variable_get('uc_ups_unit_system', variable_get('uc_length_unit', 'in')),
'#options' => array(
'in' => t('British'),
'cm' => t('Metric'),
),
'#description' => t('Choose the standard system of measurement for your country.'),
);
$form['uc_ups_insurance'] = array(
'#type' => 'checkbox',
'#title' => t('Package insurance'),
'#default_value' => variable_get('uc_ups_insurance', TRUE),
'#description' => t('When enabled, products are insured for their full value.'),
);
return system_settings_form($form);
}
/**
* Validation handler for uc_ups_admin_settings.
*
* Require password only if it hasn't been set.
*/
function uc_ups_admin_settings_validate($form_id, $form_values, $form) {
$old_password = variable_get('uc_ups_password', '');
if (!$form_values['uc_ups_password']) {
if ($old_password) {
form_set_value($form['uc_ups_password'], $old_password);
}
else {
form_set_error('uc_ups_password', t('Password field is required.'));
}
}
}
/******************************************************************************
* 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'] = variable_get('uc_store_email', NULL);
$store['email_from'] = variable_get('uc_store_email', NULL);
$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) {
$qty = $package->qty;
for ($i = 0; $i < $qty; $i++) {
$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 .= "<Description>". $package_type['description'] ."</Description>";
$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>";
$conversion = uc_weight_conversion($package->length_units, variable_get('uc_ups_unit_system', variable_get('uc_length_unit', 'in')));
$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 * $conversion, 2, '.', '') . "</Length>";
$package_schema .= "<Width>" . number_format($package->width * $conversion, 2, '.', '') . "</Width>";
$package_schema .= "<Height>" . number_format($package->height * $conversion, 2, '.', '') . "</Height>";
$package_schema .= "</Dimensions>";
}
$size = $package->length * $conversion + 2 * $conversion * ($package->width + $package->height);
switch ($ups_units) {
case 'in':
$conversion = uc_weight_conversion($package->weight_units, 'lb');
break;
case 'cm':
$conversion = uc_weight_conversion($package->weight_units, 'kg');
break;
}
$weight = max(1, $package->weight * $conversion);
$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;
}
/**
* Construct an XML shippment 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
* ShipConfirm XML document to send to UPS
*/
function uc_ups_shipment_request($packages, $origin, $destination, $ups_service) {
$store['name'] = variable_get('uc_store_name', NULL);
$store['owner'] = variable_get('uc_store_owner', NULL);
$store['email'] = variable_get('uc_store_email', NULL);
$store['email_from'] = variable_get('uc_store_email', NULL);
$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'));
$package_schema = '';
foreach ($packages as $package) {
$qty = $package->qty;
for ($i = 0; $i < $qty; $i++) {
$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>";
$conversion = constant(strtoupper($package->length_units) . '_TO_' . strtoupper(variable_get('uc_ups_unit_system', variable_get('uc_length_unit', 'in'))));
$package_schema .= "<Code>" . strtoupper(variable_get('uc_ups_unit_system', variable_get('uc_length_unit', 'in'))) . "</Code>";
$package_schema .= "</UnitOfMeasurement>";
$package_schema .= "<Length>" . (floor($package->length * $conversion) + 1) . "</Length>";
$package_schema .= "<Width>" . (floor($package->width * $conversion) + 1) . "</Width>";
$package_schema .= "<Height>" . (floor($package->height * $conversion) + 1) . "</Height>";
$package_schema .= "</Dimensions>";
}
$size = $package->length * $conversion + 2 * $conversion * ($package->width + $package->height);
switch ($ups_units) {
case 'in':
$conversion = uc_weight_conversion($package->weight_units, 'lb');
break;
case 'cm':
$conversion = uc_weight_conversion($package->weight_units, 'kg');
break;
}
$weight = $package->weight * $conversion;
$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/>";
}
$package_schema .= "<PackageServiceOptions>";
$package_schema .= "<InsuredValue>";
$package_schema .= "<CurrencyCode>" . variable_get('uc_currency_code', 'USD') . "</CurrencyCode>";
$package_schema .= "<MonetaryValue>" . number_format($package->price, 2, '.', '') . "</MonetaryValue>";
$package_schema .= "</InsuredValue>";
$package_schema .= "</PackageServiceOptions>";
$package_schema .= "</Package>";
}
}
$schema = uc_ups_access_request() . "\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ShipmentConfirmRequest xml:lang=\"en-US\">\n <Request>\n <TransactionReference>\n <CustomerContext>Complex Rate Request</CustomerContext>\n <XpciVersion>1.0001</XpciVersion>\n </TransactionReference>\n <RequestAction>ShipConfirm</RequestAction>\n <RequestOption>validate</RequestOption>\n </Request>\n <Shipment>";
$schema .= "<Shipper>";
$schema .= "<Name>" . $store['name'] . "</Name>";
$schema .= "<ShipperNumber>" . variable_get('uc_ups_shipper_number', '') . "</ShipperNumber>";
if ($store['phone']) {
$schema .= "<PhoneNumber>" . $store['phone'] . "</PhoneNumber>";
}
if ($store['fax']) {
$schema .= "<FaxNumber>" . $store['fax'] . "</FaxNumber>";
}
if ($store['email']) {
$schema .= "<EMailAddress>" . $store['email'] . "</EMailAddress>";
}
$schema .= "<Address>";
$schema .= "<AddressLine1>" . $store['street1'] . "</AddressLine1>";
if ($store['street2']) {
$schema .= "<AddressLine2>" . $store['street2'] . "</AddressLine2>";
}
$schema .= "<City>" . $store['city'] . "</City>";
$schema .= "<StateProvinceCode>{$shipper_zone}</StateProvinceCode>";
$schema .= "<PostalCode>{$shipper_zip}</PostalCode>";
$schema .= "<CountryCode>{$shipper_country}</CountryCode>";
$schema .= "</Address>";
$schema .= "</Shipper>";
$schema .= "<ShipTo>";
$schema .= "<CompanyName>" . $destination->company . "</CompanyName>";
$schema .= "<AttentionName>" . $destination->first_name . ' ' . $destination->last_name . "</AttentionName>";
$schema .= "<PhoneNumber>" . $destination->phone . "</PhoneNumber>";
$schema .= "<EMailAddress>" . $destination->email . "</EMailAddress>";
$schema .= "<Address>";
$schema .= "<AddressLine1>" . $destination->street1 . "</AddressLine1>";
if ($destination->street2) {
$schema .= "<AddressLine2>" . $destination->street2 . "</AddressLine2>";
}
$schema .= "<City>" . $destination->city . "</City>";
$schema .= "<StateProvinceCode>{$shipto_zone}</StateProvinceCode>";
$schema .= "<PostalCode>{$shipto_zip}</PostalCode>";
$schema .= "<CountryCode>{$shipto_country}</CountryCode>";
if ($destination->residence) {
$schema .= "<ResidentialAddressIndicator/>";
}
$schema .= "</Address>";
$schema .= "</ShipTo>";
$schema .= "<ShipFrom>";
$schema .= "<CompanyName>" . $origin->company . "</CompanyName>";
$schema .= "<AttentionName>" . $origin->first_name . ' ' . $origin->last_name . "</AttentionName>";
$schema .= "<PhoneNumber>" . $origin->phone . "</PhoneNumber>";
$schema .= "<EMailAddress>" . $origin->email . "</EMailAddress>";
$schema .= "<Address>";
$schema .= "<AddressLine1>" . $origin->street1 . "</AddressLine1>";
if ($origin->street2) {
$schema .= "<AddressLine2>" . $origin->street2 . "</AddressLine2>";
}
$schema .= "<City>" . $origin->city . "</City>";
$schema .= "<StateProvinceCode>{$shipfrom_zone}</StateProvinceCode>";
$schema .= "<PostalCode>{$shipfrom_zip}</PostalCode>";
$schema .= "<CountryCode>{$shipfrom_country}</CountryCode>";
$schema .= "</Address>";
$schema .= "</ShipFrom>";
$schema .= "<PaymentInformation>";
$schema .= "<Prepaid>";
$schema .= "<BillShipper>";
$schema .= "<AccountNumber>{$account}</AccountNumber>";
$schema .= "</BillShipper>";
$schema .= "</Prepaid>";
$schema .= "</PaymentInformation>";
if (variable_get('uc_ups_negotiated_rates', false)) {
$schema .= "<RateInformation>\n <NegotiatedRatesIndicator/>\n </RateInformation>";
}
$schema .= "<Service>";
$schema .= "<Code>{$service[code]}</Code>";
$schema .= "<Description>{$service[description]}</Description>";
$schema .= "</Service>";
$schema .= $package_schema;
$schema .= "</Shipment>";
$schema .= "<LabelSpecification>";
$schema .= "<LabelPrintMethod>";
$schema .= "<Code>GIF</Code>";
$schema .= "</LabelPrintMethod>";
$schema .= "<LabelImageFormat>";
$schema .= "<Code>GIF</Code>";
$schema .= "</LabelImageFormat>";
$schema .= "</LabelSpecification>";
$schema .= "</ShipmentConfirmRequest>";
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) {
include_once drupal_get_path('module', 'uc_store') . '/includes/simplexml.php';
$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;
$conversion = uc_weight_conversion($product->length_units, 'in');
$package->length = max($product->length * $conversion, $package->length);
$package->width = max($product->width * $conversion, $package->width);
$package->height = max($product->height * $conversion, $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 = variable_get('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)) {
$quotes[$ups_service]['debug'] .= htmlentities($request) . ' <br /><br /> ' . htmlentities($resp->data);
}
$response = new JSimpleXML();
$response
->loadString($resp->data);
if (isset($response->document->response[0]->error)) {
foreach ($response->document->response[0]->error as $error) {
if (user_access('configure quotes') && variable_get('uc_quote_display_debug', false)) {
$quotes[$ups_service]['error'][] = $error->errorseverity[0]
->data() . ' ' . $error->errorcode[0]
->data() . ': ' . $error->errordescription[0]
->data();
}
if ($error->errorseverity[0]
->data() == 'HardError') {
// 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->document->ratedshipment)) {
$charge = $response->document->ratedshipment[0]->totalcharges[0];
if (isset($response->document->ratedshipment[0]->negotiatedrates)) {
$charge = $response->document->ratedshipment[0]->negotiatedrates[0]->netsummarycharges[0]->grandtotal[0];
}
if (!isset($charge->currencycode) || $charge->currencycode[0]
->data() == variable_get('uc_currency_code', "USD")) {
$rate = uc_ups_markup($charge->monetaryvalue[0]
->data());
$quotes[$ups_service]['rate'] += $rate;
}
}
}
}
uasort($quotes, 'uc_quote_price_sort');
foreach ($quotes as $key => $quote) {
if (isset($quote['rate'])) {
$quotes[$key]['format'] = uc_currency_format($quote['rate']);
$quotes[$key]['option_label'] = '<img class="ups_logo" src="' . base_path() . drupal_get_path('module', 'uc_ups') . '/uc_ups_logo.gif" /> ' . $method['ups']['quote']['accessorials'][$key] . t(' Rate');
}
}
/**
* 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();
$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 message LIKE '%%SSL: fatal protocol error%%'");
// End of ugly hack.
return $quotes;
}
/**
* Shipment creation callback.
*
* Confirm shipment data before requesting a shipping label.
*
* @param $order_id
* The order id for the shipment.
* @param $package_ids
* Array of package ids to shipped.
* @ingroup forms
* @see uc_ups_fulfill_order_submit
*/
function uc_ups_fulfill_order($order_id, $package_ids) {
$form = array();
$pkg_types = _uc_ups_pkg_types();
if ($order = uc_order_load($order_id)) {
$form['order_id'] = array(
'#type' => 'value',
'#value' => $order_id,
);
$packages = array();
$addresses = array();
$form['packages'] = array(
'#tree' => true,
);
foreach ($package_ids as $id) {
$package = uc_shipping_package_load($id);
if ($package) {
foreach ($package->addresses as $address) {
if (!in_array($address, $addresses)) {
$addresses[] = $address;
}
}
// Create list of products and get a representative product for default values
$product_list = array();
$declared_value = 0;
foreach ($package->products as $product) {
$product_list[] = $product->qty . ' x ' . $product->model;
$declared_value += $product->qty * $product->price;
}
$ups_data = db_fetch_array(db_query("SELECT pkg_type FROM {uc_ups_products} WHERE nid = %d", $product->nid));
$product->ups = $ups_data;
$pkg_form = array(
'#type' => 'fieldset',
'#title' => t('Package !id', array(
'!id' => $id,
)),
);
$pkg_form['products'] = array(
'#value' => theme('item_list', $product_list),
);
$pkg_form['package_id'] = array(
'#type' => 'hidden',
'#value' => $id,
);
$pkg_form['pkg_type'] = array(
'#type' => 'select',
'#title' => t('Package type'),
'#options' => $pkg_types,
'#default_value' => $product->ups['pkg_type'],
'#required' => true,
);
$pkg_form['declared_value'] = array(
'#type' => 'textfield',
'#title' => t('Declared value'),
'#default_value' => $declared_value,
'#required' => true,
);
$pkg_type['dimensions'] = array(
'#type' => 'fieldset',
'#title' => t('Dimensions'),
'#description' => t('Physical dimensions of the package.'),
'#theme' => 'uc_ups_dimensions',
);
$pkg_form['dimensions']['units'] = array(
'#type' => 'select',
'#title' => t('Units of measurement'),
'#options' => array(
'in' => t('Inches'),
'ft' => t('Feet'),
'cm' => t('Centimeters'),
'mm' => t('Millimeters'),
),
'#default_value' => $product->length_units ? $product->length_units : variable_get('uc_length_unit', 'in'),
);
$pkg_form['dimensions']['length'] = array(
'#type' => 'textfield',
'#title' => t('Length'),
'#default_value' => $product->length,
);
$pkg_form['dimensions']['width'] = array(
'#type' => 'textfield',
'#title' => t('Width'),
'#default_value' => $product->width,
);
$pkg_form['dimensions']['height'] = array(
'#type' => 'textfield',
'#title' => t('Height'),
'#default_value' => $product->height,
);
$form['packages'][$id] = $pkg_form;
}
}
$form = array_merge($form, uc_shipping_address_form($addresses, $order));
foreach (array(
'delivery_email',
'delivery_last_name',
'delivery_company',
'delivery_street1',
'delivery_city',
'delivery_zone',
'delivery_country',
'delivery_postal_code',
) as $field) {
$form['destination'][$field]['#required'] = true;
}
$ups_services = _uc_ups_service_list();
$services = array_filter(variable_get('uc_ups_services', array()));
foreach ($services as $ups_id => $service) {
$services[$ups_id] = $ups_services[$ups_id];
}
if (count($services)) {
$form['service'] = array(
'#type' => 'select',
'#title' => t('UPS service'),
'#options' => $services,
);
}
$today = getdate();
$form['ship_date'] = array(
'#type' => 'date',
'#title' => t('Ship date'),
'#default_value' => array(
'year' => $today['year'],
'month' => $today['mon'],
'day' => $today['mday'],
),
);
$form['expected_delivery'] = array(
'#type' => 'date',
'#title' => t('Expected delivery'),
'#default_value' => array(
'year' => $today['year'],
'month' => $today['mon'],
'day' => $today['mday'],
),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Review shipment'),
);
}
else {
drupal_set_message(t("What? That's not an order id. You can't create a shipment without an order."));
drupal_goto('admin/store/orders');
}
return $form;
}
/**
* Validation handler for uc_ups_fulfill_order().
*
* Pass final information into shipment object.
*
* @see uc_ups_confirm_shipment
*/
function uc_ups_fulfill_order_validate($form_id, $form_values) {
include_once drupal_get_path('module', 'uc_store') . '/includes/simplexml.php';
$origin = new stdClass();
$destination = new stdClass();
$packages = array();
foreach ($form_values as $key => $value) {
if (substr($key, 0, 7) == 'pickup_') {
$field = substr($key, 7);
$origin->{$field} = $value;
}
else {
if (substr($key, 0, 9) == 'delivery_') {
$field = substr($key, 9);
$destination->{$field} = $value;
}
}
}
$_SESSION['ups'] = array();
$_SESSION['ups']['origin'] = $origin;
$_SESSION['ups']['destination'] = $destination;
foreach ($form_values['packages'] as $id => $pkg_form) {
$package = uc_shipping_package_load($id);
$package->pkg_type = $pkg_form['pkg_type'];
$package->value = $pkg_form['declared_value'];
$package->length = $pkg_form['dimensions']['length'];
$package->width = $pkg_form['dimensions']['width'];
$package->height = $pkg_form['dimensions']['height'];
$package->length_units = $pkg_form['dimensions']['units'];
$package->qty = 1;
$_SESSION['ups']['packages'][$id] = $package;
}
$_SESSION['ups']['service'] = $form_values['service'];
$_SESSION['ups']['ship_date'] = $form_values['ship_date'];
$_SESSION['ups']['expected_delivery'] = $form_values['expected_delivery'];
$_SESSION['ups']['order_id'] = $form_values['order_id'];
$request = uc_ups_shipment_request($_SESSION['ups']['packages'], $origin, $destination, $form_values['service']);
//print htmlentities($request);
$response_obj = drupal_http_request(variable_get('uc_ups_connection_address', 'https://wwwcie.ups.com/ups.app/xml/') . 'ShipConfirm', array(), 'POST', $request);
$response = new JSimpleXML();
$response
->loadString($response_obj->data);
//drupal_set_message('<pre>'. htmlentities($response->document->asXML()) .'</pre>');
if (is_array($response->document->response[0]->error)) {
$error = $response->document->response[0]->error[0];
$error_msg = $error->errorseverity[0]
->data() . ' Error ' . $error->errorcode[0]
->data() . ': ' . $error->errordescription[0]
->data();
drupal_set_message($error_msg, 'error');
//drupal_set_message('<pre>'. print_r($_SESSION['ups']['packages'], true) .'</pre>' . htmlentities($request) .' <br /><br /> '. htmlentities($response->data));
if ($error->errorseverity[0]
->data() == 'Hard') {
return null;
}
}
$charge = new stdClass();
// if NegotiatedRates exist, quote based on those, otherwise, use TotalCharges
if (is_array($response->document->shipmentcharges)) {
$charge = $response->document->shipmentcharges[0]->totalcharges[0];
$_SESSION['ups']['rate']['type'] = t('Total Charges');
if (is_array($response->document->shipmentcharges[0]->negotiatedrates)) {
$charge = $response->document->shipmentcharges[0]->negotiatedrates[0]->netsummarycharges[0]->grandtotal[0];
$_SESSION['ups']['rate']['type'] = t('Negotiated Rates');
}
}
$_SESSION['ups']['rate']['currency'] = $charge->currencycode[0]
->data();
$_SESSION['ups']['rate']['amount'] = $charge->monetaryvalue[0]
->data();
$_SESSION['ups']['digest'] = $response->document->shipmentdigest[0]
->data();
}
/**
* Submit handler for uc_ups_fulfill_order().
*
* Pass final information into shipment object.
*
* @see uc_ups_confirm_shipment
*/
function uc_ups_fulfill_order_submit($form_id, $form_values) {
return 'admin/store/orders/' . $form_values['order_id'] . '/shipments/ups';
}
/**
* Last chance for user to review shipment.
*
* @ingroup forms
* @see theme_uc_ups_confirm_shipment
* @see uc_ups_confirm_shipment_submit
*/
function uc_ups_confirm_shipment($order_id) {
$form = array();
$form['digest'] = array(
'#type' => 'hidden',
'#value' => $_SESSION['ups']['digest'],
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Request Pickup'),
);
return $form;
}
/**
* Display final shipment information for review.
*/
function theme_uc_ups_confirm_shipment($form) {
$output = '';
$output .= '<div class="shipping_address"><b>' . t('Ship from:') . '</b><br />';
$output .= uc_address_format(check_plain($_SESSION['ups']['origin']->first_name), check_plain($_SESSION['ups']['origin']->last_name), check_plain($_SESSION['ups']['origin']->company), check_plain($_SESSION['ups']['origin']->street1), check_plain($_SESSION['ups']['origin']->street2), check_plain($_SESSION['ups']['origin']->city), check_plain($_SESSION['ups']['origin']->zone), check_plain($_SESSION['ups']['origin']->postal_code), check_plain($_SESSION['ups']['origin']->country));
$output .= '<br />' . check_plain($_SESSION['ups']['origin']->email);
$output .= '</div>';
$output .= '<div class="shipping_address"><b>' . t('Ship to:') . '</b><br />';
$output .= uc_address_format(check_plain($_SESSION['ups']['destination']->first_name), check_plain($_SESSION['ups']['destination']->last_name), check_plain($_SESSION['ups']['destination']->company), check_plain($_SESSION['ups']['destination']->street1), check_plain($_SESSION['ups']['destination']->street2), check_plain($_SESSION['ups']['destination']->city), check_plain($_SESSION['ups']['destination']->zone), check_plain($_SESSION['ups']['destination']->postal_code), check_plain($_SESSION['ups']['destination']->country));
$output .= '<br />' . check_plain($_SESSION['ups']['destination']->email);
$output .= '</div>';
$output .= '<div class="shipment_data">';
$method = uc_ups_shipping_method();
$output .= '<b>' . $method['ups']['quote']['accessorials'][$_SESSION['ups']['service']] . '</b><br />';
$output .= '<i>' . check_plain($_SESSION['ups']['rate']['type']) . '</i>: ' . uc_currency_format($_SESSION['ups']['rate']['amount']) . ' (' . check_plain($_SESSION['ups']['rate']['currency']) . ')<br />';
$ship_date = $_SESSION['ups']['ship_date'];
$output .= 'Ship date: ' . format_date(gmmktime(12, 0, 0, $ship_date['month'], $ship_date['day'], $ship_date['year']), 'custom', variable_get('uc_date_format_default', 'm/d/Y'));
$exp_delivery = $_SESSION['ups']['expected_delivery'];
$output .= '<br />Expected delivery: ' . format_date(gmmktime(12, 0, 0, $exp_delivery['month'], $exp_delivery['day'], $exp_delivery['year']), 'custom', variable_get('uc_date_format_default', 'm/d/Y'));
$output .= "</div>\n<br style=\"clear: both;\" />";
$output .= drupal_render($form);
return $output;
}
/**
* Submit handler for uc_ups_confirm_shipment().
*
* Generate label and schedule pickup of the shipment.
*/
function uc_ups_confirm_shipment_submit($form_id, $form_values) {
include_once drupal_get_path('module', 'uc_store') . '/includes/simplexml.php';
// Request pickup using parameters in form.
$order_id = $_SESSION['ups']['order_id'];
$packages = array_keys($_SESSION['ups']['packages']);
$request = uc_ups_request_pickup($form_values['digest'], $order_id, $packages);
$result = drupal_http_request(variable_get('uc_ups_connection_address', 'https://wwwcie.ups.com/ups.app/xml/') . 'ShipAccept', array(), 'POST', $request);
$response = new JSimpleXML();
$response
->loadString($result->data);
$code = $response->document->response[0]->responsestatuscode[0]
->data();
if ($code == 0) {
// failed request
$error = $response->document->response[0]->error[0];
$error_severity = $error->errorseverity[0]
->data();
$error_code = $error->errorcode[0]
->data();
$error_description = $error->errordescription[0]
->data();
drupal_set_message(t('(@severity error @code) @description', array(
'@severity' => $error_severity,
'@code' => $error_code,
'@description' => $error_description,
)), 'error');
if ($error_severity == 'HardError') {
return 'admin/store/orders/' . $order_id . '/shipments/ups/' . implode('/', $packages);
}
}
$shipment = new stdClass();
$shipment->order_id = $order_id;
$shipment->origin = drupal_clone($_SESSION['ups']['origin']);
$shipment->destination = drupal_clone($_SESSION['ups']['destination']);
$shipment->packages = $_SESSION['ups']['packages'];
$shipment->shipping_method = 'ups';
$shipment->accessorials = $_SESSION['ups']['service'];
$shipment->carrier = t('UPS');
// if NegotiatedRates exist, quote based on those, otherwise, use TotalCharges
if (is_array($response->document->shipmentresults[0]->shipmentcharges)) {
$charge = $response->document->shipmentresults[0]->shipmentcharges[0]->totalcharges[0];
if (is_array($response->document->shipmentresults[0]->negotiatedrates)) {
$charge = $response->document->shipmentresults[0]->negotiatedrates[0]->netsummarycharges[0]->grandtotal[0];
}
}
$cost = $charge->monetaryvalue[0]
->data();
$shipment->cost = $cost;
$shipment->tracking_number = $response->document->shipmentresults[0]->shipmentidentificationnumber[0]
->data();
$ship_date = $_SESSION['ups']['ship_date'];
$shipment->ship_date = gmmktime(12, 0, 0, $ship_date['month'], $ship_date['day'], $ship_date['year']);
$exp_delivery = $_SESSION['ups']['expected_delivery'];
$shipment->expected_delivery = gmmktime(12, 0, 0, $exp_delivery['month'], $exp_delivery['day'], $exp_delivery['year']);
foreach ($response->document->shipmentresults[0]->packageresults as $package_results) {
$package =& current($shipment->packages);
$package->tracking_number = $package_results->trackingnumber[0]
->data();
$label_image = $package_results->labelimage[0]->graphicimage[0]
->data();
if (file_check_directory(file_create_path('ups_labels'), FILE_CREATE_DIRECTORY)) {
$label_path = file_create_path('ups_labels') . '/label' . $package->tracking_number . '.gif';
if ($label_file = fopen($label_path, 'wb')) {
fwrite($label_file, base64_decode($label_image));
fclose($label_file);
$package->label_image = $label_path;
}
else {
drupal_set_message(t('Could not open a file to save the label image.'), 'error');
}
}
else {
drupal_set_message(t('Could not find or create the directory "ups_labels" in the file system path.'), 'error');
}
unset($package);
next($shipment->packages);
}
uc_shipping_shipment_save($shipment);
unset($_SESSION['ups']);
return 'admin/store/orders/' . $order_id . '/shipments';
}
/**
* Construct an XML label and pickup request.
*
* @param $digest
* Base-64 encoded shipment request.
* @param $order_id
* The order id of the shipment.
* @param $packages
* An array of package ids to be shipped.
* @return
* ShipmentAcceptRequest XML document to send to UPS.
*/
function uc_ups_request_pickup($digest, $order_id = 0, $packages = array()) {
$packages = (array) $packages;
$schema = uc_ups_access_request();
$schema .= "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<ShipmentAcceptRequest>\n <Request>\n <RequestAction>ShipAccept</RequestAction>";
if ($order_id || count($packages)) {
$schema .= "\n<TransactionReference>\n <CustomerContext>";
if ($order_id) {
$schema .= "<OrderId>" . $order_id . "</OrderId>\n";
}
foreach ($packages as $pkg_id) {
$schema .= "<PackageId>" . $pkg_id . "</PackageId>\n";
}
$schema .= "</CustomerContext>\n</TransactionReference>\n";
}
$schema .= " </Request>\n <ShipmentDigest>" . $digest . "</ShipmentDigest>\n</ShipmentAcceptRequest>";
//drupal_set_message('<pre>'. htmlentities($schema) .'</pre>');
return $schema;
}
/**
* Display the shipping label for printing.
*
* Each argument is a component of the file path to the image.
*
* @ingroup themeable
*/
function theme_uc_ups_label_image() {
$args = func_get_args();
$image_path = implode('/', $args);
print '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 3.2//EN">
<html><head><title>
View/Print Label</title></head><style>
.small_text {font-size: 80%;}
.large_text {font-size: 115%;}
</style>
<body bgcolor="#FFFFFF">
<table border="0" cellpadding="0" cellspacing="0" width="600"><tr>
<td height="410" align="left" valign="top">
<b class="large_text">View/Print Label</b>
<br />
<ol class="small_text"> <li><b>Print the label:</b>
Select Print from the File menu in this browser window to print the label below.<br /><br /><li><b>
Fold the printed label at the dotted line.</b>
Place the label in a UPS Shipping Pouch. If you do not have a pouch, affix the folded label using clear plastic shipping tape over the entire label.<br /><br /><li><b>GETTING YOUR SHIPMENT TO UPS<br />
Customers without a Daily Pickup</b><ul><li>Ground, 3 Day Select, and Standard to Canada shipments must be dropped off at an authorized UPS location, or handed to a UPS driver. Pickup service is not available for these services. To find the nearest drop-off location, select the Drop-off icon from the UPS tool bar.<li>
Air shipments (including Worldwide Express and Expedited) can be picked up or dropped off. To schedule a pickup, or to find a drop-off location, select the Pickup or Drop-off icon from the UPS tool bar. </ul> <br />
<b>Customers with a Daily Pickup</b><ul><li>
Your driver will pickup your shipment(s) as usual. </ul>
</ol></td></tr></table><table border="0" cellpadding="0" cellspacing="0" width="600">
<tr>
<td class="small_text" align="left" valign="top">
FOLD HERE</td>
</tr>
<tr>
<td align="left" valign="top"><hr />
</td>
</tr>
</table>
<table>
<tr>
<td height="10">
</td>
</tr>
</table>
<table border="0" cellpadding="0" cellspacing="0" width="650" ><tr>
<td align="left" valign="top">
<img src="' . base_path() . $image_path . '" height="392" width="672">
</td>
</tr></table>
</body>
</html>';
exit;
}
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;
}
function uc_ups_void_shipment($shipment_number, $tracking_numbers = array()) {
include_once drupal_get_path('module', 'uc_store') . '/includes/simplexml.php';
$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 JSimpleXML();
$response
->loadString($resp->data);
if (is_array($response->document->response)) {
if (is_array($response->document->response[0]->responsestatuscode)) {
$success = $response->document->response[0]->responsestatuscode[0]
->data();
}
if (is_array($response->document->response[0]->error)) {
foreach ($response->document->response[0]->error as $error) {
drupal_set_message($error->errorseverity[0]
->data() . ' ' . $error->errorcode[0]
->data() . ': ' . $error->errordescription[0]
->data(), 'error');
}
}
}
if (is_array($response->document->status)) {
if (is_array($response->document->status[0]->statustype)) {
$success = $response->document->status[0]->statustype[0]->code[0]
->data();
}
}
return (bool) $success;
}
/**
* Modify the rate received from UPS before displaying to the customer.
*/
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(
'03' => t('UPS Ground'),
'11' => t('UPS Standard'),
'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'),
);
}
/**
* 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'),
);
}
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_confirm_shipment | Display final shipment information for review. |
theme_uc_ups_label_image | Display the shipping label for printing. |
uc_ups_access_request | Return XML access request to be prepended to all requests to the UPS webservice. |
uc_ups_admin_settings | UPS Online Tool settings. |
uc_ups_admin_settings_validate | Validation handler for uc_ups_admin_settings. |
uc_ups_configuration | Implementation of hook_configuration(). |
uc_ups_confirm_shipment | Last chance for user to review shipment. |
uc_ups_confirm_shipment_submit | Submit handler for uc_ups_confirm_shipment(). |
uc_ups_form_alter | Implementation of hook_form_alter(). |
uc_ups_fulfill_order | Shipment creation callback. |
uc_ups_fulfill_order_submit | Submit handler for uc_ups_fulfill_order(). |
uc_ups_fulfill_order_validate | Validation handler for uc_ups_fulfill_order(). |
uc_ups_markup | Modify the rate received from UPS before displaying to the customer. |
uc_ups_menu | Implementation of hook_menu(). |
uc_ups_nodeapi | Implementation of 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_request_pickup | Construct an XML label and pickup request. |
uc_ups_shipment_request | Construct an XML shippment request. |
uc_ups_shipping_method | Implementation of Übercart's hook_shipping_method(). |
uc_ups_shipping_quote | Construct an XML quote request. |
uc_ups_shipping_type | Implementation of Übercart's hook_shipping_type(). |
uc_ups_store_status | Implementation of Übercart's hook_store_status(). |
uc_ups_void_shipment | |
uc_ups_void_shipment_request | |
_uc_ups_new_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. |