laravel-shop/tests/Feature/StockManagementTest.php

238 lines
6.1 KiB
PHP
Raw Normal View History

2025-11-21 10:49:41 +00:00
<?php
namespace Blax\Shop\Tests\Feature;
2025-11-25 11:33:42 +00:00
use Blax\Shop\Exceptions\NotEnoughStockException;
2025-11-21 10:49:41 +00:00
use Blax\Shop\Models\Product;
use Blax\Shop\Models\ProductStock;
use Blax\Shop\Tests\TestCase;
2025-11-25 11:33:42 +00:00
use Carbon\Carbon;
2025-11-21 10:49:41 +00:00
use Illuminate\Foundation\Testing\RefreshDatabase;
class StockManagementTest extends TestCase
{
use RefreshDatabase;
/** @test */
2025-12-04 10:06:09 +00:00
public function it_can_claim_stock_for_a_product()
2025-11-21 10:49:41 +00:00
{
2025-11-25 11:33:42 +00:00
$product = Product::factory()
->withStocks(100)
->create();
2025-11-21 10:49:41 +00:00
2025-12-04 10:06:09 +00:00
$claim = $product->claimStock(
2025-11-21 10:49:41 +00:00
quantity: 10,
until: now()->addHours(2)
);
2025-12-04 10:06:09 +00:00
$this->assertNotNull($claim);
$this->assertEquals(10, $claim->quantity);
2025-11-25 11:33:42 +00:00
$this->assertEquals(90, $product->getAvailableStock());
2025-11-21 10:49:41 +00:00
}
/** @test */
2025-12-04 10:06:09 +00:00
public function it_cannot_claim_more_stock_than_available()
2025-11-21 10:49:41 +00:00
{
2025-11-25 11:33:42 +00:00
$product = Product::factory()
->withStocks(5)
->create();
2025-11-21 10:49:41 +00:00
2025-12-04 10:06:09 +00:00
$claim = null;
2025-11-25 11:33:42 +00:00
2025-12-04 10:06:09 +00:00
$this->assertThrows(fn() => $claim = $product->claimStock(15), NotEnoughStockException::class);
2025-11-21 10:49:41 +00:00
2025-12-04 10:06:09 +00:00
$this->assertNull($claim);
2025-11-25 11:33:42 +00:00
$this->assertEquals(5, $product->getAvailableStock());
2025-11-21 10:49:41 +00:00
}
/** @test */
2025-12-04 10:06:09 +00:00
public function it_can_release_claimed_stock()
2025-11-21 10:49:41 +00:00
{
2025-11-25 11:33:42 +00:00
$product = Product::factory()
->withStocks(100)
->create();
2025-11-21 10:49:41 +00:00
2025-12-04 10:06:09 +00:00
$claim = $product->claimStock(
2025-11-21 10:49:41 +00:00
quantity: 10,
2025-11-25 11:33:42 +00:00
until: now()->addHours(2)
2025-11-21 10:49:41 +00:00
);
2025-11-25 11:33:42 +00:00
$this->assertEquals(90, $product->getAvailableStock());
2025-11-21 10:49:41 +00:00
2025-12-04 10:06:09 +00:00
$claim->release();
2025-11-21 10:49:41 +00:00
2025-11-25 11:33:42 +00:00
$this->assertEquals(100, $product->refresh()->getAvailableStock());
2025-12-04 10:06:09 +00:00
$this->assertNotNull($claim->fresh()->released_at);
2025-11-21 10:49:41 +00:00
}
/** @test */
public function it_can_check_if_stock_is_pending()
{
2025-11-25 11:33:42 +00:00
$product = Product::factory()->withStocks(10)->create();
2025-11-21 10:49:41 +00:00
2025-12-04 10:06:09 +00:00
$claim = $product->claimStock(5);
2025-11-21 10:49:41 +00:00
2025-12-04 10:06:09 +00:00
$pending = ProductStock::pending()->where('id', $claim->id)->first();
2025-11-21 10:49:41 +00:00
$this->assertNotNull($pending);
$this->assertNull($pending->released_at);
}
/** @test */
public function it_can_check_if_stock_is_released()
{
2025-11-25 11:33:42 +00:00
$product = Product::factory()->withStocks(50)->create();
2025-11-21 10:49:41 +00:00
2025-12-04 10:06:09 +00:00
$claim = $product->claimStock(5);
2025-11-21 10:49:41 +00:00
2025-12-04 10:06:09 +00:00
$claim->release();
2025-11-21 10:49:41 +00:00
2025-12-04 10:06:09 +00:00
$released = ProductStock::released()->where('id', $claim->id)->first();
2025-11-21 10:49:41 +00:00
$this->assertNotNull($released);
$this->assertNotNull($released->released_at);
}
/** @test */
2025-12-04 10:06:09 +00:00
public function it_can_distinguish_temporary_and_permanent_claims()
2025-11-21 10:49:41 +00:00
{
2025-11-25 11:33:42 +00:00
$product = Product::factory()->withStocks(100)->create();
2025-11-21 10:49:41 +00:00
2025-12-04 10:06:09 +00:00
$permanentClaim = $product->claimStock(
2025-11-25 11:33:42 +00:00
quantity: 10
2025-11-21 10:49:41 +00:00
);
2025-12-04 10:06:09 +00:00
$temporaryClaim = $product->claimStock(
2025-11-21 10:49:41 +00:00
quantity: 5,
2025-11-25 11:33:42 +00:00
until: now()->addHours(1)
2025-11-21 10:49:41 +00:00
);
2025-12-04 10:06:09 +00:00
$this->assertTrue($permanentClaim->isPermanent());
$this->assertFalse($permanentClaim->isTemporary());
2025-11-21 10:49:41 +00:00
2025-12-04 10:06:09 +00:00
$this->assertTrue($temporaryClaim->isTemporary());
$this->assertFalse($temporaryClaim->isPermanent());
2025-11-21 10:49:41 +00:00
}
/** @test */
public function it_belongs_to_a_product()
{
2025-11-25 11:33:42 +00:00
$product = Product::factory()->withStocks(20)->create();
2025-11-21 10:49:41 +00:00
2025-12-04 10:06:09 +00:00
$claim = $product->claimStock(5);
2025-11-21 10:49:41 +00:00
2025-12-04 10:06:09 +00:00
$this->assertInstanceOf(Product::class, $claim->product);
$this->assertEquals($product->id, $claim->product->id);
2025-11-21 10:49:41 +00:00
}
/** @test */
public function product_has_many_stock_records()
{
2025-11-25 11:33:42 +00:00
$product = Product::factory()->withStocks(30)->create();
2025-11-21 10:49:41 +00:00
2025-11-25 11:33:42 +00:00
$product->increaseStock(10);
$product->increaseStock(10);
$product->increaseStock(50);
2025-11-21 10:49:41 +00:00
2025-11-25 11:33:42 +00:00
$this->assertCount(4, $product->stocks);
$this->assertInstanceOf(ProductStock::class, $product->stocks->first());
$this->assertEquals(30 + 10 + 10 + 50, $product->getAvailableStock());
2025-11-21 10:49:41 +00:00
}
/** @test */
2025-12-04 10:06:09 +00:00
public function it_can_get_active_stock_claims()
2025-11-21 10:49:41 +00:00
{
2025-11-25 11:33:42 +00:00
$product = Product::factory()->withStocks(100)->create();
2025-11-21 10:49:41 +00:00
2025-12-04 10:06:09 +00:00
$activeClaim = $product->claimStock(
2025-11-25 11:33:42 +00:00
quantity: 10,
until: now()->addHours(2)
);
2025-12-04 10:06:09 +00:00
$expiredClaim = $product->claimStock(
2025-11-25 11:33:42 +00:00
quantity: 5,
until: now()->subHours(1)
);
2025-11-21 10:49:41 +00:00
2025-12-04 10:06:09 +00:00
$activeClaims = $product->claims()->get();
2025-11-21 10:49:41 +00:00
2025-12-04 10:06:09 +00:00
$this->assertCount(1, $activeClaims);
$this->assertEquals($activeClaim->id, $activeClaims->first()->id);
2025-11-21 10:49:41 +00:00
}
/** @test */
public function it_cannot_release_stock_twice()
{
2025-11-25 11:33:42 +00:00
$product = Product::factory()->withStocks()->create();
2025-11-21 10:49:41 +00:00
2025-12-04 10:06:09 +00:00
$claim = $product->claimStock(5);
2025-11-21 10:49:41 +00:00
2025-12-04 10:06:09 +00:00
$this->assertTrue($claim->release());
$this->assertFalse($claim->release());
2025-11-21 10:49:41 +00:00
}
/** @test */
2025-12-04 10:06:09 +00:00
public function it_can_store_claim_note()
2025-11-21 10:49:41 +00:00
{
2025-11-25 11:33:42 +00:00
$product = Product::factory()->withStocks()->create();
$note = "Customer requested to hold this item for 2 days.";
2025-11-21 10:49:41 +00:00
2025-12-04 10:06:09 +00:00
$claim = $product->claimStock(
2025-11-21 10:49:41 +00:00
quantity: 5,
2025-11-25 11:33:42 +00:00
note: $note
2025-11-21 10:49:41 +00:00
);
2025-12-04 10:06:09 +00:00
$this->assertEquals($note, $claim->note);
2025-11-21 10:49:41 +00:00
}
/** @test */
public function it_calculates_available_stock_correctly()
{
2025-11-25 11:33:42 +00:00
$product = Product::factory()->withStocks(100)->create();
2025-12-04 10:06:09 +00:00
$claim1 = $product->claimStock(
2025-11-25 11:33:42 +00:00
quantity: 10,
until: now()->addHours(2)
);
2025-11-21 10:49:41 +00:00
2025-12-04 10:06:09 +00:00
$claim2 = $product->claimStock(
2025-11-25 11:33:42 +00:00
quantity: 5,
until: now()->addHours(1)
);
2025-11-21 10:49:41 +00:00
2025-12-04 10:06:09 +00:00
$claim1->refresh();
$claim2->refresh();
2025-11-21 10:49:41 +00:00
2025-11-25 11:33:42 +00:00
$this->assertEquals(85, $product->refresh()->getAvailableStock());
2025-11-21 10:49:41 +00:00
}
/** @test */
public function product_tracks_low_stock_threshold()
{
2025-11-25 16:02:39 +00:00
$product = Product::factory()
->withStocks(12)
->create([
'low_stock_threshold' => 10,
]);
2025-11-21 10:49:41 +00:00
$this->assertFalse($product->isLowStock());
$product->decreaseStock(8);
$this->assertTrue($product->fresh()->isLowStock());
}
/** @test */
public function it_updates_in_stock_status_automatically()
{
2025-11-25 16:02:39 +00:00
$product = Product::factory()
->withStocks(10)
->create();
2025-11-21 10:49:41 +00:00
2025-11-25 16:02:39 +00:00
$product->decreaseStock(10);
2025-11-21 10:49:41 +00:00
2025-11-25 16:02:39 +00:00
$this->assertFalse($product->fresh()->isInStock());
2025-11-21 10:49:41 +00:00
}
}