uc_restrict_qty.module 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. <?php
  2. /**
  3. * @file
  4. * Restrict the quantity on specified products so that only specified quantity may be purchased
  5. * at a time.
  6. */
  7. /**
  8. * Implementation of 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. * Implementation of 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. // Adds settings to the product features form for UC Restrict Qty.
  31. function uc_restrict_qty_settings() {
  32. $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.');
  33. $form['uc_restrict_qty_global'] = array(
  34. '#title' => t('Global limit'),
  35. '#type' => 'textfield',
  36. '#size' => 5,
  37. '#maxlength' => 5,
  38. '#description' => t('The number of different products that can be added to a cart. Set to 0 for unlimited.'),
  39. '#default_value' => variable_get('uc_restrict_qty_global', 0),
  40. );
  41. $form['uc_restrict_qty_global_replace'] = array(
  42. '#title' => t('Replace Contents'),
  43. '#type' => 'checkbox',
  44. '#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.'),
  45. '#default_value' => variable_get('uc_restrict_qty_global_replace', FALSE),
  46. );
  47. $form['defaults'] = array(
  48. '#title' => t('Defaults'),
  49. '#type' => 'fieldset',
  50. '#description' => t('These options will take action, only if product has the "Restrict quantity" feature activated.'),
  51. );
  52. $form['defaults']['uc_restrict_qty_default_qty'] = array(
  53. '#title' => t("Default maximum limit for a product"),
  54. '#type' => 'textfield',
  55. '#size' => 5,
  56. '#maxlength' => 5,
  57. '#description' => t('The number of products of single type that can be added to a cart. Set to 0 for unlimited.'),
  58. '#default_value' => variable_get('uc_restrict_qty_default_qty', 0),
  59. );
  60. $form['defaults']['uc_restrict_qty_default_lifetime'] = array(
  61. '#title' => t("Is restriction is the user's lifetime limit"),
  62. '#type' => 'checkbox',
  63. '#description' => t("Useful when you want to prevent double ordering of a product."),
  64. '#default_value' => variable_get('uc_restrict_qty_default_lifetime', FALSE),
  65. );
  66. return $form;
  67. }
  68. // Validates the textfield on the product features settings form.
  69. function uc_restrict_qty_settings_validate($form, &$form_state) {
  70. if (!is_numeric($form_state['values']['uc_restrict_qty_global']) || $form_state['values']['uc_restrict_qty_global'] < 0) {
  71. form_set_error('uc_restrict_qty_global', t('You must enter 0 or a positive number value.'));
  72. }
  73. if (!is_numeric($form_state['values']['uc_restrict_qty_default_qty']) || $form_state['values']['uc_restrict_qty_default_qty'] < 0) {
  74. form_set_error('uc_restrict_qty_default_qty', t('You must enter 0 or a positive number value.'));
  75. }
  76. }
  77. // Builds the form to display for adding or editing a the restricted quantity feature.
  78. function uc_restrict_qty_feature_form($form, &$form_state, $node, $feature) {
  79. $models = uc_product_get_models($node->nid);
  80. if (!empty($feature)) {
  81. $product_qty = db_query("SELECT * FROM {uc_restrict_qty_products} WHERE pfid = :pfid", array(':pfid' => $feature['pfid']))->fetchObject();
  82. $default_qty = $product_qty->qty;
  83. $default_model = $product_qty->model;
  84. $default_lifetime = $product_qty->lifetime;
  85. $form['pfid'] = array(
  86. '#type' => 'value',
  87. '#value' => $feature['pfid'],
  88. );
  89. $form['rqpid'] = array(
  90. '#type' => 'value',
  91. '#value' => $product_qty->rqpid,
  92. );
  93. }
  94. /* if ($result = db_result(db_query("SELECT COUNT(*) FROM {uc_product_features} WHERE nid = %d AND fid = '%s'", $node->nid, 'restrict_qty'))) {
  95. drupal_set_message(t('Adding more than one Restrict Qty. does nothing for now.', 'error'));
  96. $form['no_roles'] = array(
  97. '#value' => t('You need to <a href="!url">create new roles</a> before any can be added as product features.', array('!url' => url('admin/user/roles', array('query' => 'destination=admin/store/settings/products/edit/features')))),
  98. '#prefix' => '<p>',
  99. '#suffix' => '</p>',
  100. );
  101. return $form;
  102. }*/
  103. $form['nid'] = array(
  104. '#type' => 'value',
  105. '#value' => $node->nid,
  106. );
  107. $form['model'] = array(
  108. '#type' => 'select',
  109. '#title' => t('SKU'),
  110. '#default_value' => isset($default_model) ? $default_model : 0,
  111. '#description' => t('This is the SKU of the product that will be restricted to this quantity.'),
  112. '#options' => $models,
  113. );
  114. $form['quantity'] = array(
  115. '#title' => t('Quantity limit'),
  116. '#type' => 'textfield',
  117. '#size' => 5,
  118. '#maxlength' => 5,
  119. '#description' => t('The number of times this product can be added to a cart. Set to 0 for unlimited.'),
  120. '#default_value' => isset($default_qty) ? $default_qty : variable_get('uc_restrict_qty_default_qty', 0),
  121. );
  122. $form['lifetime'] = array(
  123. '#title' => t('Is it a lifetime restriction?'),
  124. '#type' => 'checkbox',
  125. '#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."),
  126. '#default_value' => isset($default_lifetime) ? $default_lifetime : variable_get('uc_restrict_qty_default_lifetime', FALSE),
  127. );
  128. return uc_product_feature_form($form, $form_state, $node, $feature);
  129. }
  130. // Validates the textfield on the product features settings form.
  131. function uc_restrict_qty_feature_form_validate($form, &$form_state) {
  132. // Invalid quantity?
  133. if (!is_numeric($form_state['values']['quantity']) || $form_state['values']['quantity'] < 0) {
  134. form_set_error('uc_restrict_qty_product_qty', t('You must enter 0 or a positive integer value.'));
  135. }
  136. // This feature is already set on this SKU?
  137. $product_roles = db_query("SELECT * FROM {uc_restrict_qty_products} WHERE nid = :nid AND model = :model", array(
  138. ':nid' => $form_state['values']['nid'],
  139. ':model' => $form_state['values']['model'],
  140. ))->fetchObject();
  141. if ($product_roles && $form_state['values']['pfid'] == 0) {
  142. form_set_error('uc_roles_model', t('A quantity restriction has already been set up for this SKU'));
  143. }
  144. }
  145. /**
  146. * Form submit handler for the roles feature form.
  147. */
  148. function uc_restrict_qty_feature_form_submit($form, &$form_state) {dpm($form_state);
  149. $product_qty = array(
  150. 'rqpid' => isset($form_state['values']['rqpid']) ? $form_state['values']['rqpid'] : NULL,
  151. 'pfid' => isset($form_state['values']['pfid']) ? $form_state['values']['pfid'] : NULL,
  152. 'nid' => $form_state['values']['nid'],
  153. 'model' => $form_state['values']['model'],
  154. 'qty' => $form_state['values']['quantity'],
  155. 'lifetime' => $form_state['values']['lifetime'],
  156. );
  157. $description = '<strong>'. t('SKU') .':</strong> '. (empty($product_qty['model']) ? t('Any') : $product_qty['model']) .'<br/>';
  158. $description .= '<strong>'. t('Quantity restriction') .':</strong> '. $product_qty['qty'] .'<br/>';
  159. $description .= '<strong>'. t('Type') .':</strong> '. ($product_qty['lifetime'] ? t('Lifetime') : t('Cart max.')) .'<br/>';
  160. $data = array(
  161. 'nid' => $product_qty['nid'],
  162. 'fid' => 'restrict_qty',
  163. 'description' => $description,
  164. );
  165. if (isset($product_qty['pfid'])) {
  166. $data['pfid'] = $product_qty['pfid'];
  167. }
  168. $form_state['redirect'] = uc_product_feature_save($data);
  169. $key = array();
  170. if ($product_qty['rqpid']) {
  171. $key[] = 'rqpid';
  172. }
  173. // Insert or update uc_file_product table
  174. if (empty($product_qty['pfid'])) {
  175. $product_qty['pfid'] = $data['pfid'];
  176. }
  177. drupal_write_record('uc_restrict_qty_products', $product_qty, $key);
  178. }
  179. /**
  180. * Implementation of hook_add_to_cart().
  181. */
  182. function uc_restrict_qty_add_to_cart($nid, $qty, $data) {
  183. $limit = variable_get('uc_restrict_qty_global', 0);
  184. $replaceCart = variable_get('uc_restrict_qty_global_replace', FALSE);
  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 = '%s'", 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. * Implementation of hook_add_to_cart_data().
  216. */
  217. function uc_restrict_qty_uc_add_to_cart_data($form_values) {
  218. return array('restrict_qty' => uc_restrict_qty_count($form_values));
  219. }
  220. /**
  221. * Implementation of hook_cart_item().
  222. */
  223. function uc_restrict_qty_uc_cart_item($op, &$item) {
  224. if ($op == 'load') {
  225. // If this item has a quantity restriction on it...
  226. if ($item->data['restrict_qty']['qty'] > 0) {
  227. $is_lifetime = isset($item->data['restrict_qty']['lifetime']) && $item->data['restrict_qty']['lifetime'];
  228. $restrict_qty = $is_lifetime ? $item->data['restrict_qty']['rest'] : $item->data['restrict_qty']['qty'];
  229. if ($item->qty > $restrict_qty) {
  230. // Reduce the quantity to 1 if necessary.
  231. $item->qty = $restrict_qty;
  232. db_query("UPDATE {uc_cart_products} SET qty = %d, changed = %d WHERE cart_id = '%s' AND nid = %d AND data = '%s'", $restrict_qty, time(), uc_cart_get_id(), $item->nid, serialize($item->data));
  233. 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');
  234. }
  235. }
  236. }
  237. }
  238. /**
  239. * Implementation of hook_form_alter().
  240. */
  241. function uc_restrict_qty_form_alter(&$form, &$form_state, $form_id) {
  242. // Disable the appropriate Qty. fields on the cart view form.
  243. if ($form_id == 'uc_cart_view_form') {
  244. for ($i = 0, $j = count(uc_cart_get_contents()); $i < $j; $i++) {
  245. $data = unserialize($form['items'][$i]['data']['#value']);
  246. // If this item has a quantity restriction on it...
  247. $is_lifetime = isset($data['restrict_qty']['lifetime']) && $data['restrict_qty']['lifetime'];
  248. $restrict_qty = $is_lifetime ? $data['restrict_qty']['rest'] : $data['restrict_qty']['qty'];
  249. if ($restrict_qty == 1) {
  250. $form['items'][$i]['qty']['#type'] = 'value';
  251. $form['items'][$i]['qty']['#theme'] = 'restrict_qty_field';
  252. }
  253. }
  254. }
  255. if ($form_id == 'uc_product_feature_settings_form') {
  256. $form['#validate'][] = 'uc_restrict_qty_settings_validate';
  257. }
  258. }
  259. // Themes cart Qty. boxes so they can't be changed. (currently not in use)
  260. function theme_restrict_qty_field($variables) {
  261. return check_plain($variables['form']['#value']);
  262. }
  263. // Returns the number of restrict_qty features on a product node.
  264. function uc_restrict_qty_count($form_values) {
  265. global $user;
  266. $data = db_query("SELECT qty, lifetime FROM {uc_restrict_qty_products} WHERE nid = :nid", array(':nid' => $form_values['nid']))->fetchAssoc();
  267. if ($data['lifetime']) {
  268. $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(':uid' => $user->uid , ':nid' => $form_values['nid']))->fetchField();
  269. $data['rest'] = $data['qty'] - $bought_qty;
  270. }
  271. return $data;
  272. }
  273. /* ************************************************************************* *
  274. * Actions *
  275. * ************************************************************************* */
  276. /**
  277. * Delete all data associated with a given node.
  278. *
  279. * @param $nid
  280. * A Drupal node ID.
  281. */
  282. function uc_restrict_qty_node_delete($nid) {
  283. db_query("DELETE FROM {uc_restrict_qty_products} WHERE nid = :nid", array(':nid' => $node->nid));
  284. }
  285. /**
  286. * Delete all data associated with a given product feature.
  287. *
  288. * @param $pfid
  289. * An Ubercart product feature array.
  290. */
  291. function uc_restrict_qty_feature_delete($feature) {
  292. db_query("DELETE FROM {uc_restrict_qty_products} WHERE pfid = :pfid", array(':pfid' => $feature['pfid']));
  293. }