uc_restrict_qty.module 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. <?php
  2. /**
  3. * @file
  4. * Restrict the quantity on specified products so that only specified quantity
  5. * may be purchased at a time.
  6. */
  7. /**
  8. * Implements hook_theme().
  9. */
  10. function uc_restrict_qty_theme() {
  11. return array(
  12. 'restrict_qty_field' => array(
  13. 'render element' => 'form',
  14. ),
  15. );
  16. }
  17. /**
  18. * Implements hook_uc_product_feature().
  19. */
  20. function uc_restrict_qty_uc_product_feature() {
  21. $features[] = array(
  22. 'id' => 'restrict_qty',
  23. 'title' => t('Restrict Qty'),
  24. 'callback' => 'uc_restrict_qty_feature_form',
  25. 'delete' => 'uc_restrict_qty_feature_delete',
  26. 'settings' => 'uc_restrict_qty_settings',
  27. );
  28. return $features;
  29. }
  30. /**
  31. * Adds settings to the product features form for UC Restrict Qty.
  32. */
  33. function uc_restrict_qty_settings($form, &$form_state) {
  34. $form['#description'] = t('This feature is limited in scope to preventing a user from adding different products to the cart. This does not restrict the quantity of products in the cart if updated after being added, so this feature is best used on sites where all products have a restrict quantity feature on them.');
  35. $form['uc_restrict_qty_global'] = array(
  36. '#title' => t('Global limit'),
  37. '#type' => 'textfield',
  38. '#size' => 5,
  39. '#maxlength' => 5,
  40. '#description' => t('The number of different products that can be added to a cart. Set to 0 for unlimited.'),
  41. '#default_value' => variable_get('uc_restrict_qty_global', 0),
  42. );
  43. $form['uc_restrict_qty_global_replace'] = array(
  44. '#title' => t('Replace Contents'),
  45. '#type' => 'checkbox',
  46. '#description' => t('Enabling this feature will cause the users cart to be emptied if they add more items than the Global Limit set above. This is best used when you offer mutually exclusive content (such as subscription services) where purchasing more then one product is not a valid option.'),
  47. '#default_value' => variable_get('uc_restrict_qty_global_replace', FALSE),
  48. );
  49. $form['defaults'] = array(
  50. '#title' => t('Defaults'),
  51. '#type' => 'fieldset',
  52. '#description' => t('These options will take action, only if product has the "Restrict quantity" feature activated.'),
  53. );
  54. $form['defaults']['uc_restrict_qty_default_qty'] = array(
  55. '#title' => t("Default maximum limit for a product"),
  56. '#type' => 'textfield',
  57. '#size' => 5,
  58. '#maxlength' => 5,
  59. '#description' => t('The number of products of single type that can be added to a cart. Set to 0 for unlimited.'),
  60. '#default_value' => variable_get('uc_restrict_qty_default_qty', 0),
  61. );
  62. $form['defaults']['uc_restrict_qty_default_lifetime'] = array(
  63. '#title' => t("Is restriction is the user's lifetime limit"),
  64. '#type' => 'checkbox',
  65. '#description' => t("Useful when you want to prevent double ordering of a product."),
  66. '#default_value' => variable_get('uc_restrict_qty_default_lifetime', FALSE),
  67. );
  68. return $form;
  69. }
  70. /**
  71. * Validates the textfield on the product features settings form.
  72. */
  73. function uc_restrict_qty_settings_validate($form, &$form_state) {
  74. if (!is_numeric($form_state['values']['uc_restrict_qty_global']) || $form_state['values']['uc_restrict_qty_global'] < 0) {
  75. form_set_error('uc_restrict_qty_global', t('You must enter 0 or a positive number value.'));
  76. }
  77. if (!is_numeric($form_state['values']['uc_restrict_qty_default_qty']) || $form_state['values']['uc_restrict_qty_default_qty'] < 0) {
  78. form_set_error('uc_restrict_qty_default_qty', t('You must enter 0 or a positive number value.'));
  79. }
  80. }
  81. /**
  82. * Builds the form to display for adding or editing a the restricted quantity feature.
  83. */
  84. function uc_restrict_qty_feature_form($form, &$form_state, $node, $feature) {
  85. $models = uc_product_get_models($node->nid, FALSE);
  86. if (!empty($feature)) {
  87. $product_qty = db_query("SELECT * FROM {uc_restrict_qty_products} WHERE pfid = :pfid", array(':pfid' => $feature['pfid']))->fetchObject();
  88. $default_qty = $product_qty->qty;
  89. $default_model = $product_qty->model;
  90. $default_lifetime = $product_qty->lifetime;
  91. $form['pfid'] = array(
  92. '#type' => 'value',
  93. '#value' => $feature['pfid'],
  94. );
  95. $form['rqpid'] = array(
  96. '#type' => 'value',
  97. '#value' => $product_qty->rqpid,
  98. );
  99. }
  100. $form['nid'] = array(
  101. '#type' => 'value',
  102. '#value' => $node->nid,
  103. );
  104. $form['model'] = array(
  105. '#type' => 'select',
  106. '#title' => t('SKU'),
  107. '#default_value' => isset($default_model) ? $default_model : 0,
  108. '#description' => t('This is the SKU of the product that will be restricted to this quantity.'),
  109. '#options' => $models,
  110. );
  111. $form['quantity'] = array(
  112. '#title' => t('Quantity limit'),
  113. '#type' => 'textfield',
  114. '#size' => 5,
  115. '#maxlength' => 5,
  116. '#description' => t('The number of times this product can be added to a cart. Set to 0 for unlimited.'),
  117. '#default_value' => isset($default_qty) ? $default_qty : variable_get('uc_restrict_qty_default_qty', 0),
  118. );
  119. $form['lifetime'] = array(
  120. '#title' => t('Is it a lifetime restriction?'),
  121. '#type' => 'checkbox',
  122. '#description' => t("If checked, user's ordering history will be taken into account too. Useful when you want to prevent double ordering of a product."),
  123. '#default_value' => isset($default_lifetime) ? $default_lifetime : variable_get('uc_restrict_qty_default_lifetime', FALSE),
  124. );
  125. return uc_product_feature_form($form, $form_state, $node, $feature);
  126. }
  127. /**
  128. * Validates the textfield on the product node feature settings form.
  129. */
  130. function uc_restrict_qty_feature_form_validate($form, &$form_state) {
  131. // Check for invalid quantity.
  132. if (!is_numeric($form_state['values']['quantity']) || $form_state['values']['quantity'] < 0) {
  133. form_set_error('uc_restrict_qty_product_qty', t('You must enter 0 or a positive integer value.'));
  134. }
  135. // Check if this feature is already set on this SKU.
  136. $product_roles = db_query("SELECT * FROM {uc_restrict_qty_products} WHERE nid = :nid AND model = :model", array(
  137. ':nid' => $form_state['values']['nid'],
  138. ':model' => $form_state['values']['model'],
  139. ))->fetchObject();
  140. if ($product_roles && $form_state['values']['pfid'] == 0) {
  141. form_set_error('uc_roles_model', t('A quantity restriction has already been set up for this SKU'));
  142. }
  143. }
  144. /**
  145. * Form submit handler for the roles feature form.
  146. */
  147. function uc_restrict_qty_feature_form_submit($form, &$form_state) {
  148. $product_qty = array(
  149. 'rqpid' => isset($form_state['values']['rqpid']) ? $form_state['values']['rqpid'] : NULL,
  150. 'pfid' => isset($form_state['values']['pfid']) ? $form_state['values']['pfid'] : NULL,
  151. 'nid' => $form_state['values']['nid'],
  152. 'model' => $form_state['values']['model'],
  153. 'qty' => $form_state['values']['quantity'],
  154. 'lifetime' => $form_state['values']['lifetime'],
  155. );
  156. $description = '<strong>' . t('SKU') . ':</strong>' . (empty($product_qty['model']) ? t('Any') : $product_qty['model']) . '<br/>';
  157. $description .= '<strong>' . t('Quantity restriction') . ':</strong>' . $product_qty['qty'] . '<br/>';
  158. $description .= '<strong>' . t('Type') . ':</strong>' . ($product_qty['lifetime'] ? t('Lifetime') : t('Cart max.')) . '<br/>';
  159. $data = array(
  160. 'nid' => $product_qty['nid'],
  161. 'fid' => 'restrict_qty',
  162. 'description' => $description,
  163. );
  164. if (isset($product_qty['pfid'])) {
  165. $data['pfid'] = $product_qty['pfid'];
  166. }
  167. $form_state['redirect'] = uc_product_feature_save($data);
  168. $key = array();
  169. if ($product_qty['rqpid']) {
  170. $key[] = 'rqpid';
  171. }
  172. // Insert or update uc_file_product table
  173. if (empty($product_qty['pfid'])) {
  174. $product_qty['pfid'] = $data['pfid'];
  175. }
  176. drupal_write_record('uc_restrict_qty_products', $product_qty, $key);
  177. }
  178. /**
  179. * Implements hook_uc_add_to_cart().
  180. */
  181. function uc_restrict_qty_uc_add_to_cart($nid, $qty, $data) {
  182. $limit = variable_get('uc_restrict_qty_global', 0);
  183. $replacecart = variable_get('uc_restrict_qty_global_replace', FALSE);
  184. $result = NULL; // added by Jeff
  185. // If a global restriction on the number of items has been made.
  186. if ($limit > 0) {
  187. if (count(uc_cart_get_contents()) >= $limit) {
  188. if ($replacecart) {
  189. db_query("DELETE FROM {uc_cart_products} WHERE cart_id = :cart", array(':cart' => uc_cart_get_id()));
  190. $result[] = array('success' => TRUE);
  191. }
  192. else {
  193. $message = format_plural($limit, 'Sorry, you may only have 1 item in your cart. You must checkout or remove the item in your cart before adding a different item.',
  194. 'Sorry, you may only have a total of @count items in your cart. You must checkout or remove items from your cart before adding others.');
  195. $result[] = array(
  196. 'success' => FALSE,
  197. 'message' => $message,
  198. );
  199. }
  200. }
  201. }
  202. // Check lifetime product-level limit.
  203. if ($data['restrict_qty']['lifetime']) {
  204. if (!$data['restrict_qty']['rest']) {
  205. $message = t('Sorry, you have reached the quantity limit for this product. You can not order more items of this product.');
  206. $result[] = array(
  207. 'success' => FALSE,
  208. 'message' => $message,
  209. );
  210. }
  211. }
  212. return $result;
  213. }
  214. /**
  215. * Implements hook_add_to_cart_data().
  216. * @param $form_values
  217. * The values submitted to the Add to Cart form.
  218. *
  219. * @return
  220. * An array of data to be merged into the item added to the cart.
  221. */
  222. function uc_restrict_qty_uc_add_to_cart_data($form_values) {
  223. return array('restrict_qty' => uc_restrict_qty_count($form_values));
  224. }
  225. /**
  226. * Implements of hook_uc_cart_item_update().
  227. */
  228. function uc_restrict_qty_uc_cart_item_update($item) {
  229. // Check if this item has a quantity restriction on it.
  230. if (isset($item->data['restrict_qty']['qty']) && $item->data['restrict_qty']['qty'] > 0) {
  231. $is_lifetime = isset($item->data['restrict_qty']['lifetime']) && $item->data['restrict_qty']['lifetime'];
  232. $restrict_qty = $is_lifetime ? $item->data['restrict_qty']['rest'] : $item->data['restrict_qty']['qty'];
  233. if ($item->qty > $restrict_qty) {
  234. // Reduce the quantity to 1 if necessary.
  235. $item->qty = $restrict_qty;
  236. db_update('uc_cart_products')
  237. ->fields(array('qty' => $restrict_qty, 'changed' => time()))
  238. ->condition('cart_id', uc_cart_get_id())
  239. ->condition('nid', $item->nid)
  240. ->condition('data', serialize($item->data))
  241. ->execute();
  242. drupal_set_message(format_plural($restrict_qty, 'You may only add 1 !item to your cart. Quantity has been restricted.', 'You may only add @count !item to your cart. Quantity has been restricted.', array('!item' => $item->title)), 'warning');
  243. }
  244. }
  245. }
  246. /**
  247. * Implements hook_form_alter().
  248. */
  249. function uc_restrict_qty_form_alter(&$form, &$form_state, $form_id) {
  250. // Disable the appropriate Qty. fields on the cart view form.
  251. if ($form_id == 'uc_cart_view_form') {
  252. for ($i = 0, $j = count(uc_cart_get_contents()); $i < $j; $i++) {
  253. $data = unserialize($form['items'][$i]['data']['#value']);
  254. // If this item has a quantity restriction on it.
  255. if (isset($data['restrict_qty'])) {
  256. $is_lifetime = isset($data['restrict_qty']['lifetime']) && $data['restrict_qty']['lifetime'];
  257. $restrict_qty = $is_lifetime ? $data['restrict_qty']['rest'] : $data['restrict_qty']['qty'];
  258. if ($restrict_qty == 1) {
  259. $form['items'][$i]['qty']['#type'] = 'value';
  260. $form['items'][$i]['qty']['#theme'] = 'restrict_qty_field';
  261. }
  262. }
  263. }
  264. }
  265. if ($form_id == 'uc_product_feature_settings_form') {
  266. $form['#validate'][] = 'uc_restrict_qty_settings_validate';
  267. }
  268. }
  269. /**
  270. * Themes cart Qty. boxes so they can't be changed. (currently not in use)
  271. */
  272. function theme_restrict_qty_field($variables) {
  273. return check_plain($variables['form']['#value']);
  274. }
  275. /**
  276. * Returns the number of restrict_qty features on a product node.
  277. */
  278. function uc_restrict_qty_count($form_values) {
  279. global $user;
  280. $data = db_query("SELECT qty, lifetime FROM {uc_restrict_qty_products} WHERE nid = :nid", array(':nid' => $form_values['nid']))->fetchAssoc();
  281. if ($data['lifetime']) {
  282. $bought_qty = db_query('SELECT SUM(uop.qty) FROM {uc_orders} uo LEFT JOIN {uc_order_products} uop ON uo.order_id = uop.order_id WHERE uo.order_status = :completed AND uo.uid = :uid AND uop.nid = :nid ORDER BY uop.nid', array(
  283. ':completed' => 'completed',
  284. ':uid' => $user->uid,
  285. ':nid' => $form_values['nid']
  286. ))->fetchField();
  287. $data['rest'] = $data['qty'] - $bought_qty;
  288. }
  289. return $data;
  290. }
  291. /* ************************************************************************* *
  292. * Actions *
  293. * ************************************************************************* */
  294. /**
  295. * Delete all data associated with a given node.
  296. *
  297. * @param obejct $node
  298. * A Drupal node object.
  299. */
  300. function uc_restrict_qty_node_delete($node) {
  301. db_delete('uc_restrict_qty_products')
  302. ->condition('nid', $node->nid)
  303. ->execute();
  304. }
  305. /**
  306. * Delete all data associated with a given product feature.
  307. *
  308. * @param $pfid
  309. * An Ubercart product feature array.
  310. */
  311. function uc_restrict_qty_feature_delete($feature) {
  312. db_delete('uc_restrict_qty_products')
  313. ->condition('pfid', $feature['pfid'])
  314. ->execute();
  315. }