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
<?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 &$order, array $lineItems = [])
{
if (empty($lineItems))
{
return [];
}
$updated = [];
$amount = 0;
$adjuster = new Commerce_OrderAdjustmentModel();
$adjuster->type = 'Discount';
$adjuster->name = 'Quantity Pricing';
$adjuster->description = 'The overall price of the cart has been adjsuted based on the number of items you are purchasing.';
$adjuster->orderId = $order->id;
foreach ($lineItems as $lineItem)
{
$purchasable = $lineItem->purchasable;
if (!empty($purchasable->quantityPricing))
{
$updated[] = $lineItem->id;
$updatedAmount = 0;
foreach ($purchasable->quantityPricing as $qp)
{
if (
($lineItem->qty >= $qp['minQuantity'] && $lineItem->qty <= $qp['maxQuantity'])
||
($lineItem->qty >= $qp['minQuantity'] && empty($qp['maxQuantity']))
)
{
$lineItem->discount = ($qp['price'] - $lineItem->price) * $lineItem->qty;
$updatedAmount = $lineItem->discount;
}
}
$amount += $updatedAmount;
}
}
$adjuster->amount = $amount;
$adjuster->optionsJson = ['lineItemsAffected' => $updated];
$adjuster->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()->on('commerce_lineItems.onPopulateLineItem', function($event){
$purchasable = $event->params['purchasable'];
$lineItem = $event->params['lineItem'];
// This method checks for quantity pricing and updates
// line items costs depending on what it finds.
if (!empty($purchasable->quantityPricing))
{
foreach ($purchasable->quantityPricing as $qp)
{
if (
($lineItem->qty >= $qp['minQuantity'] && $lineItem->qty <= $qp['maxQuantity'])
||
($lineItem->qty >= $qp['minQuantity'] && empty($qp['maxQuantity']))
)
{
$lineItem->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.