I'm working on building a Commerce-based store where some of the products are only allowed to be purchased in multiples of a certain quantity (the "quantity block").

For example, if the quantity block of a product is 20, then the customer can only purchase a quantity of 20, 40, 60, 80, 100, 120, etc..

I realize that I could probably handle this via a custom rule - when the user adds a product to their cart, check to make sure the quantity desired is divisible by the "quantity block" with a remainder of 0. But, I'm wondering if there is a better way...

All products in the store already have a "max quantity" defined - this is the maximum number of units per product that a customer can purchase. This is working fine with the help of a custom Rule. I'm thinking that if there is a quantity block and max quantity defined, then it would be ideal to automagically change the quantity text field into a select field will all of the possible orderable quantities.

I'm willing to write a custom module to handle this, but first I'm hoping for some guidance as to if this is even possible, what is the best way to approach (FormAPI?), and any potential pitfalls.

Thanks,
-mike

Comments

ajsleigh’s picture

Mike, it sounds like you need to be thinking in terms of "pack size" i.e. your product has a pack size of 20 so when the customer orders 2 of them you ship 40 individual items. This would eliminate any messing around with quantities in the shopping cart.

bhosmer’s picture

Sub

ultimike’s picture

ajsleigh,

Under most circumstances, I would agree with you - a pack size would be the way to go. For this particular application, it is important to the client that each product describe a single part - regardless of the packaging.

Thanks,
-mike

rszrama’s picture

Mike, I'm not sure I'd try something like this through Rules. You'd probably be best served by a form alter on the quantity textfields to turn them into select lists or something so that the customer can only choose to purchase valid amounts. I don't have any guidance off the top of my head for form IDs / elements to loop over to alter the quantity boxes, but it shouldn't be too hard to dig up. Just remember that the Cart form is coming from Views, so you won't find it defined in any one place in a module.

ultimike’s picture

Ryan,

Thanks for the advice - much appreciated. Always good to have a sanity check...

Anyway, I went ahead and implemented a custom module for this functionality utlilizing FormAPI. I'm including the code below in case it can help someone else. If you don't mind taking a quick look at it and letting me know if you see any glaring issues with it, I'd appreciate it.

Congrats on getting Commerce 1.0 out-the-door!

Thanks,
-mike

<?php

/*
 * Implements hook_form_alter().
 */
function mymodule_commerce_form_alter(&$form, &$form_state, $form_id) {

  // Find any "add to cart" forms on individual product displays and modify the quantity.
  //   field to account for quantity blocks.
  if (strpos($form_id, 'commerce_cart_add_to_cart_form_') === 0) {
    _mymodule_commerce_modify_productdisplay_quantity_widget($form);
  }

  // Find any shopping cart forms and modify the quantity fields to account for quantity blocks.
  if ($form_id == 'views_form_commerce_cart_form_default') {
    _mymodule_commerce_modify_shoppingcart_quantity_options($form);
  }
}

/*
 * Modify the quantity widget on Product Display nodes.
 */
function _mymodule_commerce_modify_productdisplay_quantity_widget(&$form) {
  // get the product id
  $pid = $form['product_id']['#value'];

  // get the options array if a select box is necessary
  $options = _mymodule_commerce_get_quantity_options($pid);
  
  // if options were returned, update the widget
  if (count($options)) {
    $form['quantity']['#type'] = 'select';
    $form['quantity']['#size'] = 1;
    $form['quantity']['#options'] = $options;
  }
}

/*
 * Modify the quantity widget in Shopping Carts.
 */
function _mymodule_commerce_modify_shoppingcart_quantity_options(&$form) {
  // loop through all of the quantity fields in the shopping cart form
  foreach ($form['edit_quantity'] as $key => $quantity_field) {
    if (is_array($quantity_field)) {
      // query the DB for each line item in order to get the product id
      $entity_type = 'commerce_line_item';
      $query = new EntityFieldQuery();
      $query->entityCondition('entity_type', $entity_type, '=')
            ->propertyCondition('line_item_id', $quantity_field['#line_item_id'], '=');
      $result = $query->execute();

      if (!empty($result[$entity_type])) {
        // load the full line item information
        $line_item = entity_load($entity_type, array_keys($result[$entity_type]));

        // get the options array if a select box is necessary
        $pid = $line_item[$quantity_field['#line_item_id']]->commerce_product['und'][0]['product_id'];
        $options = _mymodule_commerce_get_quantity_options($pid);

        // if options were returned, update the widget
        if (count($options)) {
          $form['edit_quantity'][$key]['#type'] = 'select';
          $form['edit_quantity'][$key]['#size'] = 1;
          $form['edit_quantity'][$key]['#options'] = $options;
        }
      }
    }
  }
}

/*
 * Helper function to modify quantity widgets (when necessary) when the quantity block
 *   is greater than 1 for a particular product.
 */
function _mymodule_commerce_get_quantity_options($pid) {
  $options = array();

  // get the product based on the product id and whether or not is has a Quantity Block greater than 1
  $entity_type = 'commerce_product';
  $query = new EntityFieldQuery();
  $query->entityCondition('entity_type', $entity_type, '=')
        ->propertyCondition('product_id', $pid, '=')
        ->fieldCondition('field_quantityblock', 'value', 1, '>');
  $result = $query->execute();

  if (!empty($result[$entity_type])) {
    $entities = entity_load($entity_type, array_keys($result[$entity_type]));

    // set up convenient variables for the Maximum Quantity and Quantity Block
    $max = $entities[$pid]->field_maxquantity['und'][0]['value'];
    $quantity_block = $entities[$pid]->field_quantityblock['und'][0]['value'];

    // if the Quantity Block is greater than 1, then swap out the quantity widget
    if ($quantity_block > 1) {
      $option = $quantity_block;
      while ($option < $max) {
        $options[$option] = $option;
        $option = $option + $quantity_block;
      }
    }
  }
  return $options;
}
rszrama’s picture

Status: Active » Fixed

Cool, only thing I might wonder is if for the forms you need to load the products or if the data is already in the form... but I guess the entity_load() is just pulling data from the cache, so it shouldn't be a performance problem.

ultimike’s picture

Ryan,

Nope - the product data I needed wasn't already in the form, which is why I had to pull the entire product as well.

I'll update the code if our testing uncovers any issues.

Thanks,
-mike

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

joecanti’s picture

Hi Mike,

I'm looking for a similar solution. Does your code turn the quantity widget into a select list as well?

And where do you input the quantity block size? Is that a field on the product creation?

At the moment each new quantity is a new product for me, which works wonderfully from the front end, but could make the site a bit messy from the back end, especially if the client decides they want to stock control later on.

Thanks for your time

Joe

joecanti’s picture

Ok, I had a play around and got your example working. Works really well thanks!

So now I would like to modify it to simply output the values of the quantityblock field (rather than any calculation)

EG, if I have a select list, and I select 1, 2, 3, 4, 5, 10, 20 - I would like the quantities to simply have those options available. So I could order 1, 2, 3, 4, 5, 10 or 20 items.

Do you know how I would achieve this? Should be simple, but I've not got much php experience - it's next on the list to learn!

Many thanks,

Joe

ultimike’s picture

Joe,

So you want to be able to specify a list of available quantities rather than automatically generating the list based on a "quantityblock" field, correct?

I suppose you can add a field to the product display where you can input the list of available quantities, then modify the code I wrote to pull from that field instead of pulling from the quantityblock field and doing the calculation.

I admit - it'll be a challenge for you to do unless you have some knowledge of PHP...

Thanks,
-mike

joecanti’s picture

Hi Mike,

That's the aim yes. At the moment I have a select list for the quantity block field. It can have multiple values, so the aim is to pass them straight to the quantity select, and ignore the calculation, and the '> than 1' condition.

My php is not so hot yet as I am a beginner - but a collegue of mine knows php well, so hopefully we'll get it together and find a good solution.

Perhaps I thought it was less complex than it is - usually the case!

All the best, Joe

bdevore’s picture

Just thinking out loud here, I need to do something similar. Could you hide the quantity on the commerce field and leave it at 1, set up another field in the commerce custom line item type for "quantity", and then use rules to read that list and adjust the quantities in the cart after submit? Might not scale up very well, but until a proper module exists might do in a pinch.

Never mind. I just looked into the rules and there's not currently a specific option for adjusting the quantity, not to mention once you get into setting up all the rules entities this starts to feel pretty unwieldy. Probably better off trying to do this with a hook alter on the form.

mit832’s picture

Issue summary: View changes

Hi all
After install this module following error occurred. If any body help regarding this issues, it could be great.

EntityFieldQueryException: Unknown field: field_quantityblock in EntityFieldQuery->addFieldCondition() (line 779 of D:\xampp\htdocs\Drupal\drupal-hvp24-commerce-kick\includes\entity.inc).

Thanks

mit832’s picture

Hi all
After install this module following error occurred. If any body help regarding this issues, it could be great.

EntityFieldQueryException: Unknown field: field_quantityblock in EntityFieldQuery->addFieldCondition() (line 779 of D:\xampp\htdocs\Drupal\drupal-hvp24-commerce-kick\includes\entity.inc).

Thanks