277 lines
9.3 KiB
PHP
277 lines
9.3 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
namespace Blax\Shop\Tests\Feature;
|
||
|
|
|
||
|
|
use Blax\Shop\Enums\ProductType;
|
||
|
|
use Blax\Shop\Models\Product;
|
||
|
|
use Blax\Shop\Tests\TestCase;
|
||
|
|
use Carbon\Carbon;
|
||
|
|
|
||
|
|
class PoolAvailabilityMethodsTest extends TestCase
|
||
|
|
{
|
||
|
|
protected Product $pool;
|
||
|
|
protected Product $spot1;
|
||
|
|
protected Product $spot2;
|
||
|
|
protected Product $spot3;
|
||
|
|
|
||
|
|
protected function setUp(): void
|
||
|
|
{
|
||
|
|
parent::setUp();
|
||
|
|
|
||
|
|
$this->pool = Product::factory()->create([
|
||
|
|
'name' => 'Parking Pool',
|
||
|
|
'type' => ProductType::POOL,
|
||
|
|
'manage_stock' => false,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$this->spot1 = Product::factory()->create([
|
||
|
|
'name' => 'Spot 1',
|
||
|
|
'type' => ProductType::BOOKING,
|
||
|
|
'manage_stock' => true,
|
||
|
|
]);
|
||
|
|
$this->spot1->increaseStock(2);
|
||
|
|
|
||
|
|
$this->spot2 = Product::factory()->create([
|
||
|
|
'name' => 'Spot 2',
|
||
|
|
'type' => ProductType::BOOKING,
|
||
|
|
'manage_stock' => true,
|
||
|
|
]);
|
||
|
|
$this->spot2->increaseStock(3);
|
||
|
|
|
||
|
|
$this->spot3 = Product::factory()->create([
|
||
|
|
'name' => 'Spot 3',
|
||
|
|
'type' => ProductType::BOOKING,
|
||
|
|
'manage_stock' => true,
|
||
|
|
]);
|
||
|
|
$this->spot3->increaseStock(1);
|
||
|
|
|
||
|
|
$this->pool->attachSingleItems([$this->spot1->id, $this->spot2->id, $this->spot3->id]);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @test */
|
||
|
|
public function it_gets_pool_availability_calendar()
|
||
|
|
{
|
||
|
|
$start = Carbon::now()->addDays(1)->startOfDay();
|
||
|
|
$end = Carbon::now()->addDays(3)->startOfDay();
|
||
|
|
|
||
|
|
$calendar = $this->pool->getPoolAvailabilityCalendar($start, $end);
|
||
|
|
|
||
|
|
$this->assertIsArray($calendar);
|
||
|
|
$this->assertCount(3, $calendar); // 3 days
|
||
|
|
|
||
|
|
// Each day should have availability count
|
||
|
|
foreach ($calendar as $date => $available) {
|
||
|
|
$this->assertIsString($date);
|
||
|
|
$this->assertTrue($available === 'unlimited' || is_int($available));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @test */
|
||
|
|
public function it_shows_correct_availability_per_day()
|
||
|
|
{
|
||
|
|
$start = Carbon::now()->addDays(1)->startOfDay();
|
||
|
|
$end = Carbon::now()->addDays(1)->startOfDay();
|
||
|
|
|
||
|
|
$calendar = $this->pool->getPoolAvailabilityCalendar($start, $end);
|
||
|
|
|
||
|
|
// Total availability should be 2 + 3 + 1 = 6
|
||
|
|
$dateStr = $start->format('Y-m-d');
|
||
|
|
$this->assertEquals(6, $calendar[$dateStr]);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @test */
|
||
|
|
public function it_reduces_availability_when_items_are_claimed()
|
||
|
|
{
|
||
|
|
$from = Carbon::now()->addDays(1)->startOfDay();
|
||
|
|
$until = Carbon::now()->addDays(2)->startOfDay();
|
||
|
|
|
||
|
|
// Claim 2 items from spot1
|
||
|
|
$this->spot1->claimStock(2, null, $from, $until);
|
||
|
|
|
||
|
|
$calendar = $this->pool->getPoolAvailabilityCalendar($from, $until);
|
||
|
|
|
||
|
|
$dateStr = $from->format('Y-m-d');
|
||
|
|
// Should now be 0 (spot1) + 3 (spot2) + 1 (spot3) = 4
|
||
|
|
$this->assertEquals(4, $calendar[$dateStr]);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @test */
|
||
|
|
public function it_gets_single_items_availability_without_dates()
|
||
|
|
{
|
||
|
|
$availability = $this->pool->getSingleItemsAvailability();
|
||
|
|
|
||
|
|
$this->assertIsArray($availability);
|
||
|
|
$this->assertCount(3, $availability);
|
||
|
|
|
||
|
|
// Check structure
|
||
|
|
$this->assertEquals($this->spot1->id, $availability[0]['id']);
|
||
|
|
$this->assertEquals('Spot 1', $availability[0]['name']);
|
||
|
|
$this->assertEquals(2, $availability[0]['available']);
|
||
|
|
$this->assertTrue($availability[0]['manage_stock']);
|
||
|
|
|
||
|
|
$this->assertEquals(3, $availability[1]['available']);
|
||
|
|
$this->assertEquals(1, $availability[2]['available']);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @test */
|
||
|
|
public function it_gets_single_items_availability_with_dates()
|
||
|
|
{
|
||
|
|
$from = Carbon::now()->addDays(1)->startOfDay();
|
||
|
|
$until = Carbon::now()->addDays(2)->startOfDay();
|
||
|
|
|
||
|
|
// Claim some stock
|
||
|
|
$this->spot2->claimStock(2, null, $from, $until);
|
||
|
|
|
||
|
|
$availability = $this->pool->getSingleItemsAvailability($from, $until);
|
||
|
|
|
||
|
|
$this->assertEquals(2, $availability[0]['available']); // Spot 1: still 2
|
||
|
|
$this->assertEquals(1, $availability[1]['available']); // Spot 2: 3 - 2 claimed = 1
|
||
|
|
$this->assertEquals(1, $availability[2]['available']); // Spot 3: still 1
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @test */
|
||
|
|
public function it_shows_unlimited_for_items_without_stock_management()
|
||
|
|
{
|
||
|
|
$unlimitedSpot = Product::factory()->create([
|
||
|
|
'name' => 'Unlimited Spot',
|
||
|
|
'type' => ProductType::BOOKING,
|
||
|
|
'manage_stock' => false,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$this->pool->attachSingleItems($unlimitedSpot->id);
|
||
|
|
|
||
|
|
$availability = $this->pool->getSingleItemsAvailability();
|
||
|
|
|
||
|
|
$unlimited = collect($availability)->firstWhere('id', $unlimitedSpot->id);
|
||
|
|
$this->assertEquals('unlimited', $unlimited['available']);
|
||
|
|
$this->assertFalse($unlimited['manage_stock']);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @test */
|
||
|
|
public function it_checks_if_pool_is_available_for_period()
|
||
|
|
{
|
||
|
|
$from = Carbon::now()->addDays(1)->startOfDay();
|
||
|
|
$until = Carbon::now()->addDays(2)->startOfDay();
|
||
|
|
|
||
|
|
// Pool has 6 total items available
|
||
|
|
$this->assertTrue($this->pool->isPoolAvailable($from, $until, 6));
|
||
|
|
$this->assertTrue($this->pool->isPoolAvailable($from, $until, 3));
|
||
|
|
$this->assertFalse($this->pool->isPoolAvailable($from, $until, 7));
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @test */
|
||
|
|
public function it_returns_false_when_pool_not_available()
|
||
|
|
{
|
||
|
|
$from = Carbon::now()->addDays(1)->startOfDay();
|
||
|
|
$until = Carbon::now()->addDays(2)->startOfDay();
|
||
|
|
|
||
|
|
// Claim all stock
|
||
|
|
$this->spot1->claimStock(2, null, $from, $until);
|
||
|
|
$this->spot2->claimStock(3, null, $from, $until);
|
||
|
|
$this->spot3->claimStock(1, null, $from, $until);
|
||
|
|
|
||
|
|
$this->assertFalse($this->pool->isPoolAvailable($from, $until, 1));
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @test */
|
||
|
|
public function it_gets_available_periods_for_pool()
|
||
|
|
{
|
||
|
|
$start = Carbon::now()->addDays(1)->startOfDay();
|
||
|
|
$end = Carbon::now()->addDays(10)->startOfDay();
|
||
|
|
|
||
|
|
// Claim stock for days 3-5
|
||
|
|
$claimFrom = Carbon::now()->addDays(3)->startOfDay();
|
||
|
|
$claimUntil = Carbon::now()->addDays(6)->startOfDay();
|
||
|
|
$this->spot1->claimStock(2, null, $claimFrom, $claimUntil);
|
||
|
|
$this->spot2->claimStock(3, null, $claimFrom, $claimUntil);
|
||
|
|
$this->spot3->claimStock(1, null, $claimFrom, $claimUntil);
|
||
|
|
|
||
|
|
$periods = $this->pool->getPoolAvailablePeriods($start, $end, 1);
|
||
|
|
|
||
|
|
$this->assertIsArray($periods);
|
||
|
|
// Should have availability before and after the claimed period
|
||
|
|
$this->assertGreaterThan(0, count($periods));
|
||
|
|
|
||
|
|
foreach ($periods as $period) {
|
||
|
|
$this->assertArrayHasKey('from', $period);
|
||
|
|
$this->assertArrayHasKey('until', $period);
|
||
|
|
$this->assertArrayHasKey('min_available', $period);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @test */
|
||
|
|
public function it_filters_periods_by_minimum_consecutive_days()
|
||
|
|
{
|
||
|
|
$start = Carbon::now()->addDays(1)->startOfDay();
|
||
|
|
$end = Carbon::now()->addDays(10)->startOfDay();
|
||
|
|
|
||
|
|
// Create a pattern with short availability gaps
|
||
|
|
$this->spot1->claimStock(
|
||
|
|
2,
|
||
|
|
null,
|
||
|
|
Carbon::now()->addDays(2)->startOfDay(),
|
||
|
|
Carbon::now()->addDays(3)->startOfDay()
|
||
|
|
);
|
||
|
|
|
||
|
|
$this->spot1->claimStock(
|
||
|
|
2,
|
||
|
|
null,
|
||
|
|
Carbon::now()->addDays(4)->startOfDay(),
|
||
|
|
Carbon::now()->addDays(5)->startOfDay()
|
||
|
|
);
|
||
|
|
|
||
|
|
// Get periods with minimum 3 consecutive days
|
||
|
|
$periods = $this->pool->getPoolAvailablePeriods($start, $end, 1, 3);
|
||
|
|
|
||
|
|
foreach ($periods as $period) {
|
||
|
|
$from = Carbon::parse($period['from']);
|
||
|
|
$until = Carbon::parse($period['until']);
|
||
|
|
$days = $from->diffInDays($until) + 1;
|
||
|
|
|
||
|
|
// All periods should have at least 3 days
|
||
|
|
$this->assertGreaterThanOrEqual(3, $days);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @test */
|
||
|
|
public function it_handles_higher_quantity_requirements_in_available_periods()
|
||
|
|
{
|
||
|
|
$start = Carbon::now()->addDays(1)->startOfDay();
|
||
|
|
$end = Carbon::now()->addDays(5)->startOfDay();
|
||
|
|
|
||
|
|
// Pool has 6 items total
|
||
|
|
// Get periods where at least 5 items are available
|
||
|
|
$periodsBefore = $this->pool->getPoolAvailablePeriods($start, $end, 5);
|
||
|
|
|
||
|
|
$this->assertGreaterThan(0, count($periodsBefore));
|
||
|
|
|
||
|
|
// Claim items to reduce availability below 5
|
||
|
|
$this->spot1->claimStock(
|
||
|
|
2,
|
||
|
|
null,
|
||
|
|
Carbon::now()->addDays(2)->startOfDay(),
|
||
|
|
Carbon::now()->addDays(4)->startOfDay()
|
||
|
|
);
|
||
|
|
|
||
|
|
// Now get periods where at least 5 items are available
|
||
|
|
$periodsAfter = $this->pool->getPoolAvailablePeriods($start, $end, 5);
|
||
|
|
|
||
|
|
// After claiming 2 items, only 4 items available during days 2-4
|
||
|
|
// So we should not be able to get periods with 5 items for the full range
|
||
|
|
// The periods should be different (either fewer or shorter)
|
||
|
|
$this->assertNotEquals($periodsBefore, $periodsAfter);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @test */
|
||
|
|
public function it_throws_exception_for_non_pool_products()
|
||
|
|
{
|
||
|
|
$regularProduct = Product::factory()->create([
|
||
|
|
'type' => ProductType::SIMPLE,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$this->expectException(\Exception::class);
|
||
|
|
$this->expectExceptionMessage('This method is only for pool products');
|
||
|
|
$regularProduct->getPoolAvailabilityCalendar(Carbon::now(), Carbon::now()->addDays(1));
|
||
|
|
}
|
||
|
|
}
|