Craft Commerce: How do I handle quantity based discounts on a product level? I'm curious what the best way to handle quantity discounts would be on a product/variant level. For example:

The base price of a variant is $3 each, but if you buy 10-19 they're $2 each, and if you buy more than 20 they're $1 each.

A different variant of the same product could have a base price of $10 each, but if you buy 20-49 they're $8 each, and if you buy over 50 they're $6 each.

Thoughts?

EDIT 2 (added file paths)

Based on the comments by Luke and Andris below, I've implemented an adjuster to handle this. I created a table field with the handle quantityPricing with 3 columns: minQuantity, maxQuantity, price. I then applied this field to the variants on a product.

This way a user can create as many prices for quantity ranges as they want. I then create the adjuster, checking the line items via the following:

craft/plugins/myplugin/Adjusters/Myplugin_Adjustername.php

&lt;?php

namespace Commerce\Adjusters;
use Craft\Commerce_LineItemModel;
use Craft\Commerce_OrderAdjustmentModel;
use Craft\Commerce_OrderModel;

class Myplugin_Adjustername implements Commerce_AdjusterInterface {
public function adjust(Commerce_OrderModel &amp;$order, array $lineItems = [])
{

    if (empty($lineItems))
    {
        return [];
    }

    $updated  = [];
    $amount   = 0;
    $adjuster = new Commerce_OrderAdjustmentModel();

    $adjuster-&gt;type        = 'Discount';
    $adjuster-&gt;name        = 'Quantity Pricing';
    $adjuster-&gt;description = 'The overall price of the cart has been adjsuted based on the number of items you are purchasing.';
    $adjuster-&gt;orderId     = $order-&gt;id;

    foreach ($lineItems as $lineItem)
    {
        $purchasable = $lineItem-&gt;purchasable;
        if (!empty($purchasable-&gt;quantityPricing))
        {
            $updated[]     = $lineItem-&gt;id;
            $updatedAmount = 0;
            foreach ($purchasable-&gt;quantityPricing as $qp)
            {
                if (
                    ($lineItem-&gt;qty &gt;= $qp['minQuantity'] &amp;&amp; $lineItem-&gt;qty &lt;= $qp['maxQuantity'])
                 ||
                    ($lineItem-&gt;qty &gt;= $qp['minQuantity'] &amp;&amp; empty($qp['maxQuantity']))
                )
                {
                    $lineItem-&gt;discount = ($qp['price'] - $lineItem-&gt;price) * $lineItem-&gt;qty;
                    $updatedAmount      = $lineItem-&gt;discount;
                }
            }
            $amount += $updatedAmount;
        }
    }

    $adjuster-&gt;amount      = $amount;
    $adjuster-&gt;optionsJson = ['lineItemsAffected' =&gt; $updated];

    $adjuster-&gt;included = false;

    if (empty($updated))
    {
        return[];
    }

    return [$adjuster];
}
}


I was also seemingly able to do this using onPopulateLineItem but based on the advice below changed it. My code for that looked like this:

    craft()-&gt;on('commerce_lineItems.onPopulateLineItem', function($event){

            $purchasable = $event-&gt;params['purchasable'];
            $lineItem    = $event-&gt;params['lineItem'];

            // This method checks for quantity pricing and updates 
            // line items costs depending on what it finds.
            if (!empty($purchasable-&gt;quantityPricing))
            {
                foreach ($purchasable-&gt;quantityPricing as $qp)
                {
                    if (
                        ($lineItem-&gt;qty &gt;= $qp['minQuantity'] &amp;&amp; $lineItem-&gt;qty &lt;= $qp['maxQuantity'])
                     ||
                        ($lineItem-&gt;qty &gt;= $qp['minQuantity'] &amp;&amp; empty($qp['maxQuantity']))
                    )
                    {
                        $lineItem-&gt;price = (float) $qp['price'];
                    }
                }
            }
        });


As mentioned in the comments below, I'm not exactly sure why an Adjuster is the better route to go, because both seem to work.

Please don't hesitate to mention if I did something ridiculously stupid somewhere. It certainly wouldn't be the first time.
Have you looked into building an adjuster for this purpose? You can find the documentation here