blesta / pricing
A library for handling pricing and pricing modifiers
Requires
- php: >=5.4.0
Requires (Dev)
- phpunit/phpunit: ~4.6
- satooshi/php-coveralls: ^1.0
- squizlabs/php_codesniffer: ~2.3
This package is auto-updated.
Last update: 2024-10-25 00:51:31 UTC
README
A library for handling pricing. Supports:
- Unit Prices
- Item Prices
- Unit Price that may include discounts and taxes
- Discounts
- Percentages
- Fixed amounts
- Taxes (inclusive_calculated, inclusive, exclusive)
- Inclusive and Exclusive
- Applied in sequence or compounded
- Inclusive calculated is meant to be subtracted from the item price
- Item Collection
- Iterate over Item Prices
- Aggregate totals over Item Prices
Installation
Install via composer:
composer require blesta/pricing
Basic Usage
UnitPrice
use Blesta\Pricing\Type\UnitPrice; $price = new UnitPrice(5.00, 2, "id"); $price->setDescription("2 X 5.00"); $unit_price = $price->price(); // 5.00 $qty = $price->qty(); // 2 $total = $price->total(); // 10.00 $key = $price->key(); // id // Update the unit price, quantity, and key $price->setPrice(10.00); $price->setQty(3); $price->setKey('id2');
DiscountPrice
use Blesta\Pricing\Modifier\DiscountPrice; $discount = new DiscountPrice(25.00, "percent"); $discount->setDescription("25% off"); $price_after_discount = $discount->off(100.00); // 75.00 $discount_price = $discount->on(100.00); // 25.00
TaxPrice
Exclusive tax (price does not include tax):
use Blesta\Pricing\Modifier\TaxPrice; $tax = new TaxPrice(10.00, TaxPrice::EXCLUSIVE); $tax->setDescription("10 % tax"); $tax->on(100.00); // 10.00 $tax->off(100.00); // 100.00 (price on exclusive tax doesn't include tax, so nothing to take off) $tax->including(100.00); // 110.00
Inclusive tax (price already includes tax):
use Blesta\Pricing\Modifier\TaxPrice; $tax = new TaxPrice(25.00, TaxPrice::INCLUSIVE); $tax->setDescription("25 % tax"); $tax->on(100.00); // 25.00 $tax->off(100.00); // 75.00 $tax->including(100.00); // 100.00
Inclusive tax (price already includes tax) calculated based on the price minus tax:
use Blesta\Pricing\Modifier\TaxPrice; $tax = new TaxPrice(25.00, TaxPrice::INCLUSIVE_CALCULATED); $tax->setDescription("25 % tax"); $tax->on(100.00); // 20.00 $tax->off(100.00); // 80.00 $tax->including(100.00); // 100.00
Cascading tax (tax on a tax):
use Blesta\Pricing\Modifier\TaxPrice; use Blesta\Pricing\Type\UnitPrice; $price = new UnitPrice(10.00); $tax1 = new TaxPrice(10.00, TaxPrice::EXCLUSIVE); $tax2 = new TaxPrice(5.00, TaxPrice::EXCLUSIVE); $tax2->on( $tax1->on( $price->total() ) + $price->total() ); // 0.55 = [((10.00 * 0.10) + 10.00) * 0.05]
ItemPrice
use Blesta\Pricing\Type\ItemPrice; $item_price = new ItemPrice(10.00, 3); $item_price->total(); // 30.00
With discount applied:
use Blesta\Pricing\Modifier\DiscountPrice; $discount = new DiscountPrice(5.00, "percent"); // call setDiscount() as many times as needed to apply discounts $item_price->setDiscount($discount); $item_price->totalAfterDiscount(); // 28.50
Amount applied for a specific discount:
use Blesta\Pricing\Modifier\DiscountPrice; $item_price = new ItemPrice(10.00, 3); $discount1 = new DiscountPrice(5.00, "percent"); $discount2 = new DiscountPrice(25.00, "percent"); // NOTE: Order matters here $item_price->setDiscount($discount1); $item_price->setDiscount($discount2); $item_price->discountAmount($discount1); // 1.50 $item_price->discountAmount($discount2); // 7.125 ((30.00 - 1.50) * 0.25)
With tax applied:
use Blesta\Pricing\Modifier\TaxPrice; $tax = new TaxPrice(10.00, TaxPrice::EXCLUSIVE); // call setTax() as many times as needed to apply multiple levels of taxes $item_price->setTax($tax); // pass as many TaxPrice objects to setTax as you want to compound tax // ex. $item_price->setTax($tax1, $tax2, ...); $item_price->totalAfterTax(); // 32.1375 = (subtotal + ([subtotal - discounts] * taxes)) = (30 + [30 - (1.50 + 7.125)] * 0.10)
With tax and discount:
$item_price->total(); // 23.5125 = (subtotal - discounts + ([subtotal - discounts] * taxes)) = (30 - (1.50 + 7.125) + [30 - (1.50 + 7.125)] * 0.10)
With tax and discount where the discount does not apply to the taxes:
$item_price->setDiscountTaxes(false); $item_price->total(); // 24.375 = (subtotal - discounts + ([subtotal] * taxes)) = (30 - (1.50 + 7.125) + ([30] * 0.10))
Without taxes of the 'exclusive' type:
$item_price->setDiscountTaxes(true); $item_price->excludeTax(TaxPrice::EXCLUSIVE)->totalAfterTax(); // 30.00 = (30 + [30 - (1.50 + 7.125)] * 0) $item_price->total(); // 21.375 = (30 - (1.50 + 7.125) + [30 - (1.50 + 7.125)] * 0) // Be sure to reset the excluded taxes before attempting to fetch totals that should include them again! $item_price->resetTaxes(); $item_price->total(); // 23.5125 = (subtotal - discounts + ([subtotal - discounts] * taxes)) = (30 - (1.50 + 7.125) + [30 - (1.50 + 7.125)] * 0.10) $item_price->excludeTax(TaxPrice::EXCLUSIVE)->total(); // 21.375 = (30 - (1.50 + 7.125) + [30 - (1.50 + 7.125)] * 0) $item_price->resetTaxes();
Amount applied for a specific tax:
use Blesta\Pricing\Modifier\TaxPrice; $tax1 = new TaxPrice(10.00, TaxPrice::EXCLUSIVE); $tax2 = new TaxPrice(5.00, TaxPrice::INCLUSIVE); // NOTE: order *DOES NOT* matter $item_price->setTax($tax1); $item_price->setTax($tax2); $item_price->taxAmount($tax1); // 2.1375 = ([subtotal - discounts] * taxes) = ([30 - (1.50 + 7.125)] * 0.10) $item_price->taxAmount($tax2); // 1.06875 = ([subtotal - discounts] * taxes) = ([30 - (1.50 + 7.125)] * 0.05)
Without taxes of the 'exclusive' type:
$item_price->excludeTax(TaxPrice::EXCLUSIVE)->totalAfterTax(); // 31.06875 = (subtotal + ([subtotal - discounts] * taxes)) = (30 + [30 - (1.50 + 7.125)] * 0.05) $item_price->resetTaxes();
Cascading tax:
use Blesta\Pricing\Modifier\TaxPrice; use Blesta\Pricing\Type\ItemPrice; $item_price = new ItemPrice(10.00, 3); $tax1 = new TaxPrice(10.00, TaxPrice::EXCLUSIVE); $tax2 = new TaxPrice(5.00, TaxPrice::INCLUSIVE); $tax3 = new TaxPrice(2.50, TaxPrice::EXCLUSIVE); $item_price->setTax($tax1, $tax2, $tax3); $item_price->taxAmount($tax1); // 3.00 = ([subtotal - discounts] * taxes) = ([30 - 0] * 0.10) $item_price->taxAmount($tax2); // 1.65 = ([subtotal - discounts + previous-taxes] * 0.05) = ([30.00 - 0 + 3.00] * 0.05) $item_price->taxAmount($tax3); // 0.86625 = ([subtotal - discounts + previous-taxes] * 0.025) = ([30.00 - 0 + 3.00 + 1.65] * 0.025) $item_price->taxAmount(); // 5.51625 // Exclude taxes of the 'inclusive' type $item_price->excludeTax(TaxPrice::INCLUSIVE); $item_price->taxAmount($tax1); // 3.00 = ([subtotal - discounts] * taxes) = ([30 - 0] * 0.10) $item_price->taxAmount($tax2); // 0 = ([subtotal - discounts + previous-taxes] * 0) = ([30.00 - 0 + 3.00] * 0) $item_price->taxAmount($tax3); // 0.86625 = ([subtotal - discounts + previous-taxes] * 0.025) = ([30.00 - 0 + 3.00 + 1.65] * 0.025) $item_price->taxAmount(); // 3.86625 $item_price->resetTaxes();
ItemPriceCollection
use Blesta\Pricing\Collection\ItemPriceCollection; use Blesta\Pricing\Type\ItemPrice; $item_collection = new ItemPriceCollection(); $item1 = new ItemPrice(10.00, 3); $item2 = new ItemPrice(25.00, 2); $item_collection->append($item1)->append($item2); $item_collection->total(); // 80.00 foreach ($item_collection as $item) { $item->total(); // 30.00, 50.00 }
PricingFactory
Using the PricingFactory can streamline usage. Assume you have the following:
$products = array( array('desc' => 'Apples', 'amount' => 0.5, 'qty' => 3), array('desc' => 'Oranges', 'amount' => 0.75, 'qty' => 10) );
So we initialize our PricingFactory, and let it create our DiscountPrice and TaxPrice objects for use.
use Blesta\Pricing\PricingFactory; $pricing_factory = new PricingFactory(); // Some coupon $discount = $pricing_factory->discountPrice(50.00, "percent"); $discount->setDescription('Super-Saver Coupon'); // Typical local sales tax $tax = $pricing_factory->taxPrice(10.00, TaxPrice::EXCLUSIVE); $tax->setDescription("Sales tax");
Then we let the PricingFactory initialize our ItemPriceCollection, and each ItemPrice over our data set.
$item_collection = $pricing_factory->itemPriceCollection(); foreach ($products as $product) { $item = $pricing_factory->itemPrice($product['amount'], $product['qty']); $item->setDescription($product['desc']); $item->setTax($tax); if ('Apples' === $product['desc']) { $item->setDiscount($discount); } $item_collection->append($item); } $item_collection->discountAmount($discount); // 0.75 $item_collection->taxAmount($tax); // 0.825 $item_collection->subtotal(); // 9.00 $item_collection->totalAfterTax(); // 9.825 $item_collection->totalAfterDiscount(); // 8.25 $item_collection->total(); // 9.075
You may also exclude specific taxes by their type when calculating totals:
$item_collection->excludeTax(TaxPrice::EXCLUSIVE)->taxAmount($tax); // 0.00 $item_collection->excludeTax(TaxPrice::EXCLUSIVE)->totalAfterTax(); // 9.00 $item_collection->excludeTax(TaxPrice::EXCLUSIVE)->total(); // 8.25 $item_collection->total(); // 9.075 (item tax exclusions in the collection are reset after each call to a ::total..., the ::taxAmount, or ::discountAmount)