assertArrayHasKey(SparePartOrder::STATUS_ORDERED, SparePartOrder::STATUS_NAMES); $this->assertArrayHasKey(SparePartOrder::STATUS_IN_STOCK, SparePartOrder::STATUS_NAMES); $this->assertArrayHasKey(SparePartOrder::STATUS_SHIPPED, SparePartOrder::STATUS_NAMES); } public function test_status_name_attribute_returns_correct_name(): void { $order = SparePartOrder::factory() ->inStock() ->create(); $this->assertEquals('На складе', $order->status_name); } public function test_available_qty_defaults_to_ordered_quantity_on_create(): void { $sparePart = SparePart::factory()->create(); $user = User::factory()->create(); $order = SparePartOrder::create([ 'spare_part_id' => $sparePart->id, 'user_id' => $user->id, 'ordered_quantity' => 15, 'status' => SparePartOrder::STATUS_IN_STOCK, 'with_documents' => false, ]); $this->assertEquals(15, $order->available_qty); } public function test_year_defaults_to_current_year_on_create(): void { $sparePart = SparePart::factory()->create(); $user = User::factory()->create(); $order = SparePartOrder::create([ 'spare_part_id' => $sparePart->id, 'user_id' => $user->id, 'ordered_quantity' => 10, 'status' => SparePartOrder::STATUS_IN_STOCK, 'with_documents' => false, ]); $this->assertEquals((int) date('Y'), $order->year); } public function test_reserved_qty_calculates_active_reservations(): void { $order = SparePartOrder::factory() ->inStock() ->withQuantity(20) ->create(); Reservation::factory() ->active() ->withQuantity(5) ->fromOrder($order) ->create(); Reservation::factory() ->active() ->withQuantity(3) ->fromOrder($order) ->create(); // Cancelled should not count Reservation::factory() ->cancelled() ->withQuantity(10) ->fromOrder($order) ->create(); $this->assertEquals(8, $order->reserved_qty); } public function test_free_qty_calculates_available_minus_reserved(): void { $order = SparePartOrder::factory() ->inStock() ->withQuantity(15) ->create(); Reservation::factory() ->active() ->withQuantity(6) ->fromOrder($order) ->create(); // available: 15, reserved: 6, free: 9 $this->assertEquals(9, $order->free_qty); } public function test_free_qty_never_goes_negative(): void { $order = SparePartOrder::factory() ->inStock() ->withQuantity(5) ->create(); // Create reservation larger than available (edge case) Reservation::factory() ->active() ->withQuantity(10) ->fromOrder($order) ->create(); $this->assertEquals(0, $order->free_qty); } public function test_can_reserve_returns_true_when_sufficient_qty(): void { $order = SparePartOrder::factory() ->inStock() ->withQuantity(10) ->create(); $this->assertTrue($order->canReserve(5)); $this->assertTrue($order->canReserve(10)); } public function test_can_reserve_returns_false_when_insufficient_qty(): void { $order = SparePartOrder::factory() ->inStock() ->withQuantity(10) ->create(); Reservation::factory() ->active() ->withQuantity(8) ->fromOrder($order) ->create(); // Only 2 free, trying to reserve 5 $this->assertFalse($order->canReserve(5)); } public function test_is_in_stock_method(): void { $orderInStock = SparePartOrder::factory()->inStock()->create(); $orderOrdered = SparePartOrder::factory()->ordered()->create(); $this->assertTrue($orderInStock->isInStock()); $this->assertFalse($orderOrdered->isInStock()); } public function test_is_ordered_method(): void { $orderOrdered = SparePartOrder::factory()->ordered()->create(); $orderInStock = SparePartOrder::factory()->inStock()->create(); $this->assertTrue($orderOrdered->isOrdered()); $this->assertFalse($orderInStock->isOrdered()); } public function test_is_shipped_method(): void { $orderShipped = SparePartOrder::factory()->shipped()->create(); $orderInStock = SparePartOrder::factory()->inStock()->create(); $this->assertTrue($orderShipped->isShipped()); $this->assertFalse($orderInStock->isShipped()); } public function test_scope_in_stock(): void { SparePartOrder::factory()->inStock()->create(); SparePartOrder::factory()->inStock()->create(); SparePartOrder::factory()->ordered()->create(); SparePartOrder::factory()->shipped()->create(); $inStockOrders = SparePartOrder::inStock()->get(); $this->assertCount(2, $inStockOrders); } public function test_scope_with_available(): void { SparePartOrder::factory()->inStock()->withQuantity(10)->create(); SparePartOrder::factory()->shipped()->create(); // available_qty = 0 $withAvailable = SparePartOrder::withAvailable()->get(); $this->assertCount(1, $withAvailable); } public function test_scope_with_documents(): void { SparePartOrder::factory()->withDocuments(true)->create(); SparePartOrder::factory()->withDocuments(true)->create(); SparePartOrder::factory()->withDocuments(false)->create(); $withDocs = SparePartOrder::withDocuments(true)->get(); $withoutDocs = SparePartOrder::withDocuments(false)->get(); $this->assertCount(2, $withDocs); $this->assertCount(1, $withoutDocs); } public function test_scope_for_spare_part(): void { $sparePart1 = SparePart::factory()->create(); $sparePart2 = SparePart::factory()->create(); SparePartOrder::factory()->forSparePart($sparePart1)->create(); SparePartOrder::factory()->forSparePart($sparePart1)->create(); SparePartOrder::factory()->forSparePart($sparePart2)->create(); $ordersForPart1 = SparePartOrder::forSparePart($sparePart1->id)->get(); $this->assertCount(2, $ordersForPart1); } public function test_scope_available_for_reservation(): void { $sparePart = SparePart::factory()->create(); $availableOrder = SparePartOrder::factory() ->inStock() ->withDocuments(false) ->withQuantity(10) ->forSparePart($sparePart) ->create(['created_at' => now()->subDay()]); // Not matching conditions SparePartOrder::factory() ->ordered() // wrong status ->withDocuments(false) ->forSparePart($sparePart) ->create(); SparePartOrder::factory() ->inStock() ->withDocuments(true) // wrong docs type ->forSparePart($sparePart) ->create(); SparePartOrder::factory() ->shipped() // no available qty ->withDocuments(false) ->forSparePart($sparePart) ->create(); $available = SparePartOrder::availableForReservation($sparePart->id, false)->get(); $this->assertCount(1, $available); $this->assertEquals($availableOrder->id, $available->first()->id); } public function test_casts_are_correct(): void { $order = SparePartOrder::factory() ->withDocuments(true) ->withQuantity(10) ->create(); $order->refresh(); $this->assertIsBool($order->with_documents); $this->assertIsInt($order->ordered_quantity); $this->assertIsInt($order->available_qty); $this->assertIsInt($order->year); } public function test_remaining_quantity_backward_compatibility(): void { $order = SparePartOrder::factory() ->withQuantity(15) ->create(); // Deprecated attribute should still work $this->assertEquals($order->available_qty, $order->remaining_quantity); } public function test_relations_exist(): void { $order = SparePartOrder::factory()->create(); $this->assertInstanceOf(\Illuminate\Database\Eloquent\Relations\BelongsTo::class, $order->sparePart()); $this->assertInstanceOf(\Illuminate\Database\Eloquent\Relations\BelongsTo::class, $order->user()); $this->assertInstanceOf(\Illuminate\Database\Eloquent\Relations\HasMany::class, $order->reservations()); $this->assertInstanceOf(\Illuminate\Database\Eloquent\Relations\HasMany::class, $order->movements()); } }