uc_order.api.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562
  1. <?php
  2. /**
  3. * @file
  4. * Hooks provided by the Order module.
  5. */
  6. /**
  7. * @addtogroup hooks
  8. * @{
  9. */
  10. /**
  11. * Adds invoice templates to the list of suggested template files.
  12. *
  13. * Allows modules to declare new "types" of invoice templates (other than the
  14. * default 'admin' and 'customer').
  15. *
  16. * @return
  17. * Array of template names that are available choices when mailing an invoice.
  18. */
  19. function hook_uc_invoice_templates() {
  20. return array('admin', 'customer');
  21. }
  22. /**
  23. * Defines line items that are attached to orders.
  24. *
  25. * A line item is a representation of charges, fees, and totals for an order.
  26. * Default line items include the subtotal and total line items, the tax line
  27. * item, and the shipping line item. There is also a generic line item that
  28. * store admins can use to add extra fees and discounts to manually created
  29. * orders. Module developers will use this hook to define new types of line
  30. * items for their stores. An example use would be for a module that allows
  31. * customers to use coupons and wants to represent an entered coupon as a line
  32. * item.
  33. *
  34. * Once a line item has been defined in hook_uc_line_item, Ubercart will begin
  35. * interacting with it in various parts of the code. One of the primary ways
  36. * this is done is through the callback function you specify for the line item.
  37. *
  38. * @return
  39. * Your hook should return an array of associative arrays. Each item in the
  40. * array represents a single line item, keyed by the internal ID of the line
  41. * item, and with the following members:
  42. * - "title"
  43. * - type: string
  44. * - value: The title of the line item shown to the user in various
  45. * interfaces. Use t().
  46. * - "callback"
  47. * - type: string
  48. * - value: Name of the line item's callback function, called for various
  49. * operations.
  50. * - "weight"
  51. * - type: integer
  52. * - value: Display order of the line item in lists; "lighter" items are
  53. * displayed first.
  54. * - "stored"
  55. * - type: boolean
  56. * - value: Whether or not the line item will be stored in the database.
  57. * Should be TRUE for any line item that is modifiable from the order
  58. * edit screen.
  59. * - "add_list"
  60. * - type: boolean
  61. * - value: Whether or not a line item should be included in the "Add a Line
  62. * Item" select box on the order edit screen.
  63. * - "calculated"
  64. * - type: boolean
  65. * - value: Whether or not the value of this line item should be added to
  66. * the order total. (Ex: would be TRUE for a shipping charge line item but
  67. * FALSE for the subtotal line item since the product prices are already
  68. * taken into account.)
  69. * - "display_only"
  70. * - type: boolean
  71. * - value: Whether or not this line item is simply a display of information
  72. * but not calculated anywhere. (Ex: the total line item uses display to
  73. * simply show the total of the order at the bottom of the list of line
  74. * items.)
  75. */
  76. function hook_uc_line_item() {
  77. $items[] = array(
  78. 'id' => 'generic',
  79. 'title' => t('Empty line'),
  80. 'weight' => 2,
  81. 'default' => FALSE,
  82. 'stored' => TRUE,
  83. 'add_list' => TRUE,
  84. 'calculated' => TRUE,
  85. 'callback' => 'uc_line_item_generic',
  86. );
  87. return $items;
  88. }
  89. /**
  90. * Alters a line item on an order when the order is loaded.
  91. *
  92. * @param &$item
  93. * The line item array.
  94. * @param $order
  95. * The order object containing the line item.
  96. */
  97. function hook_uc_line_item_alter(&$item, $order) {
  98. $account = user_load($order->uid);
  99. rules_invoke_event('calculate_line_item_discounts', $item, $account);
  100. }
  101. /**
  102. * Alters the line item definitions declared in hook_uc_line_item().
  103. *
  104. * @param &$items
  105. * The combined return value of hook_uc_line_item().
  106. */
  107. function hook_uc_line_item_data_alter(&$items) {
  108. // Tax amounts are added in to other line items, so the actual tax line
  109. // items should not be added to the order total.
  110. $items['tax']['calculated'] = FALSE;
  111. // Taxes are included already, so the subtotal without taxes doesn't
  112. // make sense.
  113. $items['tax_subtotal']['callback'] = NULL;
  114. }
  115. /**
  116. * Performs actions on orders.
  117. *
  118. * An order in Ubercart represents a single transaction. Orders are created
  119. * during the checkout process where they sit in the database with a status of
  120. * "In checkout". When a customer completes checkout, the order's status gets
  121. * updated to show that the sale has gone through. Once an order is created,
  122. * and even during its creation, it may be acted on by any module to connect
  123. * extra information to an order. Every time an action occurs to an order,
  124. * hook_uc_order() gets invoked to let your modules know what's happening and
  125. * make stuff happen.
  126. *
  127. * @param $op
  128. * The action being performed.
  129. * @param $order
  130. * This is the order object.
  131. * @param $arg2
  132. * This is variable and is based on the value of $op:
  133. * - new: Called when an order is created. $order is a reference to the new
  134. * order object, so modules may add to or modify the order at creation.
  135. * - presave: Before an order object is saved, the hook gets invoked with this
  136. * op to let other modules alter order data before it is written to the
  137. * database. $order is a reference to the order object.
  138. * - save: When an order object is being saved, the hook gets invoked with
  139. * this op to let other modules do any necessary saving. $order is a
  140. * reference to the order object.
  141. * - load: Called when an order is loaded after the order and product data has
  142. * been loaded from the database. Passes $order as the reference to the
  143. * order object, so modules may add to or modify the order object when it's
  144. * loaded.
  145. * - submit: When a sale is being completed and the customer has clicked the
  146. * Submit order button from the checkout screen, the hook is invoked with
  147. * this op. This gives modules a chance to determine whether or not the
  148. * order should be allowed. An example use of this is the credit module
  149. * attempting to process payments when an order is submitted and returning
  150. * a failure message if the payment failed.
  151. * To prevent an order from passing through, you must return an array
  152. * resembling the following one with the failure message:
  153. * @code
  154. * return array(array(
  155. * 'pass' => FALSE,
  156. * 'message' => t('We were unable to process your credit card.'),
  157. * ));
  158. * @endcode
  159. * - can_update: Called before an order's status is changed to make sure the
  160. * order can be updated. $order is the order object with the old order
  161. * status ID ($order->order_status), and $arg2 is simply the new order
  162. * status ID. Return FALSE to stop the update for some reason.
  163. * - update: Called when an order's status is changed. $order is the order
  164. * object with the old order status ID ($order->order_status), and $arg2 is
  165. * the new order status ID.
  166. * - can_delete: Called before an order is deleted to verify that the order
  167. * may be deleted. Returning FALSE will prevent a delete from happening.
  168. * (For example, the payment module returns FALSE by default when an order
  169. * has already received payments.)
  170. * - delete: Called when an order is deleted and before the rest of the order
  171. * information is removed from the database. Passes $order as the order
  172. * object to let your module clean up it's tables.
  173. * - total: Called when the total for an order is being calculated after the
  174. * total of the products has been added. Passes $order as the order object.
  175. * Expects in return a value (positive or negative) by which to modify the
  176. * order total.
  177. */
  178. function hook_uc_order($op, $order, $arg2) {
  179. switch ($op) {
  180. case 'save':
  181. // Do something to save payment info!
  182. break;
  183. }
  184. }
  185. /**
  186. * Adds links to local tasks for orders on the admin's list of orders.
  187. *
  188. * @param $order
  189. * An order object.
  190. *
  191. * @return
  192. * An array of specialized link arrays. Each link has the following keys:
  193. * - name: The title of page being linked.
  194. * - url: The link path. Do not use url(), but do use the $order's order_id.
  195. * - icon: HTML of an image.
  196. * - title: Title attribute text (mouseover tool-tip).
  197. */
  198. function hook_uc_order_actions($order) {
  199. $actions = array();
  200. if (user_access('fulfill orders')) {
  201. $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\";%'));
  202. if ($result->fetchField()) {
  203. $title = t('Package order !order_id products.', array('!order_id' => $order->order_id));
  204. $actions[] = array(
  205. 'name' => t('Package'),
  206. 'url' => 'admin/store/orders/' . $order->order_id . '/packages',
  207. 'icon' => theme('image', array('path' => drupal_get_path('module', 'uc_shipping') . '/images/package.gif')),
  208. 'title' => $title,
  209. );
  210. $result = db_query("SELECT COUNT(package_id) FROM {uc_packages} WHERE order_id = :id", array(':id' => $order->order_id));
  211. if ($result->fetchField()) {
  212. $title = t('Ship order !order_id packages.', array('!order_id' => $order->order_id));
  213. $actions[] = array(
  214. 'name' => t('Ship'),
  215. 'url' => 'admin/store/orders/' . $order->order_id . '/shipments',
  216. 'icon' => theme('image', array('path' => drupal_get_path('module', 'uc_shipping') . '/images/ship.gif')),
  217. 'title' => $title,
  218. );
  219. }
  220. }
  221. }
  222. return $actions;
  223. }
  224. /**
  225. * Allows the local task icons for orders to be altered.
  226. *
  227. * @param &$actions
  228. * A set of actions as defined in hook_uc_order_actions().
  229. * @param $order
  230. * An order object.
  231. */
  232. function hook_uc_order_actions_alter(&$actions, $order) {
  233. foreach ($actions as &$action) {
  234. $action['classes'][] = 'custom-action-class';
  235. }
  236. }
  237. /**
  238. * Registers callbacks for an order pane.
  239. *
  240. * This hook is used to add panes to the order viewing and administration
  241. * screens. The default panes include areas to display and edit addresses,
  242. * products, comments, etc. Developers should use this hook when they need to
  243. * display or modify any custom data pertaining to an order. For example, a
  244. * store that uses a custom checkout pane to find out a customer's desired
  245. * delivery date would then create a corresponding order pane to show the data
  246. * on the order screens.
  247. *
  248. * hook_uc_order_pane() works by defining new order panes and providing a little
  249. * bit of information about them. View the return value section below for
  250. * information about what parts of an order pane are defined by the hook.
  251. *
  252. * The real meat of an order pane is its callback function (which is specified
  253. * in the hook). The callback function handles what gets displayed on which
  254. * screen and what data can be manipulated. That is all somewhat out of the
  255. * scope of this API page, so you'll have to click here to read more about what
  256. * a callback function should contain.
  257. *
  258. * @return
  259. * An array of order pane arrays, keyed by the internal ID of the pane, with
  260. * the following members:
  261. * - callback:
  262. * - type: string
  263. * - value: The name of the callback function for this pane.
  264. * - title:
  265. * - type: string
  266. * - value: The name of the pane.
  267. * - (optional) display title:
  268. * - type: string
  269. * - value: The title of the pane as it will be displayed.
  270. * - desc:
  271. * - type: string
  272. * - value: A short description of the pane for the admin pages.
  273. * - class:
  274. * - type: string
  275. * - value: A CSS class that determines the relative position of the pane's
  276. * div. Choose "pos-left" to float left against the previous pane or
  277. * "abs-left" to start a new line of panes.
  278. * - weight:
  279. * - type: integer
  280. * - value: Default weight of the pane, defining its order on the checkout
  281. * form.
  282. * - show:
  283. * - type: array
  284. * - value: The list of op values which will show the pane. "view", "edit",
  285. * "invoice", and "customer" are possible values.
  286. *
  287. * @see uc_order_pane_callback()
  288. * @see http://www.ubercart.org/docs/developer/245/checkout
  289. */
  290. function hook_uc_order_pane() {
  291. $panes['admin_comments'] = array(
  292. 'callback' => 'uc_order_pane_admin_comments',
  293. 'title' => t('Admin comments'),
  294. 'desc' => t('View the admin comments, used for administrative notes and instructions.'),
  295. 'class' => 'abs-left',
  296. 'weight' => 9,
  297. 'show' => array('view', 'edit'),
  298. );
  299. return $panes;
  300. }
  301. /**
  302. * Alter order pane definitions.
  303. *
  304. * @param $panes
  305. * Array with the panes information as defined in hook_uc_order_pane(),
  306. * passed by reference.
  307. */
  308. function hook_uc_order_pane_alter(&$panes) {
  309. $panes['payment']['callback'] = 'my_custom_module_callback';
  310. }
  311. /**
  312. * Builds and processes an order pane defined by hook_uc_order_pane().
  313. *
  314. * @param $op
  315. * The operation the pane is performing. Possible values are "view",
  316. * "customer", "edit-form", "edit-theme" or "edit-process".
  317. * @param $order
  318. * The order being viewed or edited.
  319. * @param $form
  320. * The order's edit form. NULL for non-edit ops.
  321. * @param &$form_state
  322. * The form state array of the edit form. NULL for non-edit ops.
  323. *
  324. * @return
  325. * Varies according to the value of $op:
  326. * - view: A render array showing admin-visible order data.
  327. * - customer: A render array showing customer-visible order data.
  328. * - edit-form: $form with the pane grafted in.
  329. * - edit-theme: The rendered portion of the $form that the pane added.
  330. * - edit-process: An array of values to be modified on the order object,
  331. * keyed by the object's property, or NULL to signify no change on the order
  332. * object.
  333. */
  334. function uc_order_pane_callback($op, $order, &$form = NULL, &$form_state = NULL) {
  335. global $user;
  336. switch ($op) {
  337. case 'view':
  338. $comments = uc_order_comments_load($order->order_id, TRUE);
  339. return tapir_get_table('uc_op_admin_comments_view_table', $comments);
  340. case 'edit-form':
  341. $form['admin_comment_field'] = array(
  342. '#type' => 'fieldset',
  343. '#title' => t('Add an admin comment'),
  344. '#collapsible' => TRUE,
  345. '#collapsed' => TRUE,
  346. );
  347. $form['admin_comment_field']['admin_comment'] = array(
  348. '#type' => 'textarea',
  349. '#description' => t('Admin comments are only seen by store administrators.'),
  350. );
  351. return $form;
  352. case 'edit-theme':
  353. $comments = uc_order_comments_load($form['order_id']['#value'], TRUE);
  354. if (is_array($comments) && count($comments) > 0) {
  355. foreach ($comments as $comment) {
  356. $items[] = '[' . theme('uc_uid', array('uid' => $comment->uid)) . '] ' . filter_xss_admin($comment->message);
  357. }
  358. }
  359. else {
  360. $items = array(t('No admin comments have been entered for this order.'));
  361. }
  362. $output = theme('item_list', array('items' => $items)) . drupal_render($form['admin_comment_field']);
  363. return $output;
  364. case 'edit-process':
  365. if (!empty($order['admin_comment'])) {
  366. uc_order_comment_save($order['order_id'], $user->uid, $order['admin_comment']);
  367. }
  368. return;
  369. }
  370. }
  371. /**
  372. * Allows modules to alter order products when they're loaded with an order.
  373. *
  374. * @param &$product
  375. * The product object as found in the $order object.
  376. * @param $order
  377. * The order object to which the product belongs.
  378. *
  379. * @return
  380. * Nothing should be returned. Hook implementations should receive the
  381. * $product object by reference and alter it directly.
  382. */
  383. function hook_uc_order_product_alter(&$product, $order) {
  384. $product->model = 'SKU';
  385. }
  386. /**
  387. * Acts on order products being loaded from the database.
  388. *
  389. * This hook is invoked during order product loading, which is handled by
  390. * entity_load(), via the EntityCRUDController.
  391. *
  392. * @param array $order_products
  393. * An array of order product entities being loaded, keyed by id.
  394. *
  395. * @see hook_entity_load()
  396. */
  397. function hook_uc_order_product_load(array $order_products) {
  398. $result = db_query('SELECT pid, foo FROM {mytable} WHERE pid IN(:ids)', array(':ids' => array_keys($entities)));
  399. foreach ($result as $record) {
  400. $entities[$record->pid]->foo = $record->foo;
  401. }
  402. }
  403. /**
  404. * Responds when an order product is inserted.
  405. *
  406. * This hook is invoked after the order product is inserted into the database.
  407. *
  408. * @param object $order_product
  409. * The order product that is being inserted.
  410. *
  411. * @see hook_entity_insert()
  412. */
  413. function hook_uc_order_product_insert(object $order_product) {
  414. db_insert('mytable')
  415. ->fields(array(
  416. 'id' => entity_id('uc_order_product', $order_product),
  417. 'extra' => print_r($order_product, TRUE),
  418. ))
  419. ->execute();
  420. }
  421. /**
  422. * Acts on an order product being inserted or updated.
  423. *
  424. * This hook is invoked before the order product is saved to the database.
  425. *
  426. * @param object $order_product
  427. * The order product that is being inserted or updated.
  428. *
  429. * @see hook_entity_presave()
  430. */
  431. function hook_uc_order_product_presave(object $order_product) {
  432. $order_product->name = 'foo';
  433. }
  434. /**
  435. * Responds to an order product being updated.
  436. *
  437. * This hook is invoked after the order product has been updated in the database.
  438. *
  439. * @param object $order_product
  440. * The order product that is being updated.
  441. *
  442. * @see hook_entity_update()
  443. */
  444. function hook_uc_order_product_update(object $order_product) {
  445. db_update('mytable')
  446. ->fields(array('extra' => print_r($order_product, TRUE)))
  447. ->condition('opid', entity_id('uc_order_product', $order_product))
  448. ->execute();
  449. }
  450. /**
  451. * Responds after order product deletion.
  452. *
  453. * This hook is invoked after the order product has been removed from the
  454. * database.
  455. *
  456. * @param object $order_product
  457. * The order product that is being deleted.
  458. *
  459. * @see hook_entity_delete()
  460. * @see hook_uc_order_edit_form_product_remove()
  461. */
  462. function hook_uc_order_product_delete(object $order_product) {
  463. db_delete('mytable')
  464. ->condition('opid', entity_id('uc_order_product', $order_product))
  465. ->execute();
  466. }
  467. /**
  468. * Allow modules to specify whether a product is shippable.
  469. *
  470. * @param $product
  471. * The product to check. May be a cart item or an order product.
  472. * @return
  473. * TRUE to specify that this product is shippable.
  474. */
  475. function hook_uc_order_product_can_ship($product) {
  476. $roles = db_query("SELECT * FROM {uc_roles_products} WHERE nid = :nid", array(':nid' => $item->nid));
  477. foreach ($roles as $role) {
  478. // If the model is empty, keep looking. (Everyone needs a role model...)
  479. if (empty($role->model)) {
  480. continue;
  481. }
  482. // If there's an adjusted SKU, use it... otherwise use the node SKU.
  483. $sku = (empty($item->data['model'])) ? $item->model : $item->data['model'];
  484. // Keep looking if it doesn't match.
  485. if ($sku != $role->model) {
  486. continue;
  487. }
  488. return $role->shippable;
  489. }
  490. }
  491. /**
  492. * Registers static order states.
  493. *
  494. * Order states are module-defined categories for order statuses. Each state
  495. * will have a default status that is used when modules need to move orders to
  496. * new state, but don't know which status to use.
  497. *
  498. * @return
  499. * An array of order state definitions. Each definition is an array keyed by
  500. * the machine name of the state, with the following members:
  501. * - title: The human-readable, translated name.
  502. * - weight: The list position of the state.
  503. * - scope: Either "specific" or "general".
  504. */
  505. function hook_uc_order_state() {
  506. $states['canceled'] = array(
  507. 'title' => t('Canceled'),
  508. 'weight' => -20,
  509. 'scope' => 'specific',
  510. );
  511. $states['in_checkout'] = array(
  512. 'title' => t('In checkout'),
  513. 'weight' => -10,
  514. 'scope' => 'specific',
  515. );
  516. $states['post_checkout'] = array(
  517. 'title' => t('Post checkout'),
  518. 'weight' => 0,
  519. 'scope' => 'general',
  520. );
  521. $states['completed'] = array(
  522. 'title' => t('Completed'),
  523. 'weight' => 20,
  524. 'scope' => 'general',
  525. );
  526. return $states;
  527. }
  528. /**
  529. * @} End of "addtogroup hooks".
  530. */