2025-12-20 11:19:34 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace Blax\Shop\Tests\Feature;
|
|
|
|
|
|
|
|
|
|
use Blax\Shop\Enums\ProductType;
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Tests for cart item validation when dates change and items become unavailable.
|
|
|
|
|
*
|
|
|
|
|
* Bug: When adjusting dates in cart, some cart items show null/0 price because they
|
|
|
|
|
* are not available for the new dates. But IsReadyToCheckout incorrectly returns true.
|
|
|
|
|
*
|
|
|
|
|
* Expected behavior:
|
|
|
|
|
* - setDates() should NOT throw - it should allow users to fiddle with dates
|
|
|
|
|
* - Items that become unavailable should have price = null
|
|
|
|
|
* - Items with null price should NOT be ready for checkout
|
|
|
|
|
* - Cart.isReadyForCheckout() should return false if any items are unavailable
|
|
|
|
|
* - Exception should only be thrown at checkout time, not when changing dates
|
|
|
|
|
*/
|
|
|
|
|
class CartItemAvailabilityValidationTest extends TestCase
|
|
|
|
|
{
|
|
|
|
|
protected User $user;
|
|
|
|
|
protected Cart $cart;
|
|
|
|
|
|
|
|
|
|
protected function setUp(): void
|
|
|
|
|
{
|
|
|
|
|
parent::setUp();
|
|
|
|
|
|
|
|
|
|
$this->user = User::factory()->create();
|
|
|
|
|
auth()->login($this->user);
|
|
|
|
|
$this->cart = Cart::factory()->create([
|
|
|
|
|
'customer_id' => $this->user->id,
|
|
|
|
|
'customer_type' => get_class($this->user),
|
|
|
|
|
]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create a pool with limited singles for testing
|
|
|
|
|
*/
|
|
|
|
|
protected function createPoolWithLimitedSingles(int $numSingles = 3): Product
|
|
|
|
|
{
|
|
|
|
|
$pool = Product::factory()->create([
|
|
|
|
|
'name' => 'Limited Pool',
|
|
|
|
|
'type' => ProductType::POOL,
|
|
|
|
|
'manage_stock' => false,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
ProductPrice::factory()->create([
|
|
|
|
|
'purchasable_id' => $pool->id,
|
|
|
|
|
'purchasable_type' => Product::class,
|
|
|
|
|
'unit_amount' => 5000,
|
|
|
|
|
'currency' => 'USD',
|
|
|
|
|
'is_default' => true,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$pool->setPoolPricingStrategy('lowest');
|
|
|
|
|
|
|
|
|
|
// Create singles with 1 stock each
|
|
|
|
|
for ($i = 1; $i <= $numSingles; $i++) {
|
|
|
|
|
$single = Product::factory()->create([
|
|
|
|
|
'name' => "Single {$i}",
|
|
|
|
|
'type' => ProductType::BOOKING,
|
|
|
|
|
'manage_stock' => true,
|
|
|
|
|
]);
|
|
|
|
|
$single->increaseStock(1);
|
|
|
|
|
|
|
|
|
|
ProductPrice::factory()->create([
|
|
|
|
|
'purchasable_id' => $single->id,
|
|
|
|
|
'purchasable_type' => Product::class,
|
|
|
|
|
'unit_amount' => 5000,
|
|
|
|
|
'currency' => 'USD',
|
|
|
|
|
'is_default' => true,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$pool->attachSingleItems([$single->id]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $pool;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @test */
|
|
|
|
|
public function cart_item_with_null_price_is_not_ready_for_checkout()
|
|
|
|
|
{
|
|
|
|
|
$pool = $this->createPoolWithLimitedSingles(3);
|
|
|
|
|
|
|
|
|
|
// Add 3 items without dates
|
|
|
|
|
$this->cart->addToCart($pool, 3);
|
|
|
|
|
|
|
|
|
|
// Manually set one item's price to null to simulate unavailable item
|
|
|
|
|
$item = $this->cart->items()->first();
|
|
|
|
|
$item->update(['price' => null, 'subtotal' => null]);
|
|
|
|
|
$item->refresh();
|
|
|
|
|
|
|
|
|
|
// Item with null price should NOT be ready for checkout
|
|
|
|
|
$this->assertNull($item->price);
|
|
|
|
|
$this->assertFalse($item->is_ready_to_checkout, 'Item with null price should not be ready for checkout');
|
|
|
|
|
|
|
|
|
|
// Cart should NOT be ready for checkout
|
|
|
|
|
$this->assertFalse($this->cart->fresh()->is_ready_to_checkout, 'Cart with null-price item should not be ready');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @test */
|
|
|
|
|
public function cart_item_with_zero_price_is_not_ready_for_checkout()
|
|
|
|
|
{
|
|
|
|
|
$pool = $this->createPoolWithLimitedSingles(3);
|
|
|
|
|
|
|
|
|
|
// Add 3 items without dates
|
|
|
|
|
$this->cart->addToCart($pool, 3);
|
|
|
|
|
|
|
|
|
|
// Manually set one item's price to 0 to simulate unavailable item
|
|
|
|
|
$item = $this->cart->items()->first();
|
|
|
|
|
$item->update(['price' => 0, 'subtotal' => 0]);
|
|
|
|
|
$item->refresh();
|
|
|
|
|
|
|
|
|
|
// Item with 0 price should NOT be ready for checkout
|
|
|
|
|
$this->assertEquals(0, $item->price);
|
|
|
|
|
$this->assertFalse($item->is_ready_to_checkout, 'Item with price 0 should not be ready for checkout');
|
|
|
|
|
|
|
|
|
|
// Cart should NOT be ready for checkout
|
|
|
|
|
$this->assertFalse($this->cart->fresh()->is_ready_to_checkout, 'Cart with 0-price item should not be ready');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @test */
|
|
|
|
|
public function unallocated_pool_item_with_null_price_is_not_ready_for_checkout()
|
|
|
|
|
{
|
|
|
|
|
$pool = $this->createPoolWithLimitedSingles(3);
|
|
|
|
|
|
|
|
|
|
$from = now()->addDays(1);
|
|
|
|
|
$until = now()->addDays(2);
|
|
|
|
|
|
|
|
|
|
// Add 3 items with dates - all should be allocated
|
|
|
|
|
$this->cart->addToCart($pool, 3, [], $from, $until);
|
|
|
|
|
|
|
|
|
|
// Manually simulate an item becoming unavailable:
|
|
|
|
|
// - Remove allocation
|
|
|
|
|
// - Set price to null (the real indicator of unavailability)
|
|
|
|
|
$item = $this->cart->items()->first();
|
|
|
|
|
$meta = $item->getMeta();
|
|
|
|
|
unset($meta->allocated_single_item_id);
|
|
|
|
|
unset($meta->allocated_single_item_name);
|
|
|
|
|
$item->update([
|
|
|
|
|
'meta' => json_encode($meta),
|
|
|
|
|
'price' => null,
|
|
|
|
|
'subtotal' => null,
|
|
|
|
|
]);
|
|
|
|
|
$item->refresh();
|
|
|
|
|
|
|
|
|
|
// Item with null price should NOT be ready for checkout
|
|
|
|
|
$this->assertFalse($item->is_ready_to_checkout, 'Item with null price should not be ready for checkout');
|
|
|
|
|
|
|
|
|
|
// Cart should NOT be ready for checkout
|
|
|
|
|
$this->assertFalse($this->cart->fresh()->is_ready_to_checkout, 'Cart with unavailable item should not be ready');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @test */
|
|
|
|
|
public function setDates_does_not_throw_when_items_become_unavailable()
|
|
|
|
|
{
|
|
|
|
|
$pool = $this->createPoolWithLimitedSingles(3);
|
|
|
|
|
|
|
|
|
|
// First user books all 3 singles for specific dates
|
|
|
|
|
$user1 = User::factory()->create();
|
|
|
|
|
$user1Cart = $user1->currentCart();
|
|
|
|
|
|
|
|
|
|
$bookedFrom = now()->addDays(5);
|
|
|
|
|
$bookedUntil = now()->addDays(6);
|
|
|
|
|
|
|
|
|
|
$user1Cart->addToCart($pool, 3, [], $bookedFrom, $bookedUntil);
|
|
|
|
|
$user1Cart->checkout(); // Claims the stock
|
|
|
|
|
|
|
|
|
|
// Our user adds items without dates (should work - we have 3 total capacity)
|
|
|
|
|
$this->cart->addToCart($pool, 3);
|
|
|
|
|
|
|
|
|
|
// All items should have prices > 0 initially
|
|
|
|
|
foreach ($this->cart->items as $item) {
|
|
|
|
|
$this->assertGreaterThan(0, $item->price, 'Item should have positive price initially');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Now set dates that conflict with the booked period
|
|
|
|
|
// This should NOT throw - it should just mark items as unavailable
|
|
|
|
|
$this->cart->setDates($bookedFrom, $bookedUntil);
|
|
|
|
|
|
|
|
|
|
$this->cart->refresh();
|
|
|
|
|
$this->cart->load('items');
|
|
|
|
|
|
|
|
|
|
// Cart should NOT be ready for checkout (items are unavailable)
|
|
|
|
|
$this->assertFalse(
|
|
|
|
|
$this->cart->is_ready_to_checkout,
|
|
|
|
|
'Cart should not be ready when items are unavailable for selected dates'
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @test */
|
|
|
|
|
public function partial_availability_marks_some_items_unavailable()
|
|
|
|
|
{
|
|
|
|
|
$pool = $this->createPoolWithLimitedSingles(3);
|
|
|
|
|
|
|
|
|
|
// First user books 2 of 3 singles for specific dates
|
|
|
|
|
$user1 = User::factory()->create();
|
|
|
|
|
$user1Cart = $user1->currentCart();
|
|
|
|
|
|
|
|
|
|
$bookedFrom = now()->addDays(5);
|
|
|
|
|
$bookedUntil = now()->addDays(6);
|
|
|
|
|
|
|
|
|
|
$user1Cart->addToCart($pool, 2, [], $bookedFrom, $bookedUntil);
|
|
|
|
|
$user1Cart->checkout(); // Claims 2 singles
|
|
|
|
|
|
|
|
|
|
// Verify that only 1 single is available for the booked period
|
|
|
|
|
$available = $pool->getPoolMaxQuantity($bookedFrom, $bookedUntil);
|
|
|
|
|
$this->assertEquals(1, $available, 'Only 1 single should be available after booking 2');
|
|
|
|
|
|
|
|
|
|
// Our user adds 3 items without dates
|
|
|
|
|
$this->cart->addToCart($pool, 3);
|
|
|
|
|
|
|
|
|
|
$this->assertEquals(3, $this->cart->items()->sum('quantity'));
|
|
|
|
|
|
|
|
|
|
// Set dates where only 1 single is available
|
|
|
|
|
// Should NOT throw - just mark some items as unavailable
|
|
|
|
|
$this->cart->setDates($bookedFrom, $bookedUntil);
|
|
|
|
|
|
|
|
|
|
$this->cart->refresh();
|
|
|
|
|
$this->cart->load('items');
|
|
|
|
|
|
|
|
|
|
// Check how many items are available vs unavailable
|
|
|
|
|
$availableItems = $this->cart->items->filter(
|
|
|
|
|
fn($item) =>
|
|
|
|
|
$item->price !== null && $item->price > 0
|
|
|
|
|
);
|
|
|
|
|
$unavailableItems = $this->cart->items->filter(
|
|
|
|
|
fn($item) =>
|
|
|
|
|
$item->price === null || $item->price <= 0
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Should have 1 available and 2 unavailable
|
|
|
|
|
$this->assertEquals(1, $availableItems->count(), 'Should have 1 available item');
|
|
|
|
|
$this->assertEquals(2, $unavailableItems->count(), 'Should have 2 unavailable items');
|
|
|
|
|
|
|
|
|
|
// Cart should NOT be ready for checkout
|
|
|
|
|
$this->assertFalse($this->cart->is_ready_to_checkout, 'Cart with unavailable items should not be ready');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @test */
|
|
|
|
|
public function cart_item_without_allocated_single_for_pool_is_not_ready()
|
|
|
|
|
{
|
|
|
|
|
$pool = $this->createPoolWithLimitedSingles(3);
|
|
|
|
|
|
|
|
|
|
$from = now()->addDays(1);
|
|
|
|
|
$until = now()->addDays(2);
|
|
|
|
|
|
|
|
|
|
// Add 3 items with dates
|
|
|
|
|
$this->cart->addToCart($pool, 3, [], $from, $until);
|
|
|
|
|
|
|
|
|
|
// Verify all items are allocated and ready
|
|
|
|
|
foreach ($this->cart->items as $item) {
|
|
|
|
|
$meta = $item->getMeta();
|
|
|
|
|
$this->assertNotNull($meta->allocated_single_item_id ?? null, 'Item should be allocated');
|
|
|
|
|
$this->assertTrue($item->is_ready_to_checkout, 'Allocated item should be ready');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// All items ready - cart is ready
|
|
|
|
|
$this->assertTrue($this->cart->fresh()->is_ready_to_checkout);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @test */
|
|
|
|
|
public function removing_unavailable_items_makes_cart_ready()
|
|
|
|
|
{
|
|
|
|
|
$pool = $this->createPoolWithLimitedSingles(3);
|
|
|
|
|
|
|
|
|
|
// Add 3 items without dates
|
|
|
|
|
$this->cart->addToCart($pool, 3);
|
|
|
|
|
|
|
|
|
|
// Manually make one item unavailable (price = null)
|
|
|
|
|
$unavailableItem = $this->cart->items()->first();
|
|
|
|
|
$unavailableItem->update(['price' => null, 'subtotal' => null]);
|
|
|
|
|
|
|
|
|
|
// Cart should NOT be ready
|
|
|
|
|
$this->assertFalse($this->cart->fresh()->is_ready_to_checkout);
|
|
|
|
|
|
|
|
|
|
// Remove the unavailable item
|
|
|
|
|
$unavailableItem->delete();
|
|
|
|
|
|
|
|
|
|
// Set dates for remaining items
|
|
|
|
|
$from = now()->addDays(1);
|
|
|
|
|
$until = now()->addDays(2);
|
|
|
|
|
$this->cart->setDates($from, $until);
|
|
|
|
|
|
|
|
|
|
// Now cart should be ready
|
|
|
|
|
$this->assertTrue($this->cart->fresh()->is_ready_to_checkout);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @test */
|
|
|
|
|
public function getItemsRequiringAdjustments_includes_null_price_items()
|
|
|
|
|
{
|
|
|
|
|
$pool = $this->createPoolWithLimitedSingles(3);
|
|
|
|
|
|
|
|
|
|
$from = now()->addDays(1);
|
|
|
|
|
$until = now()->addDays(2);
|
|
|
|
|
|
|
|
|
|
// Add 3 items with dates
|
|
|
|
|
$this->cart->addToCart($pool, 3, [], $from, $until);
|
|
|
|
|
|
|
|
|
|
// Make one item have null price
|
|
|
|
|
$item = $this->cart->items()->first();
|
|
|
|
|
$item->update(['price' => null, 'subtotal' => null]);
|
|
|
|
|
|
|
|
|
|
$this->cart->refresh();
|
|
|
|
|
$this->cart->load('items');
|
|
|
|
|
|
|
|
|
|
// Get items requiring adjustments
|
|
|
|
|
$itemsNeedingAdjustment = $this->cart->getItemsRequiringAdjustments();
|
|
|
|
|
|
|
|
|
|
// The null-price item should be in the list
|
|
|
|
|
$this->assertGreaterThanOrEqual(
|
|
|
|
|
1,
|
|
|
|
|
$itemsNeedingAdjustment->count(),
|
|
|
|
|
'Null price item should require adjustment'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Check that it has 'unavailable' as the price adjustment reason
|
|
|
|
|
$nullPriceItem = $itemsNeedingAdjustment->first(fn($i) => $i->price === null);
|
|
|
|
|
$this->assertNotNull($nullPriceItem, 'Should find the null-price item');
|
|
|
|
|
|
|
|
|
|
$adjustments = $nullPriceItem->requiredAdjustments();
|
|
|
|
|
$this->assertArrayHasKey('price', $adjustments);
|
|
|
|
|
$this->assertEquals('unavailable', $adjustments['price']);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @test */
|
|
|
|
|
public function changing_dates_to_available_period_makes_items_available_again()
|
|
|
|
|
{
|
|
|
|
|
$pool = $this->createPoolWithLimitedSingles(3);
|
|
|
|
|
|
|
|
|
|
// First user books all 3 singles for specific dates
|
|
|
|
|
$user1 = User::factory()->create();
|
|
|
|
|
$user1Cart = $user1->currentCart();
|
|
|
|
|
|
|
|
|
|
$bookedFrom = now()->addDays(5);
|
|
|
|
|
$bookedUntil = now()->addDays(6);
|
|
|
|
|
|
|
|
|
|
$user1Cart->addToCart($pool, 3, [], $bookedFrom, $bookedUntil);
|
|
|
|
|
$user1Cart->checkout();
|
|
|
|
|
|
|
|
|
|
// Our user adds 3 items without dates
|
|
|
|
|
$this->cart->addToCart($pool, 3);
|
|
|
|
|
|
|
|
|
|
// Set dates that conflict - items become unavailable
|
|
|
|
|
$this->cart->setDates($bookedFrom, $bookedUntil);
|
|
|
|
|
$this->assertFalse($this->cart->fresh()->is_ready_to_checkout);
|
|
|
|
|
|
|
|
|
|
// Change to different dates where all singles are available
|
|
|
|
|
$availableFrom = now()->addDays(10);
|
|
|
|
|
$availableUntil = now()->addDays(11);
|
|
|
|
|
|
|
|
|
|
$this->cart->setDates($availableFrom, $availableUntil);
|
|
|
|
|
|
|
|
|
|
$this->cart->refresh();
|
|
|
|
|
$this->cart->load('items');
|
|
|
|
|
|
|
|
|
|
// All items should now have valid prices
|
|
|
|
|
foreach ($this->cart->items as $item) {
|
|
|
|
|
$this->assertNotNull($item->price, 'Item should have price after changing to available dates');
|
|
|
|
|
$this->assertGreaterThan(0, $item->price, 'Item should have positive price');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Cart should be ready for checkout
|
|
|
|
|
$this->assertTrue($this->cart->is_ready_to_checkout, 'Cart should be ready after changing to available dates');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @test */
|
|
|
|
|
public function checkout_throws_when_items_are_unavailable()
|
|
|
|
|
{
|
|
|
|
|
$pool = $this->createPoolWithLimitedSingles(3);
|
|
|
|
|
|
|
|
|
|
// Add items and make one unavailable
|
|
|
|
|
$this->cart->addToCart($pool, 3);
|
|
|
|
|
|
|
|
|
|
$item = $this->cart->items()->first();
|
|
|
|
|
$item->update(['price' => null, 'subtotal' => null]);
|
|
|
|
|
|
|
|
|
|
// Trying to checkout should throw CartItemMissingInformationException
|
|
|
|
|
// because the item has 'price' => 'unavailable' in requiredAdjustments()
|
|
|
|
|
$this->expectException(\Blax\Shop\Exceptions\CartItemMissingInformationException::class);
|
|
|
|
|
$this->cart->checkout();
|
|
|
|
|
}
|
2025-12-20 11:43:28 +00:00
|
|
|
|
|
|
|
|
/** @test */
|
|
|
|
|
public function checkoutSessionLink_throws_when_items_have_null_price()
|
|
|
|
|
{
|
|
|
|
|
$pool = $this->createPoolWithLimitedSingles(3);
|
|
|
|
|
|
|
|
|
|
$from = now()->addDays(1);
|
|
|
|
|
$until = now()->addDays(2);
|
|
|
|
|
|
|
|
|
|
// Add items
|
|
|
|
|
$this->cart->addToCart($pool, 3, [], $from, $until);
|
|
|
|
|
|
|
|
|
|
// Manually make one unavailable
|
|
|
|
|
$item = $this->cart->items()->first();
|
|
|
|
|
$item->update(['price' => null, 'subtotal' => null]);
|
|
|
|
|
|
|
|
|
|
// checkoutSessionLink should throw because item is unavailable
|
|
|
|
|
$this->expectException(\Blax\Shop\Exceptions\CartItemMissingInformationException::class);
|
|
|
|
|
$this->cart->checkoutSessionLink();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @test */
|
|
|
|
|
public function checkoutSessionLink_throws_when_items_have_zero_price()
|
|
|
|
|
{
|
|
|
|
|
$pool = $this->createPoolWithLimitedSingles(3);
|
|
|
|
|
|
|
|
|
|
$from = now()->addDays(1);
|
|
|
|
|
$until = now()->addDays(2);
|
|
|
|
|
|
|
|
|
|
// Add items
|
|
|
|
|
$this->cart->addToCart($pool, 3, [], $from, $until);
|
|
|
|
|
|
|
|
|
|
// Manually set price to 0 (should also be considered unavailable)
|
|
|
|
|
$item = $this->cart->items()->first();
|
|
|
|
|
$item->update(['price' => 0, 'subtotal' => 0]);
|
|
|
|
|
|
|
|
|
|
// checkoutSessionLink should throw because item has 0 price
|
|
|
|
|
$this->expectException(\Blax\Shop\Exceptions\CartItemMissingInformationException::class);
|
|
|
|
|
$this->cart->checkoutSessionLink();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @test */
|
|
|
|
|
public function pool_items_maintain_consistent_pricing_after_date_changes()
|
|
|
|
|
{
|
|
|
|
|
$pool = $this->createPoolWithLimitedSingles(3);
|
|
|
|
|
|
|
|
|
|
$from1 = now()->addDays(1);
|
|
|
|
|
$until1 = now()->addDays(2);
|
|
|
|
|
|
|
|
|
|
// Add 3 items with dates
|
|
|
|
|
$this->cart->addToCart($pool, 3, [], $from1, $until1);
|
|
|
|
|
|
|
|
|
|
// Get initial prices
|
|
|
|
|
$initialPrices = $this->cart->items->pluck('price')->sort()->values()->toArray();
|
|
|
|
|
|
|
|
|
|
// Change to different dates (same duration)
|
|
|
|
|
$from2 = now()->addDays(5);
|
|
|
|
|
$until2 = now()->addDays(6);
|
|
|
|
|
|
|
|
|
|
$this->cart->setDates($from2, $until2);
|
|
|
|
|
$this->cart->refresh();
|
|
|
|
|
$this->cart->load('items');
|
|
|
|
|
|
|
|
|
|
// Prices should be the same (only dates changed, not duration)
|
|
|
|
|
$newPrices = $this->cart->items->pluck('price')->sort()->values()->toArray();
|
|
|
|
|
|
|
|
|
|
$this->assertEquals(
|
|
|
|
|
$initialPrices,
|
|
|
|
|
$newPrices,
|
|
|
|
|
'Prices should remain consistent when only dates change (same duration)'
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** @test */
|
|
|
|
|
public function price_zero_is_treated_as_unavailable()
|
|
|
|
|
{
|
|
|
|
|
$pool = $this->createPoolWithLimitedSingles(3);
|
|
|
|
|
|
|
|
|
|
$from = now()->addDays(1);
|
|
|
|
|
$until = now()->addDays(2);
|
|
|
|
|
|
|
|
|
|
$this->cart->addToCart($pool, 3, [], $from, $until);
|
|
|
|
|
|
|
|
|
|
// Set price to 0 (simulating an old bug where 0 was used instead of null)
|
|
|
|
|
$item = $this->cart->items()->first();
|
|
|
|
|
$item->update(['price' => 0, 'subtotal' => 0]);
|
|
|
|
|
$item->refresh();
|
|
|
|
|
|
|
|
|
|
// Item should NOT be ready for checkout
|
|
|
|
|
$this->assertFalse($item->is_ready_to_checkout, 'Item with price 0 should not be ready');
|
|
|
|
|
|
|
|
|
|
// requiredAdjustments should show price as unavailable
|
|
|
|
|
$adjustments = $item->requiredAdjustments();
|
|
|
|
|
$this->assertArrayHasKey('price', $adjustments);
|
|
|
|
|
$this->assertEquals('unavailable', $adjustments['price']);
|
|
|
|
|
|
|
|
|
|
// Cart should NOT be ready
|
|
|
|
|
$this->assertFalse($this->cart->fresh()->is_ready_to_checkout);
|
|
|
|
|
}
|
2025-12-20 11:19:34 +00:00
|
|
|
}
|