Commit 1558f858 authored by Thorsten Buss's avatar Thorsten Buss

* add attributes to cart

* change condition calcuation behaviour - return only their calculation, not added to the item amount
* add Condition attribute "quantity_undepended" to calculate the condition only once per item, not multiply with the quantity
* some code simplicfications
parent 78f13726
......@@ -48,6 +48,11 @@ class Cart implements Jsonable, \JsonSerializable, Arrayable{
*/
protected $sessionKeyCartConditions;
/**
* @var \Illuminate\Support\Collection
*/
public $attributes;
protected $item_rules = array(
'id' => 'required',
// 'price' => 'required|numeric',
......@@ -73,6 +78,7 @@ class Cart implements Jsonable, \JsonSerializable, Arrayable{
$this->events->fire($this->getInstanceName() . '.created', array($this));
if (!empty($custom_item_rules))
$this->item_rules = $custom_item_rules;
$this->attributes = collect();
}
/**
......@@ -485,9 +491,7 @@ class Cart implements Jsonable, \JsonSerializable, Arrayable{
* @return int
*/
public function subTotal() {
$cart = $this->items();
$sum = $cart->sum(function (Item $item) {
$sum = $this->items()->sum(function (Item $item) {
return $item->priceSumWithConditions();
});
......@@ -500,28 +504,18 @@ class Cart implements Jsonable, \JsonSerializable, Arrayable{
* @return int
*/
public function total() {
$subTotal = $this->subTotal();
$newTotal = 0;
if ($this->getConditions()->isEmpty())
return $this->subTotal();
$process = 0;
$conditions = $this->getConditions();
// if no conditions were added, just return the sub total
if (!$conditions->count()) return $subTotal;
$conditions->each(function ($cond) use ($subTotal, &$newTotal, &$process) {
if ($cond->getTarget() === 'cart') {
($process > 0) ? $toBeCalculated = $newTotal : $toBeCalculated = $subTotal;
$newTotal = $cond->applyCondition($toBeCalculated);
$subTotal = $this->subTotal();
$process++;
}
$condTotal = $this->getConditions()->sum(function ($cond) use ($subTotal) {
return $cond->getTarget() === 'cart'
? $cond->applyCondition($subTotal)
: 0;
});
return $newTotal;
return $subTotal + $condTotal;
}
/**
......@@ -751,5 +745,22 @@ EOF;
return $this->toArray();
}
/**
* Add an attribute vor the cart
* @param $key
* @param $value
*/
public function addAttribute($key, $value) {
$this->attributes->offsetSet($key, $value);
}
/**
* @param $key
* @param null $default
* @return mixed
*/
public function getAttribute($key, $default = null) {
return $this->attributes->get($key, $default);
}
}
......@@ -96,6 +96,13 @@ class Condition extends Collection {
return $this->get('value');
}
/**
* should the amount be multiplied by the item quantity
*/
public function getQuantityUndepended() {
return $this->get('quantity_undepended', false);
}
/**
* apply condition to total or subtotal
*
......@@ -106,6 +113,17 @@ class Condition extends Collection {
return $this->apply($totalOrSubTotalOrPrice, $this->getValue());
}
/**
* apply condition to total or subtotal
*
* @param $totalOrSubTotalOrPrice
* @param int $quantity
* @return int
*/
public function applyConditionWithQuantity($totalOrSubTotalOrPrice, $quantity) {
return $this->applyWithQuantity($totalOrSubTotalOrPrice, $this->getValue(), $quantity);
}
/**
* get the calculated value of this condition supplied by the subtotal|price
*
......@@ -132,32 +150,30 @@ class Condition extends Collection {
// percentage, whether to add or subtract it to the total/subtotal/price
// if we can't find any plus/minus sign, we will assume it as plus sign
if ($this->valueIsPercentage($conditionValue)) {
$value = Helpers::normalizePercentage($this->cleanValue($conditionValue));
$this->parsedRawValue = $totalOrSubTotalOrPrice * ($value / 100);
if ($this->valueIsToBeSubtracted($conditionValue)) {
$result = Helpers::intval($totalOrSubTotalOrPrice - $this->parsedRawValue);
} else {
$result = Helpers::intval($totalOrSubTotalOrPrice + $this->parsedRawValue);
}
}
// if the value has no percent sign on it, the operation will not be a percentage
// next is we will check if it has a minus/plus sign so then we can just deduct it to total/subtotal/price
else {
$this->parsedRawValue = Helpers::normalizePrice($this->cleanValue($conditionValue));
if ($this->valueIsToBeSubtracted($conditionValue)) {
$result = Helpers::intval($totalOrSubTotalOrPrice - $this->parsedRawValue);
} else {
$result = Helpers::intval($totalOrSubTotalOrPrice + $this->parsedRawValue);
}
}
// Do not allow items with negative prices.
return $result < 0 ? 0 : $result;
return $this->valueIsToBeSubtracted($conditionValue)
? Helpers::intval(-$this->parsedRawValue)
: Helpers::intval($this->parsedRawValue);
}
/**
* apply condition with the given quantity
*
* @param $totalOrSubTotalOrPrice
* @param $conditionValue
* @return int
*/
protected function applyWithQuantity($totalOrSubTotalOrPrice, $conditionValue, $quantity=1) {
return $this->apply($totalOrSubTotalOrPrice, $conditionValue) * ($this->getQuantityUndepended() ? 1 : $quantity);
}
/**
......
......@@ -44,6 +44,7 @@ class CurrencyCart extends Cart {
$sum = $this->items()->sum(function (CurrencyItem $item) {
return $item->priceSumWithConditions()->amount();
});
return new Money($sum, $this->currency);
}
......@@ -57,18 +58,14 @@ class CurrencyCart extends Cart {
return $this->subTotal();
$subTotal = $this->subTotal()->amount();
$newTotal = 0;
$process = 0;
$this->getConditions()->each(function ($cond) use ($subTotal, &$newTotal, &$process) {
if ($cond->getTarget() === 'cart') {
($process > 0) ? $toBeCalculated = $newTotal : $toBeCalculated = $subTotal;
$newTotal = $cond->applyCondition($toBeCalculated);
$process++;
}
$condTotal = $this->getConditions()->sum(function ($cond) use ($subTotal) {
return $cond->getTarget() === 'cart'
? $cond->applyCondition($subTotal)
: 0;
});
return new Money((int)$newTotal, $this->currency);
return new Money((int)($subTotal + $condTotal), $this->currency);
}
/**
......
......@@ -23,7 +23,6 @@ class CurrencyCondition extends Condition {
parent::__construct($args);
}
/**
* apply condition
*
......@@ -35,12 +34,22 @@ class CurrencyCondition extends Condition {
if ($conditionValue instanceof Money) {
$this->parsedRawValue = $conditionValue->amount();
$result = Helpers::intval($totalOrSubTotalOrPrice + $this->parsedRawValue);
// Do not allow items with negative prices.
return $result < 0 ? 0 : $result;
return Helpers::intval($this->parsedRawValue);
}
return parent::apply($totalOrSubTotalOrPrice, $conditionValue);
}
/**
* apply condition with the given quantity
*
* @param $totalOrSubTotalOrPrice
* @param $conditionValue
* @return int
*/
protected function applyWithQuantity($totalOrSubTotalOrPrice, $conditionValue, $quantity = 1) {
return $this->getQuantityUndepended()
? $this->apply($totalOrSubTotalOrPrice, $conditionValue)
: $this->apply($totalOrSubTotalOrPrice, $conditionValue) * $quantity;
}
}
\ No newline at end of file
......@@ -52,15 +52,12 @@ class CurrencyItem extends Item {
public function priceWithConditions() {
$originalPrice = $this->price->amount();
$newPrice = 0;
$processed = 0;
if ($this->hasConditions()) {
if (is_array($this->conditions)) {
foreach ($this->conditions as $condition) {
if ($condition->getTarget() === 'item') {
($processed > 0) ? $toBeCalculated = $newPrice : $toBeCalculated = $originalPrice;
$newPrice = $condition->applyCondition($toBeCalculated);
$processed++;
$newPrice += $condition->applyCondition($originalPrice);
}
}
} else {
......@@ -69,7 +66,9 @@ class CurrencyItem extends Item {
}
}
return new Money((int)$newPrice, $this->currency);
$newPrice = (int)($originalPrice + $newPrice);
$newPrice = $newPrice > 0 ? $newPrice : 0;
return new Money($newPrice, $this->currency);
}
return new Money((int)$originalPrice, $this->currency);
}
......@@ -80,7 +79,26 @@ class CurrencyItem extends Item {
* @return Money
*/
public function priceSumWithConditions() {
return $this->priceWithConditions()->multiply($this->quantity);
$originalPrice = $this->price->amount();
$newPrice = 0;
if ($this->hasConditions()) {
if (is_array($this->conditions)) {
foreach ($this->conditions as $condition) {
if ($condition->getTarget() === 'item') {
$newPrice += $condition->applyConditionWithQuantity($originalPrice, $this->quantity);
}
}
} else {
if ($this['conditions']->getTarget() === 'item') {
$newPrice = $this['conditions']->applyConditionWithQuantity($originalPrice, $this->quantity);
}
}
$newPrice += ($this->quantity * $originalPrice);
return new Money((int)($newPrice > 0 ? $newPrice : 0), $this->currency);
}
$m = new Money((int)$originalPrice, $this->currency);
return $m->multiply($this->quantity);
}
}
\ No newline at end of file
......@@ -29,8 +29,11 @@ class Item extends Collection {
public function __construct($attributes) {
// make conditions as array and set target to item if not set
if (isset($attributes['conditions']) && !empty($attributes['conditions'])) {
// force conditions as array
if (!is_array($attributes['conditions']))
$attributes['conditions'] = [$attributes['conditions']];
// check/set the target
collect($attributes['conditions'])->transform(function ($condition) {
if ($condition instanceof Condition) {
if ($condition->getTarget() == 'cart')
......@@ -98,23 +101,17 @@ class Item extends Collection {
public function priceWithConditions() {
$originalPrice = $this->price();
$newPrice = 0;
$processed = 0;
if ($this->hasConditions()) {
if (is_array($this->conditions)) {
foreach ($this->conditions as $condition) {
if ($condition->getTarget() === 'item') {
($processed > 0) ? $toBeCalculated = $newPrice : $toBeCalculated = $originalPrice;
$newPrice = $condition->applyCondition($toBeCalculated);
$processed++;
$newPrice += $condition->applyCondition($originalPrice);
}
}
} else {
if ($this['conditions']->getTarget() === 'item') {
$newPrice = $this['conditions']->applyCondition($originalPrice);
}
}
$newPrice = (int)($originalPrice + $newPrice);
$newPrice = $newPrice > 0 ? $newPrice : 0;
return $newPrice;
}
return $originalPrice;
......@@ -126,7 +123,22 @@ class Item extends Collection {
* @return mixed|null
*/
public function priceSumWithConditions() {
return $this->priceWithConditions() * $this->quantity;
$originalPrice = $this->price();
$newPrice = 0;
if ($this->hasConditions()) {
if (is_array($this->conditions)) {
foreach ($this->conditions as $condition) {
if ($condition->getTarget() === 'item') {
$newPrice += $condition->applyConditionWithQuantity($originalPrice, $this->quantity);
}
}
}
$newPrice += ($this->quantity * $originalPrice);
return $newPrice > 0 ? $newPrice : 0;
}
return $originalPrice * $this->quantity;
}
/**
......
......@@ -814,4 +814,74 @@ class CartConditionTest extends PHPUnit_Framework_TestCase {
$this->assertEquals('2015-01-20', $conditionAttributes['sale_start_date']);
$this->assertEquals('2015-01-30', $conditionAttributes['sale_end_date']);
}
public function test_condition_with_quantity_independend_amount() {
$itemCondition = new Condition(array(
'name' => 'Test Fix 500',
'type' => 'sale',
'target' => 'item',
'quantity_undepended' => true,
'value' => '500'
));
$itemCondition1 = new Condition(array(
'name' => 'Test 10%',
'type' => 'sale',
'target' => 'item',
'quantity_undepended' => true,
'value' => '+10%'
));
$itemCondition2 = new Condition(array(
'name' => 'Test 10%',
'type' => 'sale',
'target' => 'item',
'quantity_undepended' => false,
'value' => '+10%'
));
$itemCondition3 = new Condition(array(
'name' => 'Test Fix 250',
'type' => 'sale',
'target' => 'item',
'quantity_undepended' => false,
'value' => '250'
));
$item = array(
'id' => 456,
'name' => 'Sample Item 1',
'price' => 10000,
'quantity' => 3,
'conditions' => [
$itemCondition,
$itemCondition1,
$itemCondition2,
$itemCondition3
],
);
$this->cart->add($item);
# Fix quantity undependend
$this->assertEquals(500, $itemCondition->applyCondition(0), 'condition');
$this->assertEquals(500, $itemCondition->applyConditionWithQuantity(0, 3), 'Quantitycondition');
$this->assertEquals(500, $itemCondition->applyConditionWithQuantity(1000, 3), 'Quantitycondition +itemAmount');
# % quantity undependend
$this->assertEquals(0, $itemCondition1->applyCondition(0), 'condition');
$this->assertEquals(10, $itemCondition1->applyCondition(100), 'condition');
$this->assertEquals(10, $itemCondition1->applyConditionWithQuantity(100, 3), 'Quantitycondition +itemAmount');
# % with quantity
$this->assertEquals(0, $itemCondition2->applyCondition(0), 'condition');
$this->assertEquals(10, $itemCondition2->applyCondition(100), 'condition');
$this->assertEquals(300, $itemCondition2->applyConditionWithQuantity(1000, 3), 'Quantitycondition +itemAmount');
# Fixed with quantity
$this->assertEquals(250, $itemCondition3->applyCondition(0), 'condition');
$this->assertEquals(750, $itemCondition3->applyConditionWithQuantity(0, 3), 'Quantitycondition');
$this->assertEquals(750, $itemCondition3->applyConditionWithQuantity(1000, 3), 'Quantitycondition +itemAmount');
$this->assertEquals(35250, $this->cart->total(), 'match total');
}
}
......@@ -887,4 +887,72 @@ class CurrencyCartConditionTest extends PHPUnit_Framework_TestCase {
$this->assertEquals(130755, $this->cart->total()->amount(), 'Cart should have a total of 130755');
}
public function test_condition_with_quantity_independend_amount() {
$itemCondition = new CurrencyCondition(array(
'name' => 'Test Fix 500',
'type' => 'sale',
'target' => 'item',
'quantity_undepended' => true,
'value' => new Money(500)
));
$itemCondition1 = new CurrencyCondition(array(
'name' => 'Test 10%',
'type' => 'sale',
'target' => 'item',
'quantity_undepended' => true,
'value' => '+10%'
));
$itemCondition2 = new CurrencyCondition(array(
'name' => 'Test 10%',
'type' => 'sale',
'target' => 'item',
'quantity_undepended' => false,
'value' => '+10%'
));
$itemCondition3 = new CurrencyCondition(array(
'name' => 'Test Fix 250',
'type' => 'sale',
'target' => 'item',
'quantity_undepended' => false,
'value' => new Money(250)
));
$item = array(
'id' => 456,
'name' => 'Sample Item 1',
'price' => new Money(10000),
'quantity' => 3,
'conditions' => [
$itemCondition,
$itemCondition1,
$itemCondition2,
$itemCondition3
],
);
$this->cart->add($item);
# Fix quantity undependend
$this->assertEquals(500, $itemCondition->applyCondition(0), 'condition');
$this->assertEquals(500, $itemCondition->applyConditionWithQuantity(0, 3), 'Quantitycondition');
$this->assertEquals(500, $itemCondition->applyConditionWithQuantity(1000, 3), 'Quantitycondition +itemAmount');
# % quantity undependend
$this->assertEquals(0, $itemCondition1->applyCondition(0), 'condition');
$this->assertEquals(10, $itemCondition1->applyCondition(100), 'condition');
$this->assertEquals(10, $itemCondition1->applyConditionWithQuantity(100, 3), 'Quantitycondition +itemAmount');
# % with quantity
$this->assertEquals(0, $itemCondition2->applyCondition(0), 'condition');
$this->assertEquals(10, $itemCondition2->applyCondition(100), 'condition');
$this->assertEquals(300, $itemCondition2->applyConditionWithQuantity(1000, 3), 'Quantitycondition +itemAmount');
# Fixed with quantity
$this->assertEquals(250, $itemCondition3->applyCondition(0), 'condition');
$this->assertEquals(750, $itemCondition3->applyConditionWithQuantity(0, 3), 'Quantitycondition');
$this->assertEquals(750, $itemCondition3->applyConditionWithQuantity(1000, 3), 'Quantitycondition +itemAmount');
$this->assertEquals(35250, $this->cart->total()->amount(), 'match total');
}
}
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