uc_shipping.module 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878
  1. <?php
  2. /**
  3. * @file
  4. * Organizes ordered products into packages and sets them up for shipment.
  5. * Shipping method modules may add functionality to generate shipping labels
  6. * and tracking numbers.
  7. */
  8. /**
  9. * Implements hook_help().
  10. */
  11. function uc_shipping_help($path, $arg) {
  12. switch ($path) {
  13. case 'admin/store/orders/%/packages/new':
  14. return '<p>' . t('Organize products into packages. Package numbers in multiple shipping types are of the first shipping type they appear in. All packages are given a unique ID when they are saved. Choose the default package "Sep." to automatically create a package for each of the selected quantity of products in that row.') . '</p>';
  15. }
  16. }
  17. /**
  18. * Implements hook_menu().
  19. */
  20. function uc_shipping_menu() {
  21. $items = array();
  22. $items['admin/store/orders/%uc_order/packages'] = array(
  23. 'title' => 'Packages',
  24. 'page callback' => 'uc_shipping_order_packages',
  25. 'page arguments' => array(3),
  26. 'access callback' => 'uc_shipping_order_access',
  27. 'access arguments' => array(3),
  28. 'weight' => 6,
  29. 'type' => MENU_LOCAL_TASK,
  30. 'file' => 'uc_shipping.admin.inc',
  31. );
  32. $items['admin/store/orders/%uc_order/packages/new'] = array(
  33. 'title' => 'Create packages',
  34. 'page callback' => 'drupal_get_form',
  35. 'page arguments' => array('uc_shipping_new_package', 3),
  36. 'access callback' => 'uc_shipping_order_access',
  37. 'access arguments' => array(3),
  38. 'type' => MENU_LOCAL_ACTION,
  39. 'file' => 'uc_shipping.admin.inc',
  40. );
  41. $items['admin/store/orders/%uc_order/packages/%uc_shipping_package/edit'] = array(
  42. 'title' => 'Edit package',
  43. 'page callback' => 'drupal_get_form',
  44. 'page arguments' => array('uc_shipping_package_edit', 3, 5),
  45. 'access callback' => 'uc_shipping_order_access',
  46. 'access arguments' => array(3),
  47. 'file' => 'uc_shipping.admin.inc',
  48. );
  49. $items['admin/store/orders/%uc_order/packages/%uc_shipping_package/cancel'] = array(
  50. 'title' => 'Cancel package shipment',
  51. 'page callback' => 'drupal_get_form',
  52. 'page arguments' => array('uc_shipping_package_cancel_confirm', 3, 5),
  53. 'access callback' => 'uc_shipping_order_access',
  54. 'access arguments' => array(3),
  55. 'file' => 'uc_shipping.admin.inc',
  56. );
  57. $items['admin/store/orders/%uc_order/packages/%uc_shipping_package/delete'] = array(
  58. 'title' => 'Delete package',
  59. 'page callback' => 'drupal_get_form',
  60. 'page arguments' => array('uc_shipping_package_delete_confirm', 3, 5),
  61. 'access callback' => 'uc_shipping_order_access',
  62. 'access arguments' => array(3),
  63. 'file' => 'uc_shipping.admin.inc',
  64. );
  65. $items['admin/store/orders/%uc_order/shipments'] = array(
  66. 'title' => 'Shipments',
  67. 'page callback' => 'uc_shipping_order_shipments',
  68. 'page arguments' => array(3),
  69. 'access callback' => 'uc_shipping_order_access',
  70. 'access arguments' => array(3),
  71. 'weight' => 7,
  72. 'type' => MENU_LOCAL_TASK,
  73. 'file' => 'uc_shipping.admin.inc',
  74. );
  75. $items['admin/store/orders/%uc_order/shipments/new'] = array(
  76. 'title' => 'New shipment',
  77. 'page callback' => 'drupal_get_form',
  78. 'page arguments' => array('uc_shipping_new_shipment', 3),
  79. 'access callback' => 'uc_shipping_new_shipment_access',
  80. 'access arguments' => array(3),
  81. 'type' => MENU_LOCAL_ACTION,
  82. 'file' => 'uc_shipping.admin.inc',
  83. );
  84. $items['admin/store/orders/%uc_order/shipments/%uc_shipping_shipment'] = array(
  85. 'title callback' => 'uc_shipping_shipment_page_title',
  86. 'title arguments' => array(5),
  87. 'page callback' => 'uc_shipping_shipment_view',
  88. 'page arguments' => array(3, 5),
  89. 'access callback' => 'uc_shipping_order_access',
  90. 'access arguments' => array(3),
  91. 'file' => 'uc_shipping.admin.inc',
  92. );
  93. $items['admin/store/orders/%uc_order/shipments/%uc_shipping_shipment/view'] = array(
  94. 'title' => 'View',
  95. 'weight' => -5,
  96. 'type' => MENU_DEFAULT_LOCAL_TASK,
  97. );
  98. $items['admin/store/orders/%uc_order/shipments/%uc_shipping_shipment/edit'] = array(
  99. 'title' => 'Edit',
  100. 'page callback' => 'drupal_get_form',
  101. 'page arguments' => array('uc_shipping_shipment_edit', 3, 5),
  102. 'access callback' => 'uc_shipping_order_access',
  103. 'access arguments' => array(3),
  104. 'weight' => -1,
  105. 'type' => MENU_LOCAL_TASK,
  106. 'file' => 'uc_shipping.admin.inc',
  107. );
  108. $items['admin/store/orders/%uc_order/shipments/%uc_shipping_shipment/print'] = array(
  109. 'title' => 'Print',
  110. 'page callback' => 'uc_shipping_shipment_print',
  111. 'page arguments' => array(3, 5),
  112. 'access callback' => 'uc_shipping_order_access',
  113. 'access arguments' => array(3),
  114. 'type' => MENU_LOCAL_TASK,
  115. 'file' => 'uc_shipping.admin.inc',
  116. );
  117. $items['admin/store/orders/%uc_order/shipments/%uc_shipping_shipment/packing_slip'] = array(
  118. 'title' => 'Packing slip',
  119. 'page callback' => 'uc_shipping_shipment_print',
  120. 'page arguments' => array(3, 5, FALSE),
  121. 'access callback' => 'uc_shipping_order_access',
  122. 'access arguments' => array(3),
  123. 'type' => MENU_LOCAL_TASK,
  124. 'file' => 'uc_shipping.admin.inc',
  125. );
  126. $items['admin/store/orders/%uc_order/shipments/%uc_shipping_shipment/delete'] = array(
  127. 'title' => 'Delete shipment',
  128. 'page callback' => 'drupal_get_form',
  129. 'page arguments' => array('uc_shipping_shipment_delete_confirm', 3, 5),
  130. 'access callback' => 'uc_shipping_order_access',
  131. 'access arguments' => array(3),
  132. 'file' => 'uc_shipping.admin.inc',
  133. );
  134. $items['admin/store/orders/%uc_order/ship'] = array(
  135. 'title' => 'Ship packages',
  136. 'page callback' => 'uc_shipping_make_shipment',
  137. 'page arguments' => array(3),
  138. 'access callback' => 'uc_shipping_order_access',
  139. 'access arguments' => array(3),
  140. 'file' => 'uc_shipping.admin.inc',
  141. );
  142. return $items;
  143. }
  144. /**
  145. * Title callback for admin/store/orders/%/shipments/%.
  146. */
  147. function uc_shipping_shipment_page_title($shipment) {
  148. return t('Shipment !id', array('!id' => $shipment->sid));
  149. }
  150. /**
  151. * Ensures access to the Shipments tab.
  152. */
  153. function uc_shipping_order_access($order) {
  154. return user_access('fulfill orders') && uc_order_is_shippable($order);
  155. }
  156. /**
  157. * Access callback for the new shipment page.
  158. */
  159. function uc_shipping_new_shipment_access($order) {
  160. return uc_shipping_order_access($order) && db_query("SELECT COUNT(*) FROM {uc_packages} WHERE order_id = :id AND sid IS NULL", array(':id' => $order->order_id))->fetchField();
  161. }
  162. /**
  163. * Implements hook_menu_local_tasks_alter().
  164. */
  165. function uc_shipping_menu_local_tasks_alter(&$data, $router_item, $root_path) {
  166. if ($root_path == 'admin/store/orders/%/shipments') {
  167. $order = $router_item['page_arguments'][0];
  168. $item = menu_get_item('admin/store/orders/' . $order->order_id . '/packages/new');
  169. if ($item['access']) {
  170. $data['actions']['output'][] = array(
  171. '#theme' => 'menu_local_action',
  172. '#link' => $item,
  173. );
  174. }
  175. }
  176. }
  177. /**
  178. * Implements hook_admin_paths().
  179. */
  180. function uc_shipping_admin_paths() {
  181. return array(
  182. // Don't show packing slips with the admin theme, overlay, etc.
  183. 'admin/store/orders/*/shipments/*/print' => FALSE,
  184. 'admin/store/orders/*/shipments/*/packing_slip' => FALSE,
  185. );
  186. }
  187. /**
  188. * Implements hook_permission().
  189. */
  190. function uc_shipping_permission() {
  191. return array(
  192. 'fulfill orders' => array(
  193. 'title' => t('Fulfill orders'),
  194. ),
  195. );
  196. }
  197. /**
  198. * Implements hook_theme().
  199. */
  200. function uc_shipping_theme() {
  201. return array(
  202. 'uc_shipping_new_package_fieldset' => array(
  203. 'render element' => 'fieldset',
  204. 'file' => 'uc_shipping.admin.inc',
  205. ),
  206. 'uc_shipping_edit_package_fieldset' => array(
  207. 'render element' => 'fieldset',
  208. 'file' => 'uc_shipping.admin.inc',
  209. ),
  210. 'uc_shipping_new_shipment' => array(
  211. 'render element' => 'form',
  212. 'file' => 'uc_shipping.admin.inc',
  213. ),
  214. 'uc_shipping_shipment_print' => array(
  215. 'variables' => array('order' => NULL, 'shipment' => NULL, 'labels' => TRUE),
  216. 'file' => 'uc_shipping.admin.inc',
  217. ),
  218. 'uc_packing_slip' => array(
  219. 'variables' => array('order' => NULL, 'shipment' => NULL),
  220. 'template' => 'uc-packing-slip',
  221. ),
  222. 'uc_packing_slip_page' => array(
  223. 'variables' => array('content' => NULL),
  224. 'template' => 'uc-packing-slip-page',
  225. ),
  226. );
  227. }
  228. /**
  229. * Preprocess function to make tokens available in the packing slip template.
  230. *
  231. * @see uc-packing-slip.tpl.php
  232. */
  233. function template_preprocess_uc_packing_slip(&$variables) {
  234. $tokens = token_generate('site', drupal_map_assoc(array('logo')));
  235. $variables['site_logo'] = isset($tokens['logo']) ? $tokens['logo'] : '';
  236. $tokens = token_generate('store', drupal_map_assoc(array('name', 'address', 'phone')));
  237. $variables['store_name'] = $tokens['name'];
  238. $variables['store_address'] = $tokens['address'];
  239. $variables['store_phone'] = $tokens['phone'];
  240. $order = $variables['order'];
  241. $variables['order_link'] = l($order->order_id, url('user/' . $order->uid . '/orders/' . $order->order_id, array('absolute' => TRUE)));
  242. $variables['order_email'] = check_plain($order->primary_email);
  243. $variables['billing_address'] = uc_order_address($order, 'billing');
  244. $variables['billing_phone'] = check_plain($order->billing_phone);
  245. $variables['shipping_address'] = uc_order_address($order, 'delivery');
  246. $variables['shipping_phone'] = check_plain($order->delivery_phone);
  247. if (module_exists('uc_payment')) {
  248. $payment_method = _uc_payment_method_data($order->payment_method, 'review');
  249. if (empty($payment_method)) {
  250. $payment_method = _uc_payment_method_data($order->payment_method, 'name');
  251. }
  252. $variables['payment_method'] = $payment_method;
  253. }
  254. else {
  255. $variables['payment_method'] = '';
  256. }
  257. $shipment = $variables['shipment'];
  258. $variables['carrier'] = check_plain($shipment->carrier);
  259. $variables['tracking_number'] = check_plain($shipment->tracking_number);
  260. $variables['packages'] = $shipment->packages;
  261. }
  262. /**
  263. * Preprocesses a printable packing slip page.
  264. *
  265. * @see uc-packing-slip-page.tpl.php
  266. */
  267. function template_preprocess_uc_packing_slip_page(&$variables) {
  268. $language = isset($GLOBALS['language']) ? $GLOBALS['language'] : language_default();
  269. $variables['language'] = $language;
  270. $variables['language']->dir = $language->direction ? 'rtl' : 'ltr';
  271. }
  272. /**
  273. * Implements hook_uc_order_pane().
  274. */
  275. function uc_shipping_uc_order_pane() {
  276. $panes['packages'] = array(
  277. 'callback' => 'uc_shipping_order_pane_packages',
  278. 'title' => t('Tracking numbers'),
  279. 'desc' => t('Display tracking numbers of shipped packages.'),
  280. 'class' => 'pos-left',
  281. 'weight' => 7,
  282. 'show' => array('view', 'invoice', 'customer'),
  283. );
  284. return $panes;
  285. }
  286. /**
  287. * Implements hook_uc_order_actions().
  288. */
  289. function uc_shipping_uc_order_actions($order) {
  290. $actions = array();
  291. if (user_access('fulfill orders')) {
  292. $result = db_query("SELECT COUNT(nid) FROM {uc_order_products} WHERE order_id = :id AND data LIKE :data", array(':id' => $order->order_id, ':data' => '%s:9:\"shippable\";s:1:\"1\";%'));
  293. if ($result->fetchField()) {
  294. $title = t('Package order !order_id products.', array('!order_id' => $order->order_id));
  295. $actions[] = array(
  296. 'name' => t('Package'),
  297. 'url' => 'admin/store/orders/' . $order->order_id . '/packages',
  298. 'icon' => theme('image', array('path' => drupal_get_path('module', 'uc_shipping') . '/images/package.gif', 'alt' => $title)),
  299. 'title' => $title,
  300. );
  301. $result = db_query("SELECT COUNT(package_id) FROM {uc_packages} WHERE order_id = :id", array(':id' => $order->order_id));
  302. if ($result->fetchField()) {
  303. $title = t('Ship order !order_id packages.', array('!order_id' => $order->order_id));
  304. $actions[] = array(
  305. 'name' => t('Ship'),
  306. 'url' => 'admin/store/orders/' . $order->order_id . '/shipments',
  307. 'icon' => theme('image', array('path' => drupal_get_path('module', 'uc_shipping') . '/images/ship.gif', 'alt' => $title)),
  308. 'title' => $title,
  309. );
  310. }
  311. }
  312. }
  313. return $actions;
  314. }
  315. /**
  316. * Displays the details of a package.
  317. */
  318. function uc_shipping_package_view($package) {
  319. $shipment = uc_shipping_shipment_load($package->sid);
  320. $build = array(
  321. '#prefix' => '<div class="order-pane pos-left">',
  322. '#suffix' => '</div>',
  323. );
  324. $rows = array();
  325. $build['title'] = array(
  326. '#markup' => t('Package %id:', array('%id' => $package->package_id)),
  327. '#prefix' => '<div class="order-pane-title">',
  328. '#suffix' => '</div>',
  329. );
  330. $rows[] = array(t('Contents:'), filter_xss_admin($package->description));
  331. if ($shipment) {
  332. $methods = module_invoke_all('uc_shipping_method');
  333. if (isset($methods[$shipment->shipping_method])) {
  334. $pkg_type = $methods[$shipment->shipping_method]['ship']['pkg_types'][$package->pkg_type];
  335. }
  336. }
  337. $rows[] = array(t('Package type:'), isset($pkg_type) ? $pkg_type : check_plain($package->pkg_type));
  338. if ($package->length && $package->width && $package->height) {
  339. $rows[] = array(t('Dimensions:'), t('!l x !w x !h', array('!l' => uc_length_format($package->length), '!w' => uc_length_format($package->width), '!h' => uc_length_format($package->height))));
  340. }
  341. $rows[] = array(t('Insured value:'), array('data' => array('#theme' => 'uc_price', '#price' => $package->value)));
  342. if ($package->tracking_number) {
  343. $rows[] = array(t('Tracking number:'), check_plain($package->tracking_number));
  344. }
  345. if ($shipment && isset($package->label_image) &&
  346. file_exists($package->label_image->uri)) {
  347. $rows[] = array(t('Label:'), l(t('Click to view.'), 'admin/store/orders/' . $package->order_id . '/shipments/labels/' . $shipment->shipping_method . '/' . $package->label_image->uri));
  348. }
  349. else {
  350. $rows[] = array(t('Label:'), t('n/a'));
  351. }
  352. $build['package'] = array(
  353. '#theme' => 'table',
  354. '#rows' => $rows,
  355. 'attributes' => array('style' => 'width:auto;'),
  356. );
  357. return $build;
  358. }
  359. /**
  360. * Loads a package and its products.
  361. */
  362. function uc_shipping_package_load($package_id) {
  363. static $packages = array();
  364. if (!isset($packages[$package_id])) {
  365. $result = db_query("SELECT * FROM {uc_packages} WHERE package_id = :id", array(':id' => $package_id));
  366. if ($package = $result->fetchObject()) {
  367. $products = array();
  368. $description = '';
  369. $weight = 0;
  370. $units = variable_get('uc_weight_unit', 'lb');
  371. $addresses = array();
  372. $result = db_query("SELECT op.order_product_id, pp.qty, pp.qty * op.weight AS weight, op.weight_units, op.nid, op.title, op.model, op.price, op.data FROM {uc_packaged_products} pp LEFT JOIN {uc_order_products} op ON op.order_product_id = pp.order_product_id WHERE pp.package_id = :id ORDER BY op.order_product_id", array(':id' => $package_id));
  373. foreach ($result as $product) {
  374. $address = uc_quote_get_default_shipping_address($product->nid);
  375. // TODO: Lodge complaint that array_unique() compares as strings.
  376. if (!in_array($address, $addresses)) {
  377. $addresses[] = $address;
  378. }
  379. $description .= ', ' . $product->qty . ' x ' . $product->model;
  380. // Normalize all weights to default units.
  381. $weight += $product->weight * uc_weight_conversion($product->weight_units, $units);
  382. $product->data = unserialize($product->data);
  383. $products[$product->order_product_id] = $product;
  384. }
  385. $package->addresses = $addresses;
  386. $package->description = substr($description, 2);
  387. $package->weight = $weight;
  388. $package->weight_units = $units;
  389. $package->products = $products;
  390. if ($package->label_image && $image = file_load($package->label_image)) {
  391. $package->label_image = $image;
  392. }
  393. else {
  394. unset($package->label_image);
  395. }
  396. $packages[$package_id] = $package;
  397. }
  398. else {
  399. return FALSE;
  400. }
  401. }
  402. return $packages[$package_id];
  403. }
  404. /**
  405. * Saves a package.
  406. */
  407. function uc_shipping_package_save($package) {
  408. $package = (object) $package;
  409. if (!isset($package->package_id)) {
  410. $package->package_id = db_insert('uc_packages')
  411. ->fields(array('order_id' => $package->order_id))
  412. ->execute();
  413. }
  414. if (isset($package->products) && $package->products) {
  415. $insert = db_insert('uc_packaged_products')
  416. ->fields(array('package_id', 'order_product_id', 'qty'));
  417. foreach ($package->products as $id => $product) {
  418. $insert->values(array(
  419. 'package_id' => $package->package_id,
  420. 'order_product_id' => $id,
  421. 'qty' => $product->qty,
  422. ));
  423. $result = db_query("SELECT data FROM {uc_order_products} WHERE order_product_id = :id", array(':id' => $id));
  424. if ($order_product = $result->fetchObject()) {
  425. $order_product->data = unserialize($order_product->data);
  426. $order_product->data['package_id'] = intval($package->package_id);
  427. db_update('uc_order_products')
  428. ->fields(array('data' => serialize($order_product->data)))
  429. ->condition('order_product_id', $id)
  430. ->execute();
  431. }
  432. }
  433. db_delete('uc_packaged_products')
  434. ->condition('package_id', $package->package_id)
  435. ->execute();
  436. $insert->execute();
  437. }
  438. $fields = array(
  439. 'order_id' => $package->order_id,
  440. 'shipping_type' => $package->shipping_type,
  441. );
  442. if (isset($package->pkg_type)) {
  443. $fields['pkg_type'] = $package->pkg_type;
  444. }
  445. if (isset($package->length) && isset($package->width) && isset($package->height) && isset($package->length_units)) {
  446. $fields['length'] = $package->length;
  447. $fields['width'] = $package->width;
  448. $fields['height'] = $package->height;
  449. $fields['length_units'] = $package->length_units;
  450. }
  451. if (isset($package->value)) {
  452. $fields['value'] = $package->value;
  453. }
  454. if (isset($package->sid)) {
  455. $fields['sid'] = $package->sid;
  456. }
  457. if (isset($package->tracking_number)) {
  458. $fields['tracking_number'] = $package->tracking_number;
  459. }
  460. if (isset($package->label_image) && is_object($package->label_image)) {
  461. $fields['label_image'] = $package->label_image->fid;
  462. }
  463. db_update('uc_packages')
  464. ->fields($fields)
  465. ->condition('package_id', $package->package_id)
  466. ->execute();
  467. }
  468. /**
  469. * Deletes a package.
  470. */
  471. function uc_shipping_package_delete($package_id) {
  472. // @todo: Make these delete functions take the actual object.
  473. $package = uc_shipping_package_load($package_id);
  474. db_delete('uc_packages')
  475. ->condition('package_id', $package_id)
  476. ->execute();
  477. db_delete('uc_packaged_products')
  478. ->condition('package_id', $package_id)
  479. ->execute();
  480. if (isset($package->label_image)) {
  481. file_usage_delete($package->label_image, 'uc_shipping', 'package', $package_id);
  482. file_delete($package->label_image);
  483. }
  484. drupal_set_message(t('Package @id has been deleted.', array('@id' => $package_id)));
  485. }
  486. /**
  487. * Loads a shipment and its packages.
  488. */
  489. function uc_shipping_shipment_load($shipment_id) {
  490. $shipment = db_query("SELECT * FROM {uc_shipments} WHERE sid = :sid", array(':sid' => $shipment_id))->fetchObject();
  491. if ($shipment) {
  492. $result = db_query("SELECT package_id FROM {uc_packages} WHERE sid = :sid", array(':sid' => $shipment_id));
  493. $packages = array();
  494. foreach ($result as $package) {
  495. $packages[$package->package_id] = uc_shipping_package_load($package->package_id);
  496. }
  497. $shipment->packages = $packages;
  498. $extra = module_invoke_all('uc_shipment', 'load', $shipment);
  499. if (is_array($extra)) {
  500. foreach ($extra as $key => $value) {
  501. $shipment->$key = $value;
  502. }
  503. }
  504. }
  505. return $shipment;
  506. }
  507. /**
  508. * Saves a shipment.
  509. */
  510. function uc_shipping_shipment_save($shipment) {
  511. if (isset($shipment->origin)) {
  512. foreach ($shipment->origin as $field => $value) {
  513. $field = 'o_' . $field;
  514. $shipment->$field = $value;
  515. $fields[$field] = $value;
  516. }
  517. }
  518. if (isset($shipment->destination)) {
  519. foreach ($shipment->destination as $field => $value) {
  520. $field = 'd_' . $field;
  521. $shipment->$field = $value;
  522. $fields[$field] = $value;
  523. }
  524. }
  525. $shipment->changed = time();
  526. if (!isset($shipment->sid)) {
  527. drupal_write_record('uc_shipments', $shipment);
  528. $shipment->is_new = TRUE;
  529. }
  530. else {
  531. drupal_write_record('uc_shipments', $shipment, 'sid');
  532. $shipment->is_new = FALSE;
  533. }
  534. if (is_array($shipment->packages)) {
  535. foreach ($shipment->packages as $package) {
  536. $package->sid = $shipment->sid;
  537. // Since the products haven't changed, we take them out of the object so
  538. // that they are not deleted and re-inserted.
  539. $products = $package->products;
  540. unset($package->products);
  541. uc_shipping_package_save($package);
  542. // But they're still necessary for hook_uc_shipment(), so they're added
  543. // back in.
  544. $package->products = $products;
  545. }
  546. }
  547. module_invoke_all('uc_shipment', 'save', $shipment);
  548. $order = uc_order_load($shipment->order_id);
  549. rules_invoke_event('uc_shipment_save', $order, $shipment);
  550. }
  551. /**
  552. * Deletes a shipment.
  553. */
  554. function uc_shipping_shipment_delete($shipment_id) {
  555. $shipment = uc_shipping_shipment_load($shipment_id);
  556. db_update('uc_packages')
  557. ->fields(array(
  558. 'sid' => NULL,
  559. 'tracking_number' => NULL,
  560. 'label_image' => NULL,
  561. ))
  562. ->condition('sid', $shipment_id)
  563. ->execute();
  564. db_delete('uc_shipments')
  565. ->condition('sid', $shipment_id)
  566. ->execute();
  567. foreach ($shipment->packages as $package) {
  568. if (isset($package->label_image)) {
  569. file_delete($package->label_image);
  570. unset($package->label_image);
  571. }
  572. }
  573. module_invoke_all('uc_shipment', 'delete', $shipment);
  574. }
  575. /**
  576. * Shipping order pane callback.
  577. *
  578. * @see uc_shipping_uc_order_pane()
  579. */
  580. function uc_shipping_order_pane_packages($op, $order) {
  581. switch ($op) {
  582. case 'view':
  583. case 'customer':
  584. $tracking = array();
  585. $result = db_query("SELECT sid FROM {uc_shipments} WHERE order_id = :id", array(':id' => $order->order_id));
  586. foreach ($result as $shipment) {
  587. $shipment = uc_shipping_shipment_load($shipment->sid);
  588. if ($shipment->tracking_number) {
  589. $tracking[$shipment->carrier]['data'] = $shipment->carrier;
  590. $tracking[$shipment->carrier]['children'][] = check_plain($shipment->tracking_number);
  591. }
  592. else {
  593. foreach ($shipment->packages as $package) {
  594. if ($package->tracking_number) {
  595. $tracking[$shipment->carrier]['data'] = $shipment->carrier;
  596. $tracking[$shipment->carrier]['children'][] = check_plain($package->tracking_number);
  597. }
  598. }
  599. }
  600. }
  601. // Do not show an empty pane to customers.
  602. if ($op == 'view' || !empty($tracking)) {
  603. $build['tracking'] = array(
  604. '#theme' => 'item_list',
  605. '#items' => $tracking,
  606. );
  607. return $build;
  608. }
  609. break;
  610. }
  611. }
  612. /**
  613. * Chooses an address to fill out a form.
  614. */
  615. function uc_shipping_select_address($addresses, $onchange = '', $title = NULL) {
  616. if (!is_array($addresses) || count($addresses) == 0) {
  617. $addresses = array();
  618. }
  619. $store_address = variable_get('uc_quote_store_default_address', new UcAddress());
  620. if (!in_array($store_address, $addresses)) {
  621. $addresses[] = $store_address;
  622. }
  623. $blank = array(
  624. 'first_name' => '',
  625. 'last_name' => '',
  626. 'phone' => '',
  627. 'company' => '',
  628. 'street1' => '',
  629. 'street2' => '',
  630. 'city' => '',
  631. 'postal_code' => '',
  632. 'country' => 0,
  633. 'zone' => 0,
  634. );
  635. $options = array(drupal_json_encode($blank) => t('- Reset fields -'));
  636. foreach ($addresses as $address) {
  637. $options[drupal_json_encode($address)] = $address->company . ' ' . $address->street1 . ' ' . $address->city;
  638. }
  639. $select = array(
  640. '#type' => 'select',
  641. '#title' => is_null($title) ? t('Address book') : $title,
  642. '#options' => $options,
  643. '#default_value' => drupal_json_encode($addresses[0]),
  644. '#attributes' => array('onchange' => $onchange),
  645. );
  646. return $select;
  647. }
  648. /**
  649. * Helper function for addresses in forms.
  650. *
  651. * @ingroup forms
  652. */
  653. function uc_shipping_address_form($form, &$form_state, $addresses, $order) {
  654. drupal_add_js(drupal_get_path('module', 'uc_shipping') . '/uc_shipping.js');
  655. $form['origin'] = array(
  656. '#type' => 'fieldset',
  657. '#title' => t('Origin address'),
  658. '#collapsible' => TRUE,
  659. '#collapsed' => FALSE,
  660. '#weight' => -2,
  661. );
  662. $form['origin']['pickup_address_select'] = uc_shipping_select_address($addresses, 'apply_address(\'pickup\', this.value);', t('Saved Addresses'));
  663. $form['origin']['pickup_address_select']['#weight'] = -2;
  664. $form['origin']['pickup_email'] = uc_textfield(t('E-mail'), uc_store_email(), FALSE, NULL, 255);
  665. $form['origin']['pickup_email']['#weight'] = -1;
  666. $form['origin']['pickup_address']['#tree'] = TRUE;
  667. $form['origin']['pickup_address']['pickup_address'] = array(
  668. '#type' => 'uc_address',
  669. '#default_value' => reset($addresses),
  670. '#required' => FALSE,
  671. );
  672. $form['destination'] = array(
  673. '#type' => 'fieldset',
  674. '#title' => t('Destination address'),
  675. '#collapsible' => TRUE,
  676. '#collapsed' => FALSE,
  677. '#weight' => -1,
  678. );
  679. if (isset($form_state['values']['delivery_country'])) {
  680. $order->delivery_country = $form_state['values']['delivery_country'];
  681. }
  682. $form['destination']['delivery_email'] = uc_textfield(t('E-mail'), $order->primary_email, FALSE, NULL, 255);
  683. $form['destination']['delivery_email']['#weight'] = -1;
  684. $form['destination']['delivery_address'] = array(
  685. '#type' => 'uc_address',
  686. '#default_value' => $order,
  687. '#required' => FALSE,
  688. '#key_prefix' => 'delivery',
  689. );
  690. return $form;
  691. }
  692. /**
  693. * Implements hook_views_api().
  694. */
  695. function uc_shipping_views_api() {
  696. return array(
  697. 'api' => '2.0',
  698. 'path' => drupal_get_path('module', 'uc_shipping') . '/views',
  699. );
  700. }
  701. /**
  702. * Implements hook_date_views_tables().
  703. */
  704. function uc_shipping_date_views_tables() {
  705. return array('uc_shipments');
  706. }
  707. /**
  708. * Implements hook_date_views_fields().
  709. *
  710. * All modules that create custom fields that use the
  711. * 'views_handler_field_date' handler can provide
  712. * additional information here about the type of
  713. * date they create so the date can be used by
  714. * the Date API views date argument and date filter.
  715. */
  716. function uc_shipping_date_views_fields($field) {
  717. $values = array(
  718. // The type of date: DATE_UNIX, DATE_ISO, DATE_DATETIME.
  719. 'sql_type' => DATE_UNIX,
  720. // Timezone handling options: 'none', 'site', 'date', 'utc' .
  721. 'tz_handling' => 'site',
  722. // Needed only for dates that use 'date' tz_handling.
  723. 'timezone_field' => '',
  724. // Needed only for dates that use 'date' tz_handling.
  725. 'offset_field' => '',
  726. // Array of "table.field" values for related fields that should be
  727. // loaded automatically in the Views SQL.
  728. 'related_fields' => array(),
  729. // Granularity of this date field's db data.
  730. 'granularity' => array('year', 'month', 'day', 'hour', 'minute', 'second'),
  731. );
  732. switch ($field) {
  733. case 'uc_shipments.ship_date':
  734. case 'uc_shipments.expected_delivery':
  735. case 'uc_shipments.changed':
  736. return $values;
  737. }
  738. }
  739. /**
  740. * Implements hook_uc_order().
  741. *
  742. * Prevent users from deleting orders with a shipment or package that has
  743. * a tracking number, unless the user has administrative privileges or the
  744. * "Unconditionally delete orders" permission.
  745. *
  746. * Delete packages and shipments attached to orders being deleted.
  747. */
  748. function uc_shipping_uc_order($op, $order, $arg2) {
  749. switch ($op) {
  750. case 'can_delete':
  751. // Find and check the shipments for tracking numbers.
  752. // {uc_shipments}.tracking_number is NOT NULL.
  753. $shipment_count = db_select('uc_shipments')
  754. ->condition('order_id', $order->order_id)
  755. ->condition('tracking_number', '', '<>')
  756. ->countQuery()
  757. ->execute()
  758. ->fetchField();
  759. if ($shipment_count > 0) {
  760. return FALSE;
  761. }
  762. // Find and check the packages.
  763. $package_count = db_select('uc_packages')
  764. ->condition('order_id', $order->order_id)
  765. ->isNotNull('tracking_number')
  766. ->condition('tracking_number', '', '<>')
  767. ->countQuery()
  768. ->execute()
  769. ->fetchField();
  770. if ($package_count > 0) {
  771. return FALSE;
  772. }
  773. return TRUE;
  774. case 'delete':
  775. // Find and delete the shipments.
  776. $shipment_ids = db_select('uc_shipments')
  777. ->fields(NULL, array('sid'))
  778. ->condition('order_id', $order->order_id)
  779. ->execute()
  780. ->fetchCol();
  781. foreach ($shipment_ids as $sid) {
  782. uc_shipping_shipment_delete($sid);
  783. }
  784. // Find and delete the packages.
  785. $package_ids = db_select('uc_packages')
  786. ->fields(NULL, array('package_id'))
  787. ->condition('order_id', $order->order_id)
  788. ->execute()
  789. ->fetchCol();
  790. foreach ($package_ids as $pid) {
  791. uc_shipping_package_delete($pid);
  792. }
  793. break;
  794. }
  795. }