Commit ff244cf8 authored by Thomas Bilk's avatar Thomas Bilk

added handling and tests for currency cart

parent 0a07d02c
......@@ -495,8 +495,9 @@ class Cart implements Jsonable, \JsonSerializable, Arrayable{
*/
public function total($only_with_condition_type = null) {
$subTotal = $this->subTotal($only_with_condition_type);
if ($this->getConditions($only_with_condition_type)->isEmpty())
if ($this->getConditions($only_with_condition_type)->isEmpty()) {
return $subTotal;
}
/* all cart conditions like fees or discounts are applied */
$condTotal = $this->getConditions($only_with_condition_type)
......
......@@ -54,25 +54,37 @@ class CurrencyCart extends Cart {
*
* @param null|string $only_with_condition_type Only with this conditionType
* @return Money
* @throws \Bnet\Money\MoneyException
*/
public function total($only_with_condition_type = null) {
$subTotal = $this->subTotal($only_with_condition_type);
if ($this->getConditions($only_with_condition_type)->isEmpty())
if ($this->getConditions($only_with_condition_type)->isEmpty()) {
return $subTotal;
}
$subTotal = $subTotal->amount();
$condTotal = $this->getConditions($only_with_condition_type)->sum(function ($cond) use ($subTotal) {
/** @var Condition $cond */
$price =$cond->getTarget() === Condition::TARGET_CART
? $cond->applyCondition($subTotal)
: 0;
return $price instanceof Money
? $price->amount()
: $price;
});
return new Money((int)($subTotal + $condTotal), $this->currency);
/* all cart conditions like fees or discounts are applied */
$condTotal = $this->getConditions($only_with_condition_type)
->filter(function (Condition $cond) {
return $cond->getTarget() === Condition::TARGET_CART;
})
->sum(function (Condition $cond) use ($subTotal) {
$price = $cond->applyCondition($subTotal);
return $price instanceof Money
? $price->amount()
: $price;
});
/* all conditions affecting the price are applied */
$grand_total = $this->getConditions()
->filter(function (Condition $condition) {
return $condition->getTarget() === Condition::TARGET_PRICE;
})->reduce(function ($acc, Condition $cond) {
return $acc + $cond->applyCondition($acc)->amount();
}, $subTotal + $condTotal);
return new Money((int)($grand_total < 0 ? 0 : $grand_total), $this->currency);
}
/**
......@@ -130,9 +142,13 @@ class CurrencyCart extends Cart {
* get cart sub total - items WITHOUT conditions and WITHOUT cart conditions
*
* @return Money
* @throws \Bnet\Money\MoneyException
*/
public function totalItemsWithoutConditions() {
$sum = $this->items()->sum(function (Item $item) {
if ($item->priceSum() instanceof Money) {
return $item->priceSum()->amount();
}
return $item->priceSum();
});
......@@ -169,6 +185,33 @@ class CurrencyCart extends Cart {
return (new CurrencyItems($this->session->get($this->sessionKeyCartItems)));
}
/**
* get a collection of price conditions and their actual value
* @return Conditions
* @throws \Bnet\Money\MoneyException
*/
public function priceConditions() {
$subtotal_with_conditions = $this->totalItemsWithoutConditions()->add($this->totalOnlyConditions());
return $this->getConditions()
->filter(function (Condition $c) {
return $c->getTarget() === Condition::TARGET_PRICE;
})->map(function (CurrencyCondition $c) use (&$subtotal_with_conditions) {
$cond_value = $c->applyCondition($subtotal_with_conditions);
$cond_data = $c->all();
/* when a negative condition results in a negative total
* only the remainder of the condition is used */
if (($diff = $subtotal_with_conditions->add($cond_value))->amount() < 0) {
$subtotal_with_conditions = new Money(0);
/* only the remainder is used for the condition value */
$cond_data['value'] = $cond_value->subtract($diff);
} else {
$subtotal_with_conditions = $subtotal_with_conditions->add($cond_value);
$cond_data['value'] = $cond_value;
}
return new CurrencyCondition($cond_data);
});
}
/**
* add row to cart collection
*
......
......@@ -8,6 +8,8 @@
use Bnet\Cart\Cart;
use Bnet\Cart\Condition;
use Bnet\Cart\Exceptions\InvalidConditionException;
use Bnet\Cart\Exceptions\InvalidItemException;
use Mockery as m;
require_once __DIR__ . '/helpers/SessionMock.php';
......@@ -954,8 +956,8 @@ class CartConditionTest extends PHPUnit_Framework_TestCase {
}
/**
* @throws \Bnet\Cart\Exceptions\InvalidConditionException
* @throws \Bnet\Cart\Exceptions\InvalidItemException
* @throws InvalidConditionException
* @throws InvalidItemException
*/
public function test_single_coupon() {
$this->cart->add([
......@@ -979,8 +981,8 @@ class CartConditionTest extends PHPUnit_Framework_TestCase {
}
/**
* @throws \Bnet\Cart\Exceptions\InvalidConditionException
* @throws \Bnet\Cart\Exceptions\InvalidItemException
* @throws InvalidConditionException
* @throws InvalidItemException
*/
public function test_two_coupons() {
$this->cart->add([
......@@ -1011,8 +1013,8 @@ class CartConditionTest extends PHPUnit_Framework_TestCase {
}
/**
* @throws \Bnet\Cart\Exceptions\InvalidConditionException
* @throws \Bnet\Cart\Exceptions\InvalidItemException
* @throws InvalidConditionException
* @throws InvalidItemException
*/
public function test_larger_coupons_than_items() {
$this->cart->add([
......@@ -1043,8 +1045,8 @@ class CartConditionTest extends PHPUnit_Framework_TestCase {
}
/**
* @throws \Bnet\Cart\Exceptions\InvalidConditionException
* @throws \Bnet\Cart\Exceptions\InvalidItemException
* @throws InvalidConditionException
* @throws InvalidItemException
*/
public function test_coupon_removal() {
$this->cart->add([
......@@ -1087,8 +1089,8 @@ class CartConditionTest extends PHPUnit_Framework_TestCase {
}
/**
* @throws \Bnet\Cart\Exceptions\InvalidConditionException
* @throws \Bnet\Cart\Exceptions\InvalidItemException
* @throws InvalidConditionException
* @throws InvalidItemException
*/
public function test_coupon_and_other_cart_condition() {
$this->cart->add([
......@@ -1124,8 +1126,8 @@ class CartConditionTest extends PHPUnit_Framework_TestCase {
}
/**
* @throws \Bnet\Cart\Exceptions\InvalidConditionException
* @throws \Bnet\Cart\Exceptions\InvalidItemException
* @throws InvalidConditionException
* @throws InvalidItemException
*/
public function test_cart_price_conditions() {
$this->cart->add([
......
<?php
/**
* Created by PhpStorm.
* User: darryl
* Date: 1/12/2015
* Time: 9:59 PM
*/
use Bnet\Cart\Condition;
use Bnet\Cart\CurrencyCart;
use Bnet\Cart\CurrencyCondition;
use Bnet\Cart\Exceptions\InvalidConditionException;
use Bnet\Cart\Exceptions\InvalidItemException;
use Bnet\Money\Money;
use Bnet\Money\MoneyException;
use Mockery as m;
require_once __DIR__ . '/helpers/SessionMock.php';
......@@ -1042,4 +1039,239 @@ class CurrencyCartConditionTest extends PHPUnit_Framework_TestCase {
$this->assertEquals(31095, $this->cart->total()->amount(), 'match total');
}
/**
* @throws InvalidConditionException
* @throws InvalidItemException
* @throws MoneyException
*/
public function test_single_coupon() {
$this->cart->add([
'id' => 456,
'name' => 'Sample Item 1',
'price' => new Money(100),
'quantity' => 3,
]);
$coupon = new CurrencyCondition([
'name' => 'Coupon',
'type' => 'coupon',
'target' => Condition::TARGET_PRICE,
'value' => new Money(-5),
]);
$this->cart->condition($coupon);
$this->assertEquals(300, $this->cart->subTotal()->amount());
$this->assertEquals(295, $this->cart->total()->amount());
}
/**
* @throws InvalidConditionException
* @throws InvalidItemException
* @throws MoneyException
*/
public function test_two_coupons() {
$this->cart->add([
'id' => 456,
'name' => 'Sample Item 1',
'price' => new Money(100),
'quantity' => 3,
]);
$coupon1 = new CurrencyCondition([
'name' => 'Coupon 1',
'type' => 'coupon',
'target' => Condition::TARGET_PRICE,
'value' => new Money(-5),
]);
$coupon2 = new CurrencyCondition([
'name' => 'Coupon 2',
'type' => 'coupon',
'target' => Condition::TARGET_PRICE,
'value' => new Money(-15),
]);
$this->cart->condition([$coupon1, $coupon2]);
$this->assertEquals(300, $this->cart->subTotal()->amount());
$this->assertEquals(280, $this->cart->total()->amount());
}
/**
* @throws InvalidConditionException
* @throws InvalidItemException
* @throws MoneyException
*/
public function test_larger_coupons_than_items() {
$this->cart->add([
'id' => 456,
'name' => 'Sample Item 1',
'price' => new Money(25),
'quantity' => 3,
]);
$coupon1 = new CurrencyCondition([
'name' => 'Coupon 1',
'type' => 'coupon',
'target' => Condition::TARGET_PRICE,
'value' => new Money(-50),
]);
$coupon2 = new CurrencyCondition([
'name' => 'Coupon 2',
'type' => 'coupon',
'target' => Condition::TARGET_PRICE,
'value' => new Money(-30),
]);
$this->cart->condition([$coupon1, $coupon2]);
$this->assertEquals(75, $this->cart->subTotal()->amount());
$this->assertEquals(0, $this->cart->total()->amount());
}
/**
* @throws InvalidConditionException
* @throws InvalidItemException
* @throws MoneyException
*/
public function test_coupon_removal() {
$this->cart->add([
'id' => 456,
'name' => 'Sample Item 1',
'price' => new Money(25),
'quantity' => 3,
]);
$coupon1 = new CurrencyCondition([
'name' => 'Coupon 1',
'type' => 'coupon',
'target' => Condition::TARGET_PRICE,
'value' => new Money(-50),
]);
$coupon2 = new CurrencyCondition([
'name' => 'Coupon 2',
'type' => 'coupon',
'target' => Condition::TARGET_PRICE,
'value' => new Money(-30),
]);
$this->cart->condition([$coupon1, $coupon2]);
$this->assertEquals(75, $this->cart->subTotal()->amount());
$this->assertEquals(0, $this->cart->total()->amount());
/* remove an item from the cart */
$this->cart->update(456, ['quantity' => -1]);
$this->assertEquals(50, $this->cart->subTotal()->amount());
$this->assertEquals(0, $this->cart->total()->amount());
/* remove a coupon */
$this->cart->removeCartCondition('Coupon 1');
$this->assertEquals(50, $this->cart->subTotal()->amount());
$this->assertEquals(20, $this->cart->total()->amount());
}
/**
* @throws InvalidConditionException
* @throws InvalidItemException
* @throws MoneyException
*/
public function test_coupon_and_other_cart_condition() {
$this->cart->add([
'id' => 456,
'name' => 'Sample Item 1',
'price' => new Money(25),
'quantity' => 3,
]);
$coupon1 = new CurrencyCondition([
'name' => 'Coupon 1',
'type' => 'coupon',
'target' => Condition::TARGET_PRICE,
'value' => new Money(-50),
]);
$cartCondition1 = new CurrencyCondition([
'name' => 'SALE 5%',
'type' => 'sale',
'target' => Condition::TARGET_CART,
'value' => '-5%',
'attributes' => [
'description' => 'october fest promo sale',
'sale_start_date' => '2015-01-20',
'sale_end_date' => '2015-01-30',
]
]);
$this->cart->condition([$coupon1, $cartCondition1]);
$this->assertEquals(75, $this->cart->subTotal()->amount());
$this->assertEquals(21, $this->cart->total()->amount());
}
/**
* @throws InvalidConditionException
* @throws InvalidItemException
* @throws MoneyException
*/
public function test_cart_price_conditions() {
$this->cart->add([
'id' => 456,
'name' => 'Sample Item 1',
'price' => new Money(25),
'quantity' => 3,
]);
$this->cart->condition([
new CurrencyCondition([
'name' => 'Coupon 1',
'type' => 'coupon',
'target' => Condition::TARGET_PRICE,
'value' => new Money(-50),
]),
new CurrencyCondition([
'name' => 'Coupon 2',
'type' => 'coupon',
'target' => Condition::TARGET_PRICE,
'value' => new Money(-30),
]),
]);
$conditions = $this->cart->priceConditions();
$this->assertEquals(2, $conditions->count());
$this->assertEquals(
$this->cart->subTotal()->amount(),
abs($conditions->reduce(function (Money $a, CurrencyCondition $c) {
return $a->add($c->getValue());
}, new Money(0))->amount())
);
$this->assertEquals(0, $this->cart->total()->amount());
$this->cart->update(456, ['quantity' => '+1']);
$conditions = $this->cart->priceConditions();
$this->assertEquals(80,
abs($conditions->reduce(function (Money $a, CurrencyCondition $c) {
return $a->add($c->getValue());
}, new Money(0))->amount())
);
$this->assertEquals(20, $this->cart->total()->amount());
$this->cart->removeConditionsByType('coupon');
$conditions = $this->cart->priceConditions();
$this->assertEquals(0, $conditions->count());
$this->assertEquals(0,
abs($conditions->reduce(function (Money $a, CurrencyCondition $c) {
return $a->add($c->getValue());
}, new Money(0))->amount())
);
$this->assertEquals(100, $this->cart->total()->amount());
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment