Giter VIP home page Giter VIP logo

pricing's Introduction

blesta/pricing

Build Status Coverage Status

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)

pricing's People

Contributors

abdyfranco avatar clphillips avatar jreissmueller avatar tysonphillips avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

pricing's Issues

Support tax before discount

Currently tax is calculated for item prices after discounts are applied. There should be an options to calculate tax before discounts are applied.

Add Money Support

Represent all pricing values as Money from Blesta\Money.

Example usage:

$fiveDollars = new Money(500, new Currency('USD'));
$fiveDollars = Money::fromString('5.00', 'USD'); // or this
$fiveDollars = Money::fromString('5.00', new Currency('USD')); // or this

$price = new UnitPrice($fiveDollars, 2);
$price-->setDescription('2 x 5.00');
$unit_price = $price->price(); // Money
$qty = $price->qty(); // 2
$tenDollars = $price->total(); // Money

$tenDollars->getConvertedAmount(); // 10.00
echo $tenDollars; // 10.00

Any value that supports float should support Money, and convert to Money internally if provided with float. Should use the XXX currency code (which denotes no currency), where the precision is out to 4 decimal places. For example:

$price = new UnitPrice(5.00, 2);
$price->price(); // Money 5.0000 XXX
$price->total(); // Money 10.0000 XXX

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.