laravel-shop/tests/Feature/CartAddToCartPoolPricingTes...

1396 lines
50 KiB
PHP
Raw Normal View History

<?php
namespace Blax\Shop\Tests\Feature;
use Blax\Shop\Enums\ProductRelationType;
use Blax\Shop\Enums\ProductType;
2025-12-16 12:58:03 +00:00
use Blax\Shop\Enums\PricingStrategy;
use Blax\Shop\Enums\StockType;
use Blax\Shop\Models\Cart;
use Blax\Shop\Models\Product;
use Blax\Shop\Models\ProductPrice;
use Blax\Shop\Tests\TestCase;
use Carbon\Carbon;
use Workbench\App\Models\User;
class CartAddToCartPoolPricingTest extends TestCase
{
protected User $user;
protected Cart $cart;
protected Product $poolProduct;
protected Product $singleItem1;
protected Product $singleItem2;
protected function setUp(): void
{
parent::setUp();
$this->user = User::factory()->create();
$this->cart = Cart::factory()->create([
'customer_id' => $this->user->id,
'customer_type' => get_class($this->user),
]);
// Create pool product
$this->poolProduct = Product::factory()->create([
'name' => 'Parking Pool',
'type' => ProductType::POOL,
'manage_stock' => false,
]);
// Create single items
$this->singleItem1 = Product::factory()->create([
'name' => 'Parking Spot 1',
'type' => ProductType::BOOKING,
'manage_stock' => true,
]);
$this->singleItem1->increaseStock(1);
$this->singleItem2 = Product::factory()->create([
'name' => 'Parking Spot 2',
'type' => ProductType::BOOKING,
'manage_stock' => true,
]);
$this->singleItem2->increaseStock(1);
// Link single items to pool
$this->poolProduct->productRelations()->attach($this->singleItem1->id, [
'type' => ProductRelationType::SINGLE->value,
]);
$this->poolProduct->productRelations()->attach($this->singleItem2->id, [
'type' => ProductRelationType::SINGLE->value,
]);
}
/** @test */
public function it_adds_pool_with_direct_price_to_cart_without_dates()
{
// Set direct price on pool
ProductPrice::factory()->create([
'purchasable_id' => $this->poolProduct->id,
'purchasable_type' => Product::class,
'unit_amount' => 3000, // 30.00 per day
'currency' => 'USD',
'is_default' => true,
]);
$cartItem = $this->cart->addToCart($this->poolProduct, 1);
$this->assertNotNull($cartItem);
$this->assertEquals($this->poolProduct->id, $cartItem->purchasable_id);
$this->assertEquals(1, $cartItem->quantity);
$this->assertEquals(3000, $cartItem->price); // 30.00 per day × 1 day
$this->assertEquals(3000, $cartItem->subtotal); // 30.00 × 1 quantity
$this->assertNull($cartItem->from);
$this->assertNull($cartItem->until);
}
/** @test */
public function it_adds_pool_with_inherited_price_to_cart_without_dates()
{
// Set prices on single items (20€ and 50€)
ProductPrice::factory()->create([
'purchasable_id' => $this->singleItem1->id,
'purchasable_type' => Product::class,
'unit_amount' => 2000, // 20.00
'currency' => 'USD',
'is_default' => true,
]);
ProductPrice::factory()->create([
'purchasable_id' => $this->singleItem2->id,
'purchasable_type' => Product::class,
'unit_amount' => 5000, // 50.00
'currency' => 'USD',
'is_default' => true,
]);
2025-12-16 12:58:03 +00:00
// Set pricing strategy to average: (2000 + 5000) / 2 = 3500
$this->poolProduct->setPricingStrategy(PricingStrategy::AVERAGE);
// Pool should inherit average: (2000 + 5000) / 2 = 3500
$cartItem = $this->cart->addToCart($this->poolProduct, 1);
$this->assertNotNull($cartItem);
$this->assertEquals($this->poolProduct->id, $cartItem->purchasable_id);
$this->assertEquals(1, $cartItem->quantity);
$this->assertEquals(3500, $cartItem->price); // Average: 35.00
$this->assertEquals(3500, $cartItem->subtotal);
}
/** @test */
public function it_adds_pool_with_direct_price_to_cart_with_booking_dates()
{
// Set direct price on pool
ProductPrice::factory()->create([
'purchasable_id' => $this->poolProduct->id,
'purchasable_type' => Product::class,
'unit_amount' => 3000, // 30.00 per day
'currency' => 'USD',
'is_default' => true,
]);
$from = Carbon::now()->addDays(1)->startOfDay();
$until = Carbon::now()->addDays(4)->startOfDay(); // 3 days
$days = $from->diffInDays($until);
$cartItem = $this->cart->addToCart($this->poolProduct, 1, [], $from, $until);
$this->assertNotNull($cartItem);
$this->assertEquals($this->poolProduct->id, $cartItem->purchasable_id);
$this->assertEquals(1, $cartItem->quantity);
$this->assertEquals(9000, $cartItem->price); // 30.00 × 3 days
$this->assertEquals(9000, $cartItem->subtotal); // 90.00
$this->assertEquals($from->format('Y-m-d H:i:s'), $cartItem->from->format('Y-m-d H:i:s'));
$this->assertEquals($until->format('Y-m-d H:i:s'), $cartItem->until->format('Y-m-d H:i:s'));
}
/** @test */
public function it_adds_pool_with_inherited_price_to_cart_with_booking_dates()
{
// Set prices on single items (20€ and 50€)
ProductPrice::factory()->create([
'purchasable_id' => $this->singleItem1->id,
'purchasable_type' => Product::class,
'unit_amount' => 2000, // 20.00 per day
'currency' => 'USD',
'is_default' => true,
]);
ProductPrice::factory()->create([
'purchasable_id' => $this->singleItem2->id,
'purchasable_type' => Product::class,
'unit_amount' => 5000, // 50.00 per day
'currency' => 'USD',
'is_default' => true,
]);
$from = Carbon::now()->addDays(1)->startOfDay();
$until = Carbon::now()->addDays(3)->startOfDay(); // 2 days
$days = $from->diffInDays($until);
2025-12-16 12:58:03 +00:00
// Set pricing strategy to average: (2000 + 5000) / 2 = 3500 per day
$this->poolProduct->setPricingStrategy(PricingStrategy::AVERAGE);
// Pool inherits average: (2000 + 5000) / 2 = 3500 per day
$cartItem = $this->cart->addToCart($this->poolProduct, 1, [], $from, $until);
$this->assertNotNull($cartItem);
$this->assertEquals($this->poolProduct->id, $cartItem->purchasable_id);
$this->assertEquals(1, $cartItem->quantity);
$this->assertEquals(7000, $cartItem->price); // 35.00 × 2 days
$this->assertEquals(7000, $cartItem->subtotal);
$this->assertEquals($from->format('Y-m-d H:i:s'), $cartItem->from->format('Y-m-d H:i:s'));
$this->assertEquals($until->format('Y-m-d H:i:s'), $cartItem->until->format('Y-m-d H:i:s'));
}
/** @test */
public function it_calculates_price_for_multiple_pool_items_with_booking_dates()
{
// Set direct price on pool
ProductPrice::factory()->create([
'purchasable_id' => $this->poolProduct->id,
'purchasable_type' => Product::class,
'unit_amount' => 2500, // 25.00 per day
'currency' => 'USD',
'is_default' => true,
]);
$from = Carbon::now()->addDays(1)->startOfDay();
$until = Carbon::now()->addDays(6)->startOfDay(); // 5 days
2025-12-19 11:47:55 +00:00
// Adding 2 pool items creates separate cart items (one per single item)
// because each single item has its own stock limit
$cartItem = $this->cart->addToCart($this->poolProduct, 2, [], $from, $until);
2025-12-19 11:47:55 +00:00
// Returns the last cart item created (quantity 1)
$this->assertEquals(1, $cartItem->quantity);
$this->assertEquals(12500, $cartItem->price); // 25.00 × 5 days per unit
2025-12-19 11:47:55 +00:00
$this->assertEquals(12500, $cartItem->subtotal); // 125.00 × 1 quantity
// But total cart should have 2 items with combined subtotal
$this->assertEquals(2, $this->cart->fresh()->items->count());
$this->assertEquals(25000, $this->cart->fresh()->getTotal()); // 125.00 × 2 units = 250.00
}
/** @test */
public function it_uses_lowest_pricing_strategy_with_mixed_single_item_prices()
{
// Set prices on single items (20€ and 50€)
ProductPrice::factory()->create([
'purchasable_id' => $this->singleItem1->id,
'purchasable_type' => Product::class,
'unit_amount' => 2000, // 20.00
'currency' => 'USD',
'is_default' => true,
]);
ProductPrice::factory()->create([
'purchasable_id' => $this->singleItem2->id,
'purchasable_type' => Product::class,
'unit_amount' => 5000, // 50.00
'currency' => 'USD',
'is_default' => true,
]);
// Set pricing strategy to lowest
$this->poolProduct->setPoolPricingStrategy('lowest');
$cartItem = $this->cart->addToCart($this->poolProduct, 1);
$this->assertEquals(2000, $cartItem->price); // Lowest: 20.00
$this->assertEquals(2000, $cartItem->subtotal);
}
/** @test */
public function it_uses_highest_pricing_strategy_with_mixed_single_item_prices()
{
// Set prices on single items (20€ and 50€)
ProductPrice::factory()->create([
'purchasable_id' => $this->singleItem1->id,
'purchasable_type' => Product::class,
'unit_amount' => 2000, // 20.00
'currency' => 'USD',
'is_default' => true,
]);
ProductPrice::factory()->create([
'purchasable_id' => $this->singleItem2->id,
'purchasable_type' => Product::class,
'unit_amount' => 5000, // 50.00
'currency' => 'USD',
'is_default' => true,
]);
// Set pricing strategy to highest
$this->poolProduct->setPoolPricingStrategy('highest');
$cartItem = $this->cart->addToCart($this->poolProduct, 1);
$this->assertEquals(5000, $cartItem->price); // Highest: 50.00
$this->assertEquals(5000, $cartItem->subtotal);
}
/** @test */
public function it_uses_lowest_pricing_strategy_with_booking_dates()
{
// Set prices on single items (20€ and 50€)
ProductPrice::factory()->create([
'purchasable_id' => $this->singleItem1->id,
'purchasable_type' => Product::class,
'unit_amount' => 2000, // 20.00 per day
'currency' => 'USD',
'is_default' => true,
]);
ProductPrice::factory()->create([
'purchasable_id' => $this->singleItem2->id,
'purchasable_type' => Product::class,
'unit_amount' => 5000, // 50.00 per day
'currency' => 'USD',
'is_default' => true,
]);
// Set pricing strategy to lowest
$this->poolProduct->setPoolPricingStrategy('lowest');
$from = Carbon::now()->addDays(1)->startOfDay();
$until = Carbon::now()->addDays(4)->startOfDay(); // 3 days
$cartItem = $this->cart->addToCart($this->poolProduct, 1, [], $from, $until);
$this->assertEquals(6000, $cartItem->price); // 20.00 × 3 days
$this->assertEquals(6000, $cartItem->subtotal);
}
/** @test */
public function it_adds_regular_product_to_cart_without_dates()
{
$regularProduct = Product::factory()->create([
'name' => 'Regular Product',
'type' => ProductType::SIMPLE,
'manage_stock' => true,
]);
$regularProduct->increaseStock(10);
ProductPrice::factory()->create([
'purchasable_id' => $regularProduct->id,
'purchasable_type' => Product::class,
'unit_amount' => 1500, // 15.00
'currency' => 'USD',
'is_default' => true,
]);
$cartItem = $this->cart->addToCart($regularProduct, 2);
$this->assertEquals(2, $cartItem->quantity);
$this->assertEquals(1500, $cartItem->price);
$this->assertEquals(3000, $cartItem->subtotal);
$this->assertNull($cartItem->from);
$this->assertNull($cartItem->until);
}
/** @test */
2025-12-19 11:47:55 +00:00
public function it_creates_separate_items_when_adding_same_pool_product_with_same_dates()
{
ProductPrice::factory()->create([
'purchasable_id' => $this->poolProduct->id,
'purchasable_type' => Product::class,
'unit_amount' => 3000,
'currency' => 'USD',
'is_default' => true,
]);
$from = Carbon::now()->addDays(1)->startOfDay();
$until = Carbon::now()->addDays(3)->startOfDay();
$cartItem1 = $this->cart->addToCart($this->poolProduct, 1, [], $from, $until);
$cartItem2 = $this->cart->addToCart($this->poolProduct, 1, [], $from, $until);
2025-12-19 11:47:55 +00:00
// Items from different single items don't merge, even with same dates
$this->assertNotEquals($cartItem1->id, $cartItem2->id);
$this->assertEquals(1, $cartItem1->quantity);
$this->assertEquals(1, $cartItem2->quantity);
// Both items have the same price since they use pool fallback
$this->assertEquals(6000, $cartItem1->subtotal); // 3000 × 2 days × 1 unit
$this->assertEquals(6000, $cartItem2->subtotal); // 3000 × 2 days × 1 unit
// Total cart subtotal
$this->assertEquals(12000, $this->cart->fresh()->getTotal()); // 6000 × 2 = 12000
}
/** @test */
public function it_creates_separate_cart_items_for_same_pool_with_different_dates()
{
ProductPrice::factory()->create([
'purchasable_id' => $this->poolProduct->id,
'purchasable_type' => Product::class,
'unit_amount' => 3000,
'currency' => 'USD',
'is_default' => true,
]);
$from1 = Carbon::now()->addDays(1)->startOfDay();
$until1 = Carbon::now()->addDays(3)->startOfDay();
$from2 = Carbon::now()->addDays(5)->startOfDay();
$until2 = Carbon::now()->addDays(7)->startOfDay();
$cartItem1 = $this->cart->addToCart($this->poolProduct, 1, [], $from1, $until1);
$cartItem2 = $this->cart->addToCart($this->poolProduct, 1, [], $from2, $until2);
$this->assertNotEquals($cartItem1->id, $cartItem2->id);
$this->assertEquals(1, $cartItem1->quantity);
$this->assertEquals(1, $cartItem2->quantity);
$this->assertEquals(2, $this->cart->items()->count());
}
/** @test */
public function it_calculates_correct_total_for_cart_with_multiple_pool_items()
{
ProductPrice::factory()->create([
'purchasable_id' => $this->poolProduct->id,
'purchasable_type' => Product::class,
'unit_amount' => 2500, // 25.00 per day
'currency' => 'USD',
'is_default' => true,
]);
$from = Carbon::now()->addDays(1)->startOfDay();
$until = Carbon::now()->addDays(4)->startOfDay(); // 3 days
// Add 2 units
$this->cart->addToCart($this->poolProduct, 2, [], $from, $until);
$total = $this->cart->getTotal();
// 25.00 × 3 days × 2 units = 150.00
$this->assertEquals(15000, $total);
}
/** @test */
public function it_handles_pool_with_sale_price()
{
ProductPrice::factory()->create([
'purchasable_id' => $this->poolProduct->id,
'purchasable_type' => Product::class,
'unit_amount' => 5000, // 50.00
'sale_unit_amount' => 3000, // 30.00 (sale)
'currency' => 'USD',
'is_default' => true,
]);
// Set sale period
$this->poolProduct->update([
'sale_start' => now()->subDay(),
'sale_end' => now()->addDay(),
]);
$from = Carbon::now()->addDays(1)->startOfDay();
$until = Carbon::now()->addDays(3)->startOfDay(); // 2 days
$cartItem = $this->cart->addToCart($this->poolProduct, 1, [], $from, $until);
$this->assertEquals(6000, $cartItem->price); // 30.00 × 2 days (sale price)
$this->assertEquals(10000, $cartItem->regular_price); // 50.00 × 2 days (regular price)
}
/** @test */
public function it_handles_pool_with_inherited_sale_price()
{
ProductPrice::factory()->create([
'purchasable_id' => $this->singleItem1->id,
'purchasable_type' => Product::class,
'unit_amount' => 5000,
'sale_unit_amount' => 3000,
'currency' => 'USD',
'is_default' => true,
]);
ProductPrice::factory()->create([
'purchasable_id' => $this->singleItem2->id,
'purchasable_type' => Product::class,
'unit_amount' => 7000,
'sale_unit_amount' => 5000,
'currency' => 'USD',
'is_default' => true,
]);
// Set sale period on single items
$this->singleItem1->update([
'sale_start' => now()->subDay(),
'sale_end' => now()->addDay(),
]);
$this->singleItem2->update([
'sale_start' => now()->subDay(),
'sale_end' => now()->addDay(),
]);
$from = Carbon::now()->addDays(1)->startOfDay();
$until = Carbon::now()->addDays(2)->startOfDay(); // 1 day
2025-12-16 12:58:03 +00:00
// Set pricing strategy to average
$this->poolProduct->setPricingStrategy(PricingStrategy::AVERAGE);
$cartItem = $this->cart->addToCart($this->poolProduct, 1, [], $from, $until);
// Average sale price: (3000 + 5000) / 2 = 4000 per day
$this->assertEquals(4000, $cartItem->price);
// Average regular price: (5000 + 7000) / 2 = 6000 per day
$this->assertEquals(6000, $cartItem->regular_price);
}
/** @test */
public function it_handles_zero_days_as_one_day_minimum()
{
ProductPrice::factory()->create([
'purchasable_id' => $this->poolProduct->id,
'purchasable_type' => Product::class,
2025-12-17 16:57:17 +00:00
'unit_amount' => 30,
'currency' => 'USD',
'is_default' => true,
]);
2025-12-17 16:57:17 +00:00
// Same day booking (4 hours)
$from = Carbon::now()->addDays(1)->setTime(10, 0);
$until = Carbon::now()->addDays(1)->setTime(14, 0);
$cartItem = $this->cart->addToCart($this->poolProduct, 1, [], $from, $until);
2025-12-17 16:57:17 +00:00
// 4 hours = 0.1667 days, 30 * 0.1667 = 5.00 (rounded to 2 decimals)
$this->assertEquals('5.00', $cartItem->price);
}
/** @test */
public function it_throws_exception_when_adding_pool_without_any_pricing()
{
// Pool with no direct price and single items with no prices
$pool = Product::factory()->create([
'name' => 'Pool Without Pricing',
'type' => ProductType::POOL,
'manage_stock' => false,
]);
$spot = Product::factory()->create([
'name' => 'Spot Without Price',
'type' => ProductType::BOOKING,
'manage_stock' => true,
]);
$spot->increaseStock(1);
$pool->productRelations()->attach($spot->id, [
'type' => ProductRelationType::SINGLE->value,
]);
$this->expectException(\Blax\Shop\Exceptions\HasNoPriceException::class);
$this->cart->addToCart($pool, 1);
}
/** @test */
public function it_throws_exception_when_pool_not_available_for_booking_period()
{
ProductPrice::factory()->create([
'purchasable_id' => $this->poolProduct->id,
'purchasable_type' => Product::class,
'unit_amount' => 3000,
'currency' => 'USD',
'is_default' => true,
]);
$from = Carbon::now()->addDays(1)->startOfDay();
$until = Carbon::now()->addDays(3)->startOfDay();
// Claim all single items for the period
$this->singleItem1->claimStock(1, null, $from, $until);
$this->singleItem2->claimStock(1, null, $from, $until);
// Try to add pool for same period
$this->expectException(\Blax\Shop\Exceptions\NotEnoughStockException::class);
$this->expectExceptionMessage('has only 0 items available');
$this->cart->addToCart($this->poolProduct, 1, [], $from, $until);
}
/** @test */
public function it_throws_exception_when_booking_product_not_available_for_period()
{
$bookingProduct = Product::factory()->create([
'name' => 'Meeting Room',
'type' => ProductType::BOOKING,
'manage_stock' => true,
]);
$bookingProduct->increaseStock(1);
ProductPrice::factory()->create([
'purchasable_id' => $bookingProduct->id,
'purchasable_type' => Product::class,
'unit_amount' => 5000,
'currency' => 'USD',
'is_default' => true,
]);
$from = Carbon::now()->addDays(1)->startOfDay();
$until = Carbon::now()->addDays(3)->startOfDay();
// Claim the booking product for the period
$bookingProduct->claimStock(1, null, $from, $until);
// Try to add for overlapping period
$this->expectException(\Blax\Shop\Exceptions\NotEnoughStockException::class);
$this->expectExceptionMessage('not available for the requested period');
$this->cart->addToCart($bookingProduct, 1, [], $from, $until);
}
/** @test */
public function it_throws_exception_when_only_from_date_provided()
{
ProductPrice::factory()->create([
'purchasable_id' => $this->poolProduct->id,
'purchasable_type' => Product::class,
'unit_amount' => 3000,
'currency' => 'USD',
'is_default' => true,
]);
$from = Carbon::now()->addDays(1);
$this->expectException(\Exception::class);
$this->expectExceptionMessage("Both 'from' and 'until' dates must be provided together");
$this->cart->addToCart($this->poolProduct, 1, [], $from, null);
}
/** @test */
public function it_throws_exception_when_only_until_date_provided()
{
ProductPrice::factory()->create([
'purchasable_id' => $this->poolProduct->id,
'purchasable_type' => Product::class,
'unit_amount' => 3000,
'currency' => 'USD',
'is_default' => true,
]);
$until = Carbon::now()->addDays(3);
$this->expectException(\Exception::class);
$this->expectExceptionMessage("Both 'from' and 'until' dates must be provided together");
$this->cart->addToCart($this->poolProduct, 1, [], null, $until);
}
/** @test */
public function it_throws_exception_when_from_is_after_until()
{
ProductPrice::factory()->create([
'purchasable_id' => $this->poolProduct->id,
'purchasable_type' => Product::class,
'unit_amount' => 3000,
'currency' => 'USD',
'is_default' => true,
]);
$from = Carbon::now()->addDays(5);
$until = Carbon::now()->addDays(2); // Before from
$this->expectException(\Exception::class);
$this->expectExceptionMessage("'from' date must be before the 'until' date");
$this->cart->addToCart($this->poolProduct, 1, [], $from, $until);
}
/** @test */
public function it_throws_exception_when_from_equals_until()
{
ProductPrice::factory()->create([
'purchasable_id' => $this->poolProduct->id,
'purchasable_type' => Product::class,
'unit_amount' => 3000,
'currency' => 'USD',
'is_default' => true,
]);
$date = Carbon::now()->addDays(1);
$this->expectException(\Exception::class);
$this->expectExceptionMessage("'from' date must be before the 'until' date");
$this->cart->addToCart($this->poolProduct, 1, [], $date, $date);
}
/** @test */
public function it_creates_separate_items_for_same_product_same_dates_different_parameters()
{
ProductPrice::factory()->create([
'purchasable_id' => $this->poolProduct->id,
'purchasable_type' => Product::class,
'unit_amount' => 3000,
'currency' => 'USD',
'is_default' => true,
]);
$from = Carbon::now()->addDays(1);
$until = Carbon::now()->addDays(3);
$cartItem1 = $this->cart->addToCart($this->poolProduct, 1, ['zone' => 'A'], $from, $until);
$cartItem2 = $this->cart->addToCart($this->poolProduct, 1, ['zone' => 'B'], $from, $until);
$this->assertNotEquals($cartItem1->id, $cartItem2->id);
$this->assertEquals(2, $this->cart->items()->count());
$this->assertEquals(['zone' => 'A'], $cartItem1->parameters);
$this->assertEquals(['zone' => 'B'], $cartItem2->parameters);
}
/** @test */
public function it_throws_exception_when_pool_quantity_exceeds_available_items()
{
ProductPrice::factory()->create([
'purchasable_id' => $this->poolProduct->id,
'purchasable_type' => Product::class,
'unit_amount' => 3000,
'currency' => 'USD',
'is_default' => true,
]);
$from = Carbon::now()->addDays(1);
$until = Carbon::now()->addDays(3);
// Pool has 2 single items, try to add 3 (with dates to check availability)
$this->expectException(\Blax\Shop\Exceptions\NotEnoughStockException::class);
$this->expectExceptionMessage('has only 2 items available');
$this->cart->addToCart($this->poolProduct, 3, [], $from, $until);
}
/** @test */
public function it_handles_partial_pool_availability()
{
ProductPrice::factory()->create([
'purchasable_id' => $this->poolProduct->id,
'purchasable_type' => Product::class,
'unit_amount' => 3000,
'currency' => 'USD',
'is_default' => true,
]);
$from = Carbon::now()->addDays(1);
$until = Carbon::now()->addDays(3);
// Claim one of the two single items
$this->singleItem1->claimStock(1, null, $from, $until);
// Should be able to add 1 (one spot still available)
$cartItem = $this->cart->addToCart($this->poolProduct, 1, [], $from, $until);
$this->assertNotNull($cartItem);
// But not 2
$this->expectException(\Blax\Shop\Exceptions\NotEnoughStockException::class);
$this->cart->addToCart($this->poolProduct, 2, [], $from, $until);
}
/** @test */
public function it_throws_exception_for_regular_product_without_price()
{
$regularProduct = Product::factory()->create([
'name' => 'Product Without Price',
'type' => ProductType::SIMPLE,
'manage_stock' => true,
]);
$regularProduct->increaseStock(10);
$this->expectException(\Blax\Shop\Exceptions\HasNoPriceException::class);
$this->cart->addToCart($regularProduct, 1);
}
/** @test */
public function it_allows_adding_booking_product_without_dates()
{
$bookingProduct = Product::factory()->create([
'name' => 'Meeting Room',
'type' => ProductType::BOOKING,
'manage_stock' => true,
]);
$bookingProduct->increaseStock(5);
ProductPrice::factory()->create([
'purchasable_id' => $bookingProduct->id,
'purchasable_type' => Product::class,
'unit_amount' => 5000,
'currency' => 'USD',
'is_default' => true,
]);
// Should be able to add without dates
$cartItem = $this->cart->addToCart($bookingProduct, 1);
$this->assertNotNull($cartItem);
$this->assertEquals($bookingProduct->id, $cartItem->purchasable_id);
$this->assertNull($cartItem->from);
$this->assertNull($cartItem->until);
$this->assertEquals(5000, $cartItem->price); // 1 day default
}
/** @test */
public function it_allows_adding_pool_product_without_dates()
{
ProductPrice::factory()->create([
'purchasable_id' => $this->poolProduct->id,
'purchasable_type' => Product::class,
'unit_amount' => 3000,
'currency' => 'USD',
'is_default' => true,
]);
// Should be able to add without dates
$cartItem = $this->cart->addToCart($this->poolProduct, 1);
$this->assertNotNull($cartItem);
$this->assertEquals($this->poolProduct->id, $cartItem->purchasable_id);
$this->assertNull($cartItem->from);
$this->assertNull($cartItem->until);
$this->assertEquals(3000, $cartItem->price); // 1 day default
}
/** @test */
public function it_allows_updating_cart_item_dates_later()
{
ProductPrice::factory()->create([
'purchasable_id' => $this->poolProduct->id,
'purchasable_type' => Product::class,
'unit_amount' => 3000,
'currency' => 'USD',
'is_default' => true,
]);
// Add without dates
$cartItem = $this->cart->addToCart($this->poolProduct, 1);
$this->assertNull($cartItem->from);
$this->assertNull($cartItem->until);
// Update with dates
$from = Carbon::now()->addDays(1);
$until = Carbon::now()->addDays(3);
$cartItem->update([
'from' => $from,
'until' => $until,
'price' => 3000 * 2, // 2 days
'subtotal' => 3000 * 2 * 1, // 2 days × 1 quantity
]);
$cartItem->refresh();
$this->assertEquals($from->format('Y-m-d H:i:s'), $cartItem->from->format('Y-m-d H:i:s'));
$this->assertEquals($until->format('Y-m-d H:i:s'), $cartItem->until->format('Y-m-d H:i:s'));
$this->assertEquals(6000, $cartItem->price);
}
/** @test */
public function it_limits_pool_in_cart_quantity_by_single_products()
{
// Set price on pool
ProductPrice::factory()->create([
'purchasable_id' => $this->poolProduct->id,
'purchasable_type' => Product::class,
'unit_amount' => 3000,
'currency' => 'USD',
'is_default' => true,
]);
// Assert cart is empty
$this->assertEquals(0, $this->cart->items()->count());
// Assert poolProduct has quantity availability of 2 (based on 2 single items)
$availableQuantity = $this->poolProduct->getAvailableQuantity();
$this->assertEquals(2, $availableQuantity);
2025-12-20 10:22:04 +00:00
// Set booking dates for the test
$from = now()->addDays(1);
$until = now()->addDays(3);
2025-12-19 11:47:55 +00:00
// Adding 2 pool items creates 2 cart items (one per single item)
2025-12-20 10:22:04 +00:00
$cartItem = $this->cart->addToCart($this->poolProduct, 2, [], $from, $until);
$this->assertNotNull($cartItem);
2025-12-19 11:47:55 +00:00
// Returns the last cart item (quantity 1)
$this->assertEquals(1, $cartItem->quantity);
// But total items should be 2
$this->assertEquals(2, $this->cart->fresh()->items->sum('quantity'));
2025-12-20 10:22:04 +00:00
// Try to add 1 more with dates (total would be 3, but only 2 available)
$this->expectException(\Blax\Shop\Exceptions\NotEnoughStockException::class);
2025-12-20 10:22:04 +00:00
$this->expectExceptionMessage('has only 0 items available'); // 2 total - 2 in cart = 0 remaining
$this->cart->addToCart($this->poolProduct, 1, [], $from, $until);
}
/** @test */
public function it_counts_single_item_stock_quantities_in_pool_availability()
{
// Create a pool with multiple single items having different stock quantities
$pool = Product::factory()->create([
'name' => 'Large Parking Pool',
'type' => ProductType::POOL,
'manage_stock' => false,
]);
// Create single items with different stock quantities
$spot1 = Product::factory()->create([
'name' => 'Standard Spot',
'type' => ProductType::BOOKING,
'manage_stock' => true,
]);
$spot1->increaseStock(5); // 5 units available
$spot2 = Product::factory()->create([
'name' => 'Premium Spot',
'type' => ProductType::BOOKING,
'manage_stock' => true,
]);
$spot2->increaseStock(3); // 3 units available
$spot3 = Product::factory()->create([
'name' => 'VIP Spot',
'type' => ProductType::BOOKING,
'manage_stock' => true,
]);
$spot3->increaseStock(2); // 2 units available
// Attach single items to pool
$pool->attachSingleItems([$spot1->id, $spot2->id, $spot3->id]);
// Pool should have availability = 5 + 3 + 2 = 10
$availableQuantity = $pool->getAvailableQuantity();
$this->assertEquals(10, $availableQuantity);
// Set price on pool
ProductPrice::factory()->create([
'purchasable_id' => $pool->id,
'purchasable_type' => Product::class,
'unit_amount' => 3000,
'currency' => 'USD',
'is_default' => true,
]);
$cart = Cart::factory()->create([
'customer_id' => $this->user->id,
'customer_type' => get_class($this->user),
]);
2025-12-20 10:22:04 +00:00
// Set dates for validation
$from = now()->addDays(1);
$until = now()->addDays(3);
2025-12-19 11:47:55 +00:00
// Adding 10 pool items creates multiple cart items (grouped by single item)
// Since each single item stock is counted as 5+3+2=10
2025-12-20 10:22:04 +00:00
$cartItem = $cart->addToCart($pool, 10, [], $from, $until);
$this->assertNotNull($cartItem);
2025-12-19 11:47:55 +00:00
// Returns the last cart item (from VIP Spot with 2 stock)
$this->assertEquals(2, $cartItem->quantity);
// But total items in cart should sum to 10
$this->assertEquals(10, $cart->fresh()->items->sum('quantity'));
2025-12-20 10:22:04 +00:00
// But not 11 - with dates for validation
$this->expectException(\Blax\Shop\Exceptions\NotEnoughStockException::class);
2025-12-20 10:22:04 +00:00
$this->expectExceptionMessage('has only 0 items available'); // 10 total - 10 in cart = 0 remaining
$cart->addToCart($pool, 1, [], $from, $until);
}
/** @test */
public function it_counts_available_stock_with_booking_dates()
{
// Create pool with single items having stock
$pool = Product::factory()->create([
'name' => 'Conference Room Pool',
'type' => ProductType::POOL,
'manage_stock' => false,
]);
$room1 = Product::factory()->create([
'name' => 'Room A',
'type' => ProductType::BOOKING,
'manage_stock' => true,
]);
$room1->increaseStock(3);
$room2 = Product::factory()->create([
'name' => 'Room B',
'type' => ProductType::BOOKING,
'manage_stock' => true,
]);
$room2->increaseStock(2);
$pool->attachSingleItems([$room1->id, $room2->id]);
ProductPrice::factory()->create([
'purchasable_id' => $pool->id,
'purchasable_type' => Product::class,
'unit_amount' => 5000,
'currency' => 'USD',
'is_default' => true,
]);
$from = Carbon::now()->addDays(1)->startOfDay();
$until = Carbon::now()->addDays(2)->startOfDay();
// Total availability should be 3 + 2 = 5
$this->assertEquals(5, $pool->getAvailableQuantity($from, $until));
$cart = Cart::factory()->create([
'customer_id' => $this->user->id,
'customer_type' => get_class($this->user),
]);
2025-12-19 11:47:55 +00:00
// Adding 5 pool items creates multiple cart items (grouped by single item)
$cartItem = $cart->addToCart($pool, 5, [], $from, $until);
$this->assertNotNull($cartItem);
2025-12-19 11:47:55 +00:00
// Returns the last cart item (from Room B with 2 stock)
$this->assertEquals(2, $cartItem->quantity);
// But total items in cart should sum to 5
$this->assertEquals(5, $cart->fresh()->items->sum('quantity'));
}
/** @test */
public function it_allows_unlimited_pool_when_single_items_dont_manage_stock()
{
// Create pool with single items that don't manage stock
$pool = Product::factory()->create([
'name' => 'Unlimited Parking Pool',
'type' => ProductType::POOL,
'manage_stock' => false,
]);
$spot1 = Product::factory()->create([
'name' => 'Unlimited Spot 1',
'type' => ProductType::BOOKING,
'manage_stock' => false, // No stock management
]);
$spot2 = Product::factory()->create([
'name' => 'Unlimited Spot 2',
'type' => ProductType::BOOKING,
'manage_stock' => false, // No stock management
]);
$pool->attachSingleItems([$spot1->id, $spot2->id]);
ProductPrice::factory()->create([
'purchasable_id' => $pool->id,
'purchasable_type' => Product::class,
'unit_amount' => 3000,
'currency' => 'USD',
'is_default' => true,
]);
// Pool should have unlimited availability
$this->assertEquals(PHP_INT_MAX, $pool->getAvailableQuantity());
$cart = Cart::factory()->create([
'customer_id' => $this->user->id,
'customer_type' => get_class($this->user),
]);
// Should be able to add any quantity without dates
$cartItem = $cart->addToCart($pool, 1000);
$this->assertNotNull($cartItem);
$this->assertEquals(1000, $cartItem->quantity);
// And with dates
$from = Carbon::now()->addDays(1)->startOfDay();
$until = Carbon::now()->addDays(2)->startOfDay();
$cartItem2 = $cart->addToCart($pool, 500, [], $from, $until);
$this->assertNotNull($cartItem2);
$this->assertEquals(500, $cartItem2->quantity);
}
2025-12-16 12:58:03 +00:00
/** @test */
public function it_picks_correct_price_for_pool_and_items_and_respects_stocks()
{
$this->actingAs($this->user);
$pool = Product::factory()
->withPrices(1, 5000) // 50€
->create([
'name' => 'Parking Pool',
'type' => ProductType::POOL
]);
$spot1 = Product::factory()
->withStocks(2)
->withPrices(1, 2000) // 20€
->create([
'name' => 'Spot 1',
'type' => ProductType::BOOKING,
]);
$spot2 = Product::factory()
->withStocks(2)
->create([
'name' => 'Spot 2',
'type' => ProductType::BOOKING,
]);
$spot3 = Product::factory()
->withStocks(2)
->withPrices(1, 8000) // 80€
->create([
'name' => 'Spot 3',
'type' => ProductType::BOOKING,
]);
$pool->attachSingleItems([
$spot1->id,
$spot2->id,
$spot3->id
]);
2025-12-20 10:22:04 +00:00
// Pool should have availability of 6
2025-12-16 12:58:03 +00:00
$this->assertEquals(6, $pool->getAvailableQuantity());
$pool->setPoolPricingStrategy('lowest');
$cart = $this->user->currentCart();
$this->assertEquals(0, $cart->items()->count());
2025-12-20 10:22:04 +00:00
// Set dates for booking to test progressive pricing with date-aware allocation
$from = now()->addDays(1);
$until = now()->addDays(3);
// With flexible cart: adding with dates validates stock
2025-12-16 12:58:03 +00:00
$this->assertThrows(
2025-12-20 10:22:04 +00:00
fn() => $cartItem = $cart->addToCart($pool, 1000, [], $from, $until),
2025-12-16 12:58:03 +00:00
\Blax\Shop\Exceptions\NotEnoughStockException::class
);
2025-12-20 10:22:04 +00:00
// 1. Addition - with dates for proper allocation
// Price per day: 20.00, Booking: 2 days, Total: 40.00
$this->assertEquals(2000, $pool->getCurrentPrice(cart: $cart, from: $from, until: $until)); // 20.00/day
$this->assertEquals(2000, $pool->getLowestAvailablePoolPrice($from, $until)); // 20.00/day
$this->assertEquals(8000, $pool->getHighestAvailablePoolPrice($from, $until)); // 80.00/day
$cartItem = $cart->addToCart($pool, 1, [], $from, $until);
2025-12-16 12:58:03 +00:00
$this->assertNotNull($cartItem);
2025-12-20 10:22:04 +00:00
$this->assertEquals(4000, $cartItem->price); // 20.00/day × 2 days = 40.00
$this->assertEquals(4000, $cartItem->subtotal); // 40.00 × 1
2025-12-16 12:58:03 +00:00
2025-12-20 10:22:04 +00:00
// 2. Addition - should merge with 1st item (same single, same price, same dates)
// Price per day: 20.00, Booking: 2 days, Total: 40.00
$this->assertEquals(2000, $pool->getCurrentPrice(from: $from, until: $until)); // 20.00/day
$this->assertEquals(2000, $pool->getLowestAvailablePoolPrice($from, $until)); // 20.00/day
$this->assertEquals(8000, $pool->getHighestAvailablePoolPrice($from, $until)); // 80.00/day
$cartItem = $cart->addToCart($pool, 1, [], $from, $until);
2025-12-16 12:58:03 +00:00
$this->assertNotNull($cartItem);
2025-12-20 10:22:04 +00:00
// Merges with 1st item: quantity becomes 2, subtotal becomes 80.00
$this->assertEquals(2, $cartItem->quantity);
$this->assertEquals(8000, $cartItem->subtotal); // 40.00 × 2
2025-12-16 12:58:03 +00:00
2025-12-20 10:22:04 +00:00
// 3. Addition - first Spot1 unit exhausted, moves to second Spot1 unit
// Both units of Spot1 now have 1 item each, so next item goes to Spot2
// Price per day: 50.00 (Spot2 inherits from pool), Booking: 2 days, Total: 100.00
$this->assertEquals(5000, $pool->getCurrentPrice(cart: $cart, from: $from, until: $until)); // 50.00/day
$this->assertEquals(5000, $pool->getLowestAvailablePoolPrice($from, $until)); // 50.00/day
$this->assertEquals(8000, $pool->getHighestAvailablePoolPrice($from, $until)); // 80.00/day
$cartItem = $cart->addToCart($pool, 1, [], $from, $until);
2025-12-16 12:58:03 +00:00
$this->assertNotNull($cartItem);
2025-12-20 10:22:04 +00:00
$this->assertEquals(10000, $cartItem->price); // 50.00/day × 2 days = 100.00
$this->assertEquals(10000, $cartItem->subtotal); // 100.00 × 1
2025-12-16 12:58:03 +00:00
2025-12-20 10:22:04 +00:00
// 4. Addition - merges with 3rd item (same Spot2, same price, same dates)
// Price per day: 50.00, Booking: 2 days, Total: 100.00
$this->assertEquals(5000, $pool->getCurrentPrice(from: $from, until: $until)); // 50.00/day
$this->assertEquals(5000, $pool->getLowestAvailablePoolPrice($from, $until)); // 50.00/day
$this->assertEquals(8000, $pool->getHighestAvailablePoolPrice($from, $until)); // 80.00/day
$cartItem = $cart->addToCart($pool, 1, [], $from, $until);
2025-12-16 12:58:03 +00:00
$this->assertNotNull($cartItem);
2025-12-20 10:22:04 +00:00
$this->assertEquals(2, $cartItem->quantity);
$this->assertEquals(20000, $cartItem->subtotal); // 100.00 × 2
2025-12-16 12:58:03 +00:00
2025-12-20 10:22:04 +00:00
// 5. Addition - Spot1 and Spot2 both exhausted, moves to Spot3
// Price per day: 80.00, Booking: 2 days, Total: 160.00
$this->assertEquals(8000, $pool->getCurrentPrice(cart: $cart, from: $from, until: $until)); // 80.00/day
$this->assertEquals(8000, $pool->getLowestAvailablePoolPrice($from, $until)); // 80.00/day
$this->assertEquals(8000, $pool->getHighestAvailablePoolPrice($from, $until)); // 80.00/day
$cartItem = $cart->addToCart($pool, 1, [], $from, $until);
2025-12-16 12:58:03 +00:00
$this->assertNotNull($cartItem);
2025-12-20 10:22:04 +00:00
$this->assertEquals(16000, $cartItem->price); // 80.00/day × 2 days = 160.00
$this->assertEquals(16000, $cartItem->subtotal); // 160.00 × 1
2025-12-16 12:58:03 +00:00
2025-12-20 10:22:04 +00:00
// 6. Addition - merges with 5th item (same Spot3, same price, same dates)
// Price per day: 80.00, Booking: 2 days, Total: 160.00
$this->assertEquals(8000, $pool->getCurrentPrice(from: $from, until: $until)); // 80.00/day
$this->assertEquals(8000, $pool->getLowestAvailablePoolPrice($from, $until)); // 80.00/day
$this->assertEquals(8000, $pool->getHighestAvailablePoolPrice($from, $until)); // 80.00/day
$cartItem = $cart->addToCart($pool, 1, [], $from, $until);
2025-12-16 12:58:03 +00:00
$this->assertNotNull($cartItem);
2025-12-20 10:22:04 +00:00
$this->assertEquals(2, $cartItem->quantity);
$this->assertEquals(32000, $cartItem->subtotal); // 160.00 × 2
2025-12-16 12:58:03 +00:00
$this->assertEquals(3, $cart->items()->count());
2025-12-20 10:22:04 +00:00
$this->assertNull($pool->getCurrentPrice(from: $from, until: $until));
$this->assertNull($pool->getLowestAvailablePoolPrice($from, $until));
$this->assertNull($pool->getHighestAvailablePoolPrice($from, $until));
$this->assertNull($pool->getCurrentPrice(cart: $cart, from: $from, until: $until));
$this->assertNull($pool->getLowestAvailablePoolPrice($from, $until));
$this->assertNull($pool->getHighestAvailablePoolPrice($from, $until));
2025-12-16 12:58:03 +00:00
2025-12-20 10:22:04 +00:00
// 7. Addition - should fail because all 6 items are allocated for this period
2025-12-16 12:58:03 +00:00
$this->assertThrows(
2025-12-20 10:22:04 +00:00
fn() => $cart->addToCart($pool, 1, [], $from, $until),
2025-12-16 12:58:03 +00:00
\Blax\Shop\Exceptions\NotEnoughStockException::class
);
}
/** @test */
public function it_picks_correct_price_respects_stocks_respects_timespan_for_price()
{
$this->actingAs($this->user);
$pool = Product::factory()
->withPrices(1, 5000) // 50€
->create([
'name' => 'Parking Pool',
'type' => ProductType::POOL
]);
$spot1 = Product::factory()
->withStocks(2)
->withPrices(1, 2000) // 20€
->create([
'name' => 'Spot 1',
'type' => ProductType::BOOKING,
]);
$spot2 = Product::factory()
->withStocks(2)
->create([
'name' => 'Spot 2',
'type' => ProductType::BOOKING,
]);
$spot3 = Product::factory()
->withStocks(2)
->withPrices(1, 8000) // 80€
->create([
'name' => 'Spot 3',
'type' => ProductType::BOOKING,
]);
$pool->attachSingleItems([
$spot1->id,
$spot2->id,
$spot3->id
]);
2025-12-17 09:41:52 +00:00
$from = now()->addWeek();
$until = now()->addWeek()->addDays(5); // 5 days
2025-12-20 10:22:04 +00:00
// Pool should have availability of 6
2025-12-16 12:58:03 +00:00
$this->assertEquals(6, $pool->getAvailableQuantity());
$pool->setPoolPricingStrategy('lowest');
$cart = $this->user->currentCart();
$this->assertEquals(0, $cart->items()->count());
2025-12-20 10:22:04 +00:00
// With flexible cart: adding without dates is allowed, but with dates stock is validated
2025-12-16 12:58:03 +00:00
$this->assertThrows(
2025-12-20 10:22:04 +00:00
fn() => $cartItem = $cart->addToCart($pool, 1000, [], $from, $until),
2025-12-16 12:58:03 +00:00
\Blax\Shop\Exceptions\NotEnoughStockException::class
);
$cart->addToCart(
$pool,
3,
[],
2025-12-17 09:41:52 +00:00
$from,
$until
2025-12-16 12:58:03 +00:00
);
2025-12-17 16:57:17 +00:00
$this->assertEqualsWithDelta(
2025-12-16 12:58:03 +00:00
(2000 * 2 * 5) + (5000 * 1 * 5),
2025-12-17 16:57:17 +00:00
$cart->getTotal(),
0.01 // Allow 1 cent tolerance for floating point errors
2025-12-16 12:58:03 +00:00
);
2025-12-17 09:41:52 +00:00
$this->assertEquals(
5000,
$pool->getCurrentPrice()
);
2025-12-16 12:58:03 +00:00
$cart->addToCart(
$pool,
3,
[],
2025-12-17 09:41:52 +00:00
$from,
$until
2025-12-16 12:58:03 +00:00
);
2025-12-17 16:57:17 +00:00
$this->assertEqualsWithDelta(
2025-12-16 12:58:03 +00:00
(2000 * 2 * 5) + (5000 * 2 * 5) + (8000 * 2 * 5),
2025-12-17 16:57:17 +00:00
$cart->getTotal(),
0.01
2025-12-16 12:58:03 +00:00
);
2025-12-17 09:41:52 +00:00
$this->assertNull($pool->getCurrentPrice());
2025-12-16 12:58:03 +00:00
$this->assertEquals(3, $cart->items()->count());
// Clear cart
$cart->items()->delete();
$this->assertEquals(0, $cart->items()->count());
$this->assertEquals(0, $cart->getTotal());
// Make one spot unavailable for part of the period
$spot2->adjustStock(
StockType::CLAIMED,
1,
from: now()->addWeek()->addDays(2),
until: now()->addWeek()->addDays(3)
);
$this->assertThrows(
fn() => $cart->addToCart(
$pool,
6,
[],
2025-12-17 09:41:52 +00:00
$from,
$until
2025-12-16 12:58:03 +00:00
),
\Blax\Shop\Exceptions\NotEnoughStockException::class
);
$cart->addToCart(
$pool,
5,
[],
2025-12-17 09:41:52 +00:00
$from,
$until
);
2025-12-17 16:57:17 +00:00
$this->assertEqualsWithDelta(
2025-12-17 09:41:52 +00:00
(2000 * 2 * 5) + (5000 * 1 * 5) + (8000 * 2 * 5),
2025-12-17 16:57:17 +00:00
$cart->getTotal(),
0.01
2025-12-17 09:41:52 +00:00
);
$cart->removeFromCart($pool, 1);
2025-12-17 16:57:17 +00:00
$this->assertEqualsWithDelta(
2025-12-17 09:41:52 +00:00
(2000 * 2 * 5) + (5000 * 1 * 5) + (8000 * 1 * 5),
2025-12-17 16:57:17 +00:00
$cart->getTotal(),
0.01
2025-12-17 09:41:52 +00:00
);
$this->assertEquals(8000, $pool->getCurrentPrice());
$cart->removeFromCart($pool, 1);
2025-12-17 16:57:17 +00:00
$this->assertEqualsWithDelta(
2025-12-17 09:41:52 +00:00
(2000 * 2 * 5) + (5000 * 1 * 5),
2025-12-17 16:57:17 +00:00
$cart->getTotal(),
0.01
2025-12-17 09:41:52 +00:00
);
// Get cart item with price 2000
$cartItem = $cart->items()
->orderBy('price', 'asc')
->first();
$cart->removeFromCart($cartItem, 1);
2025-12-17 16:57:17 +00:00
$this->assertEqualsWithDelta(
2025-12-17 09:41:52 +00:00
(2000 * 1 * 5) + (5000 * 1 * 5),
2025-12-17 16:57:17 +00:00
$cart->getTotal(),
0.01
2025-12-17 09:41:52 +00:00
);
$this->assertEquals(
2000,
$pool->getCurrentPrice()
);
$cart->addToCart(
$pool,
1,
[],
$from,
$until
);
2025-12-17 16:57:17 +00:00
$this->assertEqualsWithDelta(
2025-12-17 09:41:52 +00:00
(2000 * 2 * 5) + (5000 * 1 * 5),
2025-12-17 16:57:17 +00:00
$cart->getTotal(),
0.01
2025-12-17 09:41:52 +00:00
);
// Get cart item with price 2000
$cartItem = $cart->items()
->orderBy('price', 'asc')
->first();
$cart->removeFromCart($cartItem, 2);
2025-12-17 16:57:17 +00:00
$this->assertEqualsWithDelta(
2025-12-17 09:41:52 +00:00
(5000 * 1 * 5),
2025-12-17 16:57:17 +00:00
$cart->getTotal(),
0.01
2025-12-17 09:41:52 +00:00
);
$this->assertEquals(2000, $pool->getCurrentPrice());
$cart->addToCart(
$pool,
4,
[],
$from,
$until
2025-12-16 12:58:03 +00:00
);
2025-12-17 16:57:17 +00:00
$this->assertEqualsWithDelta(
2025-12-16 12:58:03 +00:00
(2000 * 2 * 5) + (5000 * 1 * 5) + (8000 * 2 * 5),
2025-12-17 16:57:17 +00:00
$cart->getTotal(),
0.01
2025-12-16 12:58:03 +00:00
);
}
}