uc_usps.module

Tracking 5.x-1.x branch
  1. drupal
    1. 5 contributions/ubercart/shipping/uc_usps/uc_usps.module
    2. 6 contributions/ubercart/shipping/uc_usps/uc_usps.module
    3. 7 contributions/ubercart/shipping/uc_usps/uc_usps.module

Shipping quote method module that receives quotes from the United States Postal Service via XML web service.

Coded by Lyle Mantooth.

Functions & methods

NameDescription
uc_usps_admin_settingsConfigure USPS settings.
uc_usps_configurationImplementation of hook_configuration().
uc_usps_form_alterImplementation of hook_form_alter().
uc_usps_intl_quoteCallback for retrieving USPS shipping quote to other countries.
uc_usps_intl_rate_request
uc_usps_markupModify the rate received from USPS before displaying to the customer.
uc_usps_menuImplementation of hook_menu().
uc_usps_nodeapiImplementation of hook_nodeapi().
uc_usps_quoteCallback for retrieving USPS shipping quote.
uc_usps_rate_requestConstruct an XML quote request.
uc_usps_shipping_methodImplementation of Übercart's hook_shipping_method().
uc_usps_shipping_typeImplementation of Übercart's hook_shipping_type().
_uc_usps_intl_services
_uc_usps_package_products
_uc_usps_pkg_typesConvenience function for select form elements.
_uc_usps_servicesConvenience function for select form elements.

File

View source
  1. <?php
  2. /**
  3. * @file
  4. * Shipping quote method module that receives quotes from the United States
  5. * Postal Service via XML web service.
  6. *
  7. * Coded by Lyle Mantooth.
  8. */
  9. /******************************************************************************
  10. * Drupal Hooks *
  11. ******************************************************************************/
  12. /**
  13. * Implementation of hook_menu().
  14. */
  15. function uc_usps_menu($may_cache) {
  16. $items = array();
  17. if ($may_cache) {
  18. $items[] = array('path' => 'admin/store/settings/quotes/methods/usps',
  19. 'title' => t('USPS'),
  20. 'access' => user_access('configure quotes'),
  21. 'callback' => 'drupal_get_form',
  22. 'callback arguments' => array('uc_usps_admin_settings'),
  23. 'type' => MENU_LOCAL_TASK,
  24. );
  25. }
  26. return $items;
  27. }
  28. /**
  29. * Implementation of hook_form_alter().
  30. *
  31. * Add package type to products.
  32. *
  33. * @see uc_product_form
  34. */
  35. function uc_usps_form_alter($form_id, &$form) {
  36. $node = $form['#node'];
  37. if (is_object($node) && $form_id == $node->type .'_node_form' && isset($form['base']['dimensions']) && in_array($node->type, module_invoke_all('product_types'))) {
  38. $enabled = variable_get('uc_quote_enabled', array());
  39. $weight = variable_get('uc_quote_method_weight', array('usps' => 0, 'usps_intl' => 1));
  40. $form['shipping']['usps'] = array('#type' => 'fieldset',
  41. '#title' => t('USPS product description'),
  42. '#collapsible' => true,
  43. '#collapsed' => ($enabled['usps'] == false || uc_product_get_shipping_type($node) != 'small_package'),
  44. '#weight' => $weight['usps'],
  45. '#tree' => true,
  46. );
  47. $form['shipping']['usps']['container'] = array('#type' => 'select',
  48. '#title' => t('Package type'),
  49. '#options' => _uc_usps_pkg_types(),
  50. '#default_value' => $node->usps['container'] ? $node->usps['container'] : 'RECTANGULAR',
  51. );
  52. }
  53. }
  54. /**
  55. * Implementation of hook_nodeapi().
  56. */
  57. function uc_usps_nodeapi(&$node, $op) {
  58. if (in_array($node->type, module_invoke_all('product_types'))) {
  59. switch ($op) {
  60. case 'insert':
  61. case 'update':
  62. if (isset($node->usps)) {
  63. $usps_values = $node->usps;
  64. if (!$node->revision) {
  65. db_query("DELETE FROM {uc_usps_products} WHERE vid = %d", $node->vid);
  66. }
  67. db_query("INSERT INTO {uc_usps_products} (vid, nid, container) VALUES (%d, %d, '%s')",
  68. $node->vid, $node->nid, $usps_values['container']);
  69. }
  70. break;
  71. case 'load':
  72. if (uc_product_get_shipping_type($node) == 'small_package') {
  73. return array('usps' => db_fetch_array(db_query("SELECT * FROM {uc_usps_products} WHERE vid = %d", $node->vid)));
  74. }
  75. break;
  76. case 'delete':
  77. db_query("DELETE FROM {uc_usps_products} WHERE nid = %d", $node->nid);
  78. break;
  79. case 'delete revision':
  80. db_query("DELETE FROM {uc_usps_products} WHERE vid = %d", $node->vid);
  81. break;
  82. }
  83. }
  84. }
  85. /******************************************************************************
  86. * Workflow-ng Hooks *
  87. ******************************************************************************/
  88. /**
  89. * Implementation of hook_configuration().
  90. *
  91. * Connect USPS quote action and event.
  92. */
  93. function uc_usps_configuration() {
  94. $enabled = variable_get('uc_quote_enabled', array());
  95. $configurations = array(
  96. 'uc_usps_get_quote' => array(
  97. '#label' => t('Shipping quote from USPS'),
  98. '#event' => 'get_quote_from_usps',
  99. '#module' => 'uc_usps',
  100. '#active' => $enabled['usps'],
  101. ),
  102. 'uc_usps_get_intl_quote' => array(
  103. '#label' => t('Shipping quote from USPS Intl.'),
  104. '#event' => 'get_quote_from_usps_intl',
  105. '#module' => 'uc_usps',
  106. '#active' => $enabled['usps_intl'],
  107. ),
  108. );
  109. // Domestic areas include U.S., American Samoa, Guam, Puerto Rico, and the Virgin Islands
  110. $countries = array(16, 316, 630, 840, 850);
  111. $us_area_condition = workflow_ng_use_condition('uc_order_condition_delivery_country', array(
  112. '#label' => t('Is in domestic US areas (US, AS, GU, PR, VI)'),
  113. '#settings' => array(
  114. 'countries' => $countries,
  115. ),
  116. ));
  117. $not_us_area_condition = workflow_ng_use_condition('uc_order_condition_delivery_country', array(
  118. '#label' => t('Is not in domestic US areas (US, AS, GU, PR, VI)'),
  119. '#negate' => true,
  120. '#settings' => array(
  121. 'countries' => $countries,
  122. ),
  123. ));
  124. $action = workflow_ng_use_action('uc_quote_action_get_quote', array(
  125. '#label' => t('Fetch a shipping quote'),
  126. ));
  127. $configurations['uc_usps_get_quote'] = workflow_ng_configure($configurations['uc_usps_get_quote'], $action, $us_area_condition);
  128. $configurations['uc_usps_get_intl_quote'] = workflow_ng_configure($configurations['uc_usps_get_intl_quote'], $action, $not_us_area_condition);
  129. return $configurations;
  130. }
  131. /******************************************************************************
  132. * Übercart Hooks *
  133. ******************************************************************************/
  134. /**
  135. * Implementation of Übercart's hook_shipping_type().
  136. */
  137. function uc_usps_shipping_type() {
  138. $weight = variable_get('uc_quote_type_weight', array('small_package' => 0));
  139. $types = array('small_package' => array(
  140. 'id' => 'small_package',
  141. 'title' => t('Small package'),
  142. 'weight' => $weight['small_package'],
  143. ));
  144. return $types;
  145. }
  146. /**
  147. * Implementation of Übercart's hook_shipping_method().
  148. */
  149. function uc_usps_shipping_method() {
  150. $enabled = variable_get('uc_quote_enabled', array());
  151. $weight = variable_get('uc_quote_method_weight', array('usps' => 0, 'usps_intl' => 1));
  152. $methods = array(
  153. 'usps' => array(
  154. 'id' => 'usps',
  155. 'title' => t('U.S. Postal Service'),
  156. 'quote' => array(
  157. 'type' => 'small_package',
  158. 'callback' => 'uc_usps_quote',
  159. 'accessorials' => _uc_usps_services(),
  160. ),
  161. 'enabled' => $enabled['usps'],
  162. 'weight' => $weight['usps'],
  163. ),
  164. 'usps_intl' => array(
  165. 'id' => 'usps_intl',
  166. 'title' => t('U.S. Postal Service (Intl.)'),
  167. 'quote' => array(
  168. 'type' => 'small_package',
  169. 'callback' => 'uc_usps_intl_quote',
  170. 'accessorials' => _uc_usps_intl_services(),
  171. ),
  172. 'enabled' => $enabled['usps_intl'],
  173. 'weight' => $weight['usps_intl'],
  174. ),
  175. );
  176. return $methods;
  177. }
  178. /******************************************************************************
  179. * Menu Callbacks *
  180. ******************************************************************************/
  181. /**
  182. * Configure USPS settings.
  183. *
  184. * @ingroup forms
  185. */
  186. function uc_usps_admin_settings() {
  187. $form = array();
  188. $form['uc_usps_user_id'] = array('#type' => 'textfield',
  189. '#title' => t('USPS user ID'),
  190. '#description' => t('To acquire or locate your user ID, refer to the <a href="!url">USPS documentation</a>.', array('!url' => 'http://www.ubercart.org/docs/user/312/usps_web_tool_shipping_quote_settings')),
  191. '#default_value' => variable_get('uc_usps_user_id', ''),
  192. );
  193. $form['uc_usps_services'] = array('#type' => 'checkboxes',
  194. '#title' => t('USPS services'),
  195. '#default_value' => variable_get('uc_usps_services', _uc_usps_services()),
  196. '#options' => _uc_usps_services(),
  197. '#description' => t('Select the USPS services that are available to customers. Be sure to include the services that the Postal Service agrees are available to you.'),
  198. );
  199. $form['uc_usps_intl_services'] = array('#type' => 'checkboxes',
  200. '#title' => t('USPS international services'),
  201. '#default_value' => variable_get('uc_usps_intl_services', _uc_usps_intl_services()),
  202. '#options' => _uc_usps_intl_services(),
  203. '#description' => t('Select the USPS services that are available to customers. Be sure to include the services that the Postal Service agrees are available to you.'),
  204. );
  205. $form['uc_usps_markup_type'] = array('#type' => 'select',
  206. '#title' => t('Markup type'),
  207. '#default_value' => variable_get('uc_usps_markup_type', 'percentage'),
  208. '#options' => array(
  209. 'percentage' => t('Percentage (%)'),
  210. 'multiplier' => t('Multiplier (×)'),
  211. 'currency' => t('Addition (!currency)', array('!currency' => variable_get('uc_currency_sign', '$'))),
  212. ),
  213. );
  214. $form['uc_usps_markup'] = array('#type' => 'textfield',
  215. '#title' => t('Shipping rate markup'),
  216. '#default_value' => variable_get('uc_usps_markup', '0'),
  217. '#description' => t('Markup shipping rate quote by dollar amount, percentage, or multiplier.'),
  218. );
  219. $form['uc_usps_all_in_one'] = array('#type' => 'radios',
  220. '#title' => t('Product packages'),
  221. '#default_value' => variable_get('uc_usps_all_in_one', 1),
  222. '#options' => array(
  223. 0 => t('Each in its own package'),
  224. 1 => t('All in one'),
  225. ),
  226. '#description' => t('Indicate whether each product is quoted as shipping separately or all in one package.'),
  227. );
  228. return system_settings_form($form);
  229. }
  230. /******************************************************************************
  231. * Module Functions *
  232. ******************************************************************************/
  233. /**
  234. * Callback for retrieving USPS shipping quote.
  235. *
  236. * @param $products
  237. * Array of cart contents.
  238. * @param $details
  239. * Order details other than product information.
  240. * @return
  241. * JSON object containing rate, error, and debugging information.
  242. */
  243. function uc_usps_quote($products, $details) {
  244. include_once(drupal_get_path('module', 'uc_store') .'/includes/simplexml.php');
  245. //drupal_set_message('<pre>'. print_r($products, true) .'</pre>');
  246. //drupal_set_message('<pre>'. print_r($details, true) .'</pre>');
  247. $services = array();
  248. $addresses = array((array)variable_get('uc_quote_store_default_address', new stdClass()));
  249. $packages = _uc_usps_package_products($products, $addresses);
  250. if (!count($packages)) {
  251. return array();
  252. }
  253. $dest = (object)$details;
  254. $usps_server = 'production.shippingapis.com';
  255. $api_dll = 'ShippingAPI.dll';
  256. $connection_url = 'http://'. $usps_server .'/'. $api_dll;
  257. foreach ($packages as $key => $ship_packages) {
  258. $orig = (object)$addresses[$key];
  259. $orig->email = variable_get('uc_store_email', '');
  260. $request = uc_usps_rate_request($ship_packages, $orig, $dest);
  261. $result = drupal_http_request($connection_url, array(), 'POST', $request);
  262. if (user_access('configure quotes') && variable_get('uc_quote_display_debug', false)) {
  263. $services['data']['debug'] .= htmlentities($result->data) ."<br />\n";
  264. }
  265. $response = new JSimpleXML();
  266. $response->loadString($result->data);
  267. if (is_array($response->document->package)) {
  268. foreach ($response->document->package as $package) {
  269. if (isset($package->error)) {
  270. $services['data']['error'] .= $package->error[0]->description[0]->data() .'<br />';
  271. }
  272. else {
  273. foreach ($package->postage as $postage) {
  274. $attr = $postage->attributes();
  275. if ($attr['classid'] === 0 || $attr['classid'] === '0') {
  276. if ($postage->mailservice[0]->data() == "First-Class Mail Parcel") {
  277. $attr['classid'] = 'zeroParcel';
  278. }
  279. else if ($postage->mailservice[0]->data() == "First-Class Mail Flat") {
  280. $attr['classid'] = 'zeroFlat';
  281. }
  282. else {
  283. $attr['classid'] = 'zero';
  284. }
  285. }
  286. $services[$attr['classid']]['label'] = t('U.S.P.S. @service', array('@service' => $postage->mailservice[0]->data()));
  287. $services[$attr['classid']]['rate'] += uc_usps_markup($postage->rate[0]->data());
  288. }
  289. }
  290. }
  291. }
  292. }
  293. $usps_services = array_filter(variable_get('uc_usps_services', _uc_usps_services()));
  294. foreach ($services as $service => $quote) {
  295. if ($service != 'data' && !in_array($service, $usps_services)) {
  296. unset($services[$service]);
  297. }
  298. }
  299. /* $transitreq = 'USERID="' . variable_get('uc_usps_user_id', '') .'">' .
  300. '<OriginZip>' . $orig->postal_code . '</OriginZip>' .
  301. '<DestinationZip>' . $dest->postal_code . '</DestinationZip>'; */
  302. foreach ($services as $key => $quote) {
  303. if (isset($quote['rate'])) {
  304. $services[$key]['format'] = uc_currency_format($quote['rate']);
  305. $services[$key]['option_label'] = $quote['label'];
  306. /* if (strpos($quote['label'], 'Express') !== false) {
  307. $transreq = 'API=ExpressMailCommitment&XML='. urlencode(str_replace('Zip', 'ZIP', '<ExpressMailCommitmentRequest ' . $transitreq . '<Date/></ExpressMailCommitmentRequest>'));
  308. }
  309. elseif (strpos($quote['label'], 'Priority') !== false) {
  310. $transreq = 'API=PriorityMail&XML='. urlencode( '<PriorityMailRequest ' . $transitreq . '</PriorityMailRequest>');
  311. }
  312. else {
  313. $transreq = 'API=StandardB&XML='. urlencode( '<StandardBRequest ' . $transitreq . '</StandardBRequest>');
  314. }
  315. $result = drupal_http_request($connection_url, array(), 'POST', $transreq);
  316. if (user_access('configure quotes') && variable_get('uc_quote_display_debug', false)) {
  317. $services['data']['debug'] .= htmlentities($result->data) ."<br />\n";
  318. }
  319. $transresp = new JSimpleXML();
  320. $transresp->loadString($result->data);
  321. if (isset($transresp->document)) {
  322. if ($transresp->document->name() == 'Error') {
  323. $services[$key]['error'][] = $transresp->document->description[0]->data();
  324. }
  325. else if (isset($transresp->document->days)) {
  326. $services[$key]['transit'] = $transresp->document->days[0]->data();
  327. $services[$key]['option_label'] .= format_plural($services[$key]['transit'], ', @count day in transit', ', @count days in transit');
  328. }
  329. else if (isset($transresp->document->commitment)) {
  330. $services[$key]['commitment'] = $transresp->document->commitment[0]->commitmentname[0]->data() .' -- '. $transresp->document->commitment[0]->commitmenttime[0]->data();
  331. $services[$key]['option_label'] .= ', '. $services[$key]['commitment'];
  332. }
  333. } */
  334. }
  335. }
  336. uasort($services, 'uc_quote_price_sort');
  337. return $services;
  338. }
  339. /**
  340. * Callback for retrieving USPS shipping quote to other countries.
  341. *
  342. * @param $products
  343. * Array of cart contents.
  344. * @param $details
  345. * Order details other than product information.
  346. * @return
  347. * JSON object containing rate, error, and debugging information.
  348. */
  349. function uc_usps_intl_quote($products, $details) {
  350. include_once(drupal_get_path('module', 'uc_store') .'/includes/simplexml.php');
  351. $services = array();
  352. $addresses = array(variable_get('uc_quote_store_default_address', new stdClass()));
  353. $packages = _uc_usps_package_products($products, $addresses);
  354. if (!count($packages)) {
  355. return array();
  356. }
  357. $dest = (object)$details;
  358. $usps_server = 'production.shippingapis.com';
  359. $api_dll = 'ShippingAPI.dll';
  360. $connection_url = 'http://'. $usps_server .'/'. $api_dll;
  361. foreach ($packages as $key => $ship_packages) {
  362. $orig = (object)$addresses[$key];
  363. $orig->email = variable_get('uc_store_email', '');
  364. $request = uc_usps_intl_rate_request($ship_packages, $orig, $dest);
  365. $result = drupal_http_request($connection_url, array(), 'POST', $request);
  366. if (user_access('configure quotes') && variable_get('uc_quote_display_debug', false)) {
  367. $services['data']['debug'] .= htmlentities($result->data) ."<br />\n";
  368. }
  369. $response = new JSimpleXML();
  370. $response->loadString($result->data);
  371. // drupal_set_message('<pre>'. htmlentities($result->data) .'</pre>');
  372. if (is_array($response->document->package)) {
  373. foreach ($response->document->package as $package) {
  374. if (isset($package->error)) {
  375. $services['data']['error'] .= $package->error[0]->description[0]->data() .'<br />';
  376. }
  377. else {
  378. foreach ($package->service as $service) {
  379. $attr = $service->attributes();
  380. if ($attr['id'] === 0 || $attr['id'] === '0') {
  381. $attr['id'] = 'zero';
  382. }
  383. $services[$attr['id']]['label'] = t('USPS @service', array('@service' => $service->svcdescription[0]->data()));
  384. $services[$attr['id']]['rate'] += uc_usps_markup($service->postage[0]->data());
  385. }
  386. }
  387. }
  388. }
  389. }
  390. $usps_services = variable_get('uc_usps_intl_services', _uc_usps_intl_services());
  391. foreach ($services as $service => $quote) {
  392. if (!in_array($service, $usps_services)) {
  393. unset($services[$service]);
  394. }
  395. }
  396. foreach ($services as $key => $quote) {
  397. if (isset($quote['rate'])) {
  398. $services[$key]['format'] = uc_currency_format($quote['rate']);
  399. $services[$key]['option_label'] = $quote['label'];
  400. }
  401. }
  402. uasort($services, 'uc_quote_price_sort');
  403. return $services;
  404. }
  405. /**
  406. * Construct an XML quote request.
  407. *
  408. * @param $packages
  409. * Array of packages received from the cart.
  410. * @param $origin
  411. * Delivery origin address information.
  412. * @param $destination
  413. * Delivery destination address information.
  414. * @return
  415. * RateV3Request XML document to send to USPS
  416. */
  417. function uc_usps_rate_request($packages, $origin, $destination) {
  418. $request = '<RateV3Request USERID="' . variable_get('uc_usps_user_id', '') . '">';
  419. $services_count = 0;
  420. foreach ($packages as $package) {
  421. $qty = $package->qty;
  422. for ($i = 0; $i < $qty; $i++) {
  423. $request .= '<Package ID="' . $services_count . '">' .
  424. '<Service>ALL</Service>' .
  425. '<ZipOrigination>' . substr($origin->postal_code, 0, 5) . '</ZipOrigination>' .
  426. '<ZipDestination>' . substr($destination->postal_code, 0, 5) . '</ZipDestination>' .
  427. '<Pounds>' . intval($package->pounds) . '</Pounds>' .
  428. '<Ounces>' . number_format($package->ounces, 1, '.', '') . '</Ounces>' .
  429. '<Container>' . $package->container . '</Container>' .
  430. '<Size>' . $package->size . '</Size>' .
  431. '<Machinable>' . ($package->machinable ? 'True' : 'False') . '</Machinable>' .
  432. '</Package>';
  433. $services_count++;
  434. }
  435. }
  436. $request .= '</RateV3Request>';
  437. $request = 'API=RateV3&XML=' . urlencode($request);
  438. return $request;
  439. }
  440. /*
  441. * uc_usps_intl_rate_request function (added by Erik - feel free to improve)
  442. *
  443. */
  444. function uc_usps_intl_rate_request($packages, $origin, $destination) {
  445. $request = '<IntlRateRequest USERID="' . variable_get('uc_usps_user_id', '') . '">';
  446. $services_count = 0;
  447. // This needs to be international name per USPS website. See http://pe.usps.com/text/Imm/immctry.htm
  448. $shipto_country = uc_get_country_data(array('country_id' => $destination->country));
  449. foreach ($packages as $package) {
  450. $qty = $package->qty;
  451. for ($i = 0; $i < $qty; $i++) {
  452. $request .= '<Package ID="' . $services_count . '">' .
  453. '<Pounds>' . intval($package->pounds) . '</Pounds>' .
  454. '<Ounces>' . ceil($package->ounces) . '</Ounces>' .
  455. '<MailType>Package</MailType>' .
  456. '<Country>'.$shipto_country[0]['country_name'].'</Country>' .
  457. '</Package>';
  458. $services_count++;
  459. }
  460. }
  461. $request .= '</IntlRateRequest>';
  462. $request = 'API=IntlRate&XML=' . urlencode($request);
  463. return $request;
  464. }
  465. /**
  466. * Modify the rate received from USPS before displaying to the customer.
  467. */
  468. function uc_usps_markup($rate) {
  469. $markup = variable_get('uc_usps_markup', '0');
  470. $type = variable_get('uc_usps_markup_type', 'percentage');
  471. if (is_numeric(trim($markup))) {
  472. switch ($type) {
  473. case 'percentage':
  474. return $rate + $rate * floatval(trim($markup)) / 100;
  475. case 'multiplier':
  476. return $rate * floatval(trim($markup));
  477. case 'currency':
  478. return $rate + floatval(trim($markup));
  479. }
  480. }
  481. else {
  482. return $rate;
  483. }
  484. }
  485. function _uc_usps_package_products($products, &$addresses) {
  486. $last_key = 0;
  487. $packages = array();
  488. if (variable_get('uc_usps_all_in_one', true) && count($products) > 1) {
  489. foreach ($products as $product) {
  490. if ($product->nid) {
  491. $address = (array)uc_quote_get_default_shipping_address($product->nid);
  492. $key = array_search($address, $addresses);
  493. if ($key === false) {
  494. $addresses[++$last_key] = $address;
  495. $key = $last_key;
  496. $packages[$key][0] = new stdClass();
  497. }
  498. }
  499. $packages[$key][0]->price += $product->price * $product->qty;
  500. $packages[$key][0]->weight += $product->weight * $product->qty * uc_weight_conversion($product->weight_units, 'lb');
  501. }
  502. foreach ($packages as $key => $package) {
  503. $packages[$key][0]->pounds = floor($package[0]->weight);
  504. $packages[$key][0]->ounces = 16 * ($package[0]->weight - $packages[$key][0]->pounds);
  505. $packages[$key][0]->container = 'RECTANGULAR';
  506. $packages[$key][0]->size = 'REGULAR';
  507. // Packages are "machinable" if heavier than 6oz. and less than 35lbs.
  508. $packages[$key][0]->machinable = (
  509. ($packages[$key][0]->pounds == 0 ? $packages[$key][0]->ounces >= 6 : true) &&
  510. $packages[$key][0]->pounds <= 35 &&
  511. ($packages[$key][0]->pounds == 35 ? $packages[$key][0]->ounces == 0 : true)
  512. );
  513. $packages[$key][0]->qty = 1;
  514. }
  515. }
  516. else {
  517. foreach ($products as $product) {
  518. if ($product->nid) {
  519. $address = (array)uc_quote_get_default_shipping_address($product->nid);
  520. $key = array_search($address, $addresses);
  521. if ($key === false) {
  522. $addresses[++$last_key] = $address;
  523. $key = $last_key;
  524. }
  525. }
  526. if (!$product->pkg_qty) {
  527. $product->pkg_qty = 1;
  528. }
  529. $num_of_pkgs = (int)($product->qty / $product->pkg_qty);
  530. if ($num_of_pkgs) {
  531. $package = drupal_clone($product);
  532. $package->description = $product->model;
  533. $weight = $product->weight * $product->pkg_qty;
  534. switch ($product->weight_units) {
  535. case 'g':
  536. $weight = $weight / 1000;
  537. case 'kg':
  538. $weight = $weight * 2.2;
  539. case 'lb':
  540. $package->pounds = floor($weight);
  541. $package->ounces = 16 * ($weight - $package->pounds);
  542. break;
  543. case 'oz':
  544. $package->pounds = floor($weight / 16);
  545. $package->ounces = $weight - $package->pounds * 16;
  546. break;
  547. }
  548. $package->container = $product->usps['container'];
  549. $length_conversion = uc_length_conversion($product->length_units, 'in');
  550. $package->length = max($product->length, $product->width) * $length_conversion;
  551. $package->width = min($product->length, $product->width) * $length_conversion;
  552. $package->height = $product->height * $length_conversion;
  553. if ($package->length < $package->width) {
  554. list($package->length, $package->width) = array($package->width, $package->length);
  555. }
  556. $package->girth = 2 * $package->width + 2 * $package->height;
  557. $package->size = $package->length + $package->girth;
  558. if ($package->size <= 84) {
  559. $package->size = 'REGULAR';
  560. }
  561. else if ($package->size <= 108) {
  562. $package->size = 'LARGE';
  563. }
  564. else if ($package->size <= 130) {
  565. $package->size = 'OVERSIZE';
  566. }
  567. else {
  568. $package->size = 'GI-HUGE-IC'; // Too big for the U.S. Postal service.
  569. }
  570. $package->machinable = (
  571. $package->length >= 6 && $package->length <= 34 &&
  572. $package->width >= 0.25 && $package->width <= 17 &&
  573. $package->height >= 3.5 && $package->height <= 17 &&
  574. ($package->pounds == 0 ? $package->ounces >= 6 : true) &&
  575. $package->pounds <= 35 &&
  576. ($package->pounds == 35 ? $package->ounces == 0 : true)
  577. );
  578. $package->price = $product->price * $product->pkg_qty;
  579. $package->qty = $num_of_pkgs;
  580. $packages[$key][] = $package;
  581. }
  582. $remaining_qty = $product->qty % $product->pkg_qty;
  583. if ($remaining_qty) {
  584. $package = drupal_clone($product);
  585. $package->description = $product->model;
  586. $weight = $product->weight * $remaining_qty;
  587. switch ($product->weight_units) {
  588. case 'g':
  589. $weight = $weight / 1000;
  590. case 'kg':
  591. $weight = $weight * 2.2;
  592. case 'lb':
  593. $package->pounds = floor($weight);
  594. $package->ounces = 16 * ($weight - $package->pounds);
  595. break;
  596. case 'oz':
  597. $package->pounds = floor($weight / 16);
  598. $package->ounces = $weight - $package->pounds * 16;
  599. break;
  600. }
  601. $package->container = $product->usps['container'];
  602. $package->length = max($product->length, $product->width) * $length_conversion;
  603. $package->width = min($product->length, $product->width) * $length_conversion;
  604. $package->height = $product->height * $length_conversion;
  605. $package->girth = 2 * $package->width + 2 * $package->height;
  606. $package->size = $package->length + $package->girth;
  607. if ($package->size <= 84) {
  608. $package->size = 'REGULAR';
  609. }
  610. else if ($package->size <= 108) {
  611. $package->size = 'LARGE';
  612. }
  613. else if ($package->size <= 130) {
  614. $package->size = 'OVERSIZE';
  615. }
  616. else {
  617. $package->size = 'GI-HUGE-IC'; // Too big for the U.S. Postal service.
  618. }
  619. $package->machinable = (
  620. $package->length >= 6 && $package->length <= 34 &&
  621. $package->width >= 0.25 && $package->width <= 17 &&
  622. $package->height >= 3.5 && $package->height >= 17 &&
  623. ($package->pounds == 0 ? $package->ounces >= 6 : true) &&
  624. $package->pounds <= 35 &&
  625. ($package->pounds == 35 ? $package->ounces == 0 : true)
  626. );
  627. $package->price = $product->price * $remaining_qty;
  628. $package->qty = 1;
  629. $packages[$key][] = $package;
  630. }
  631. }
  632. }
  633. return $packages;
  634. }
  635. /**
  636. * Convenience function for select form elements.
  637. */
  638. function _uc_usps_pkg_types() {
  639. return array(
  640. 'VARIABLE' => t('Variable'),
  641. 'FLAT RATE BOX' => t('Flat rate box'),
  642. 'FLAT RATE ENVELOPE' => t('Flat rate envelope'),
  643. 'RECTANGULAR' => t('Rectangular'),
  644. 'NONRECTANGULAR' => t('Non-rectangular'),
  645. );
  646. }
  647. /**
  648. * Convenience function for select form elements.
  649. */
  650. function _uc_usps_services() {
  651. return array(
  652. 'zero' => t('U.S.P.S. First-Class Mail'),
  653. 'zeroFlat' => t('U.S.P.S. First-Class Flat'),
  654. 'zeroParcel' => t('U.S.P.S. First-Class Parcel'),
  655. 1 => t('U.S.P.S. Priority Mail'),
  656. 2 => t('U.S.P.S. Express Mail PO to PO'),
  657. 3 => t('U.S.P.S. Express Mail PO to Addressee'),
  658. 4 => t('U.S.P.S. Parcel Post'),
  659. 5 => t('U.S.P.S. Bound Printed Matter'),
  660. 6 => t('U.S.P.S. Media Mail'),
  661. 7 => t('U.S.P.S. Library'),
  662. 12 => t('U.S.P.S. First-Class Postcard Stamped'),
  663. 13 => t('U.S.P.S. Express Mail Flat-Rate Envelope'),
  664. 16 => t('U.S.P.S. Priority Mail Flat-Rate Envelope'),
  665. 17 => t('U.S.P.S. Priority Mail Flat-Rate Box'),
  666. );
  667. }
  668. function _uc_usps_intl_services() {
  669. return array(
  670. 1 => t('Express Mail International (EMS)'),
  671. 2 => t('Priority Mail International'),
  672. //3 => t('First Class Mail International'), // Deprecated May 12, 2008
  673. 4 => t('Global Express Guaranteed'),
  674. 6 => t('Global Express Guaranteed Non-Document Rectangular'),
  675. 7 => t('Global Express Guaranteed Non-Document Non-Rectangular'),
  676. 8 => t('Priority Mail International Flat Rate Envelope'),
  677. 9 => t('Priority Mail International Flat Rate Box'),
  678. 10 => t('Express Mail International (EMS) Flat Rate Envelope'),
  679. 13 => t('First Class Mail International Letter'),
  680. 14 => t('First Class Mail International Flat'),
  681. 15 => t('First Class Mail International Parcel'),
  682. );
  683. }