Skip to content

Commit

Permalink
Merge pull request #4 from blesta/missing-commits-1-2-0
Browse files Browse the repository at this point in the history
Missing commits from 1.2.0 tag
  • Loading branch information
clphillips committed Aug 31, 2015
2 parents e95e085 + 84fa146 commit 5a1ac99
Show file tree
Hide file tree
Showing 6 changed files with 471 additions and 75 deletions.
47 changes: 44 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ A library for handling pricing. Supports:
- Fixed amounts
- Taxes (inclusive, exclusive)
- Inclusive and Exclusive
- Applied in sequence of compounded
- Applied in sequence or compounded
- Item Collection
- Iterate over Item Prices
- Aggregate totals over Item Prices
Expand Down Expand Up @@ -90,17 +90,31 @@ $item_price->total(); // 30.00
With discount applied:

```php
$discount = new Discount(5.00, "percent");
$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:

```php
$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:

```php
$tax = new Discount(10.00, "exclusive");
$tax = new TaxPrice(10.00, "exclusive");

// call setTax() as many times as needed to apply multiple levels of taxes
$item_price->setTax($tax);
Expand All @@ -115,6 +129,33 @@ With tax and discount:
$item_price->total(); // 31.35
```

Amount applied for a specific tax:

```php
$tax1 = new TaxPrice(10.00, "exclusive");
$tax2 = new TaxPrice(5.00, "exclusive");

// NOTE: order *DOES NOT* matter
$item_price->setTax($tax1);
$item_price->setTax($tax2);

$item_price->taxAmount($tax1); // 3.00
$item_price->taxAmount($tax2); // 1.50
```

Cascading tax:

```php
$tax1 = new TaxPrice(10.00, "exclusive");
$tax2 = new TaxPrice(5.00, "exclusive");
$tax3 = new TaxPrice(2.50, "exclusive");

$item_price->setTax($tax1, $tax2, $tax3);
$item_price->taxAmount($tax1); // 3.00
$item_price->taxAmount($tax2); // ((30.00 * 0.10) + 30.00) * 0.05 -> 1.65
$item_price->taxAmount($tax3); // (((30.00 * 0.10) + 30.00) * 0.05) + 30.00 * 0.025 -> 0.86625
```

### ItemPriceCollection

```php
Expand Down
6 changes: 3 additions & 3 deletions src/Collection/ItemPriceCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -208,12 +208,12 @@ public function discounts()
}

/**
* Resets the applied discount amounts for all DiscountPrice's in the collection
* Resets the applied discount amounts for all ItemPrice's in the collection
*/
public function resetDiscounts()
{
foreach ($this->discounts() as $discount) {
$discount->reset();
foreach ($this->collection as $item_price) {
$item_price->resetDiscounts();
}
}

Expand Down
193 changes: 157 additions & 36 deletions src/Type/ItemPrice.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ class ItemPrice extends UnitPrice implements PriceTotalInterface
* @var boolean Whether or not to cache discount subtotals
*/
private $cache_discount_amounts = false;
/**
* @var float The item price subtotal after individual discounts were applied
*/
private $discounted_subtotal = 0;

/**
* @var array A numerically-indexed array of DiscountPrice objects
Expand All @@ -23,6 +27,20 @@ class ItemPrice extends UnitPrice implements PriceTotalInterface
*/
protected $taxes = array();

/**
* Initialize the item price
*
* @param float $price The unit price
* @param int $qty The quantity of unit prices (optional, default 1)
*/
public function __construct($price, $qty = 1)
{
parent::__construct($price, $qty);

// Reset the internal discount subtotal
$this->resetDiscountSubtotal();
}

/**
* Assigns a discount to the item
*
Expand Down Expand Up @@ -139,28 +157,83 @@ public function total()
*/
public function taxAmount(TaxPrice $tax = null)
{
$tax_amount = 0;
$taxable_price = $this->totalAfterDiscount();

// Determine the tax set on this item's price
if ($tax) {
$tax_amount = $tax->on($taxable_price);
$tax_amount = $this->amountTax($tax);
} else {
// Determine all taxes set on this item's price, compounded accordingly
foreach ($this->taxes as $tax_group) {
$compound_tax = 0;
foreach ($tax_group as $tax) {
$compound_tax += $tax->on($taxable_price + $compound_tax);
}
$tax_amount = $this->amountTaxAll();
}

return $tax_amount;
}

/**
* Retrieves the tax amount for the given TaxPrice
*
* @param TaxPrice $tax A specific tax price whose tax to calculate for this item
* @return float The total tax amount for the given TaxPrice
*/
private function amountTax(TaxPrice $tax)
{
$taxable_price = $this->totalAfterDiscount();
$tax_amount = 0;

// Sum all taxes
$tax_amount += $compound_tax;
foreach ($this->taxes as $tax_group) {
// Only calculate the tax amount if the tax exists in a tax group
if (in_array($tax, $tax_group, true)) {
$tax_amount = $this->compoundTaxAmount($tax_group, $taxable_price, $tax);
}
}

return $tax_amount;
}

/**
* Retrieves the total tax amount considering all item taxes
*
* @return float The total tax amount for all taxes set for this item
*/
private function amountTaxAll()
{
$taxable_price = $this->totalAfterDiscount();
$tax_amount = 0;

// Determine all taxes set on this item's price, compounded accordingly
foreach ($this->taxes as $tax_group) {
// Sum all taxes
$tax_amount += $this->compoundTaxAmount($tax_group, $taxable_price);
}

return $tax_amount;
}

/**
* Retrieves the tax amount for a specific tax group
*
* @param array $tax_group A subset of the taxes array
* @param float $taxable_price The total amount from which to calculate tax
* @param TaxPrice $tax A specific tax from the group whose tax amount to retrieve (optional)
* @return float The total tax amount for all taxes set for this item in this group, or
* the tax amount for the given TaxPrice
*/
private function compoundTaxAmount(array $tax_group, $taxable_price, TaxPrice $tax = null)
{
$compound_tax = 0;

foreach ($tax_group as $tax_price) {
// Calculate the compound tax
$tax_amount = $tax_price->on($taxable_price + $compound_tax);
$compound_tax += $tax_amount;

// Ignore any other group taxes, and only return the tax amount for the given TaxPrice
if ($tax && $tax === $tax_price) {
return $tax_amount;
}
}

return $compound_tax;
}

/**
* Retrieves the total discount amount considering all item discounts, or just the given discount
*
Expand All @@ -176,31 +249,9 @@ public function discountAmount(DiscountPrice $discount = null)

// Determine the discount set on this item's price
if ($discount) {
$total_discount = $discount->on($subtotal);
$total_discount = $this->amountDiscount($discount);
} else {
// Determine all the discounts set on this item's price
$temp_subtotal = $subtotal;
foreach ($this->discounts as $key => $discount) {
// Fetch the discount amount and remove it from the DiscountPrice,
// or use the values previously cached
if ($this->cache_discount_amounts || empty($this->discount_amounts)) {
// Get the discount on the subtotal
$discount_amount = $discount->on($temp_subtotal);
$total_discount += $discount_amount;

// Cache the discount set for this DiscountPrice
if ($this->cache_discount_amounts) {
$this->discount_amounts[$key] = $discount_amount;
}

// Update the subtotal for this item to remove the amount discounted
$temp_subtotal = $discount->off($temp_subtotal);
} else {
// Use the cached discount amount for this DiscountPrice
$total_discount += $this->discount_amounts[$key];
}

}
$total_discount = $this->amountDiscountAll();
}

// Total discount not to exceed the subtotal amount, neither positive nor negative
Expand All @@ -211,6 +262,64 @@ public function discountAmount(DiscountPrice $discount = null)
);
}

/**
* Retrieves the total discount amount considering the given discount
*
* @param DiscountPrice $discount A specific discount price whose discount to calculate
* for this item
* @return float The total discount amount for the given discount price
*/
private function amountDiscount(DiscountPrice $discount)
{
$total_discount = 0;

// Only calculate the discount amount if the discount is set for this item
if (in_array($discount, $this->discounts, true)) {
// Get the discount on the discounted subtotal remaining
$total_discount = $discount->on($this->discounted_subtotal);

// Update the discounted subtotal for this item by removing the amount discounted
$this->discounted_subtotal = $discount->off($this->discounted_subtotal);
}

return $total_discount;
}

/**
* Retrieves the total discount amount considering all item discounts
*
* @return float The total discount amount for all discounts set for this item
*/
private function amountDiscountAll()
{
$subtotal = $this->subtotal();
$total_discount = 0;

// Determine all the discounts set on this item's price
foreach ($this->discounts as $key => $discount) {
// Fetch the discount amount and remove it from the DiscountPrice,
// or use the values previously cached
if ($this->cache_discount_amounts || empty($this->discount_amounts)) {
// Get the discount on the subtotal
$discount_amount = $discount->on($subtotal);
$total_discount += $discount_amount;

// Cache the discount set for this DiscountPrice
if ($this->cache_discount_amounts) {
$this->discount_amounts[$key] = $discount_amount;
}

// Update the subtotal for this item to remove the amount discounted
$subtotal = $discount->off($subtotal);
} else {
// Use the cached discount amount for this DiscountPrice
$total_discount += $this->discount_amounts[$key];
}
}

return $total_discount;
}

/**
* Fetch all unique taxes set
*
Expand Down Expand Up @@ -240,8 +349,20 @@ public function discounts()
*/
public function resetDiscounts()
{
// Reset the internal discounted subtotal
$this->resetDiscountSubtotal();

// Reset each discount
foreach ($this->discounts as $discount) {
$discount->reset();
}
}

/**
* Resets the discounted subtotal used internally
*/
private function resetDiscountSubtotal()
{
$this->discounted_subtotal = $this->subtotal();
}
}
Loading

0 comments on commit 5a1ac99

Please sign in to comment.