| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467 |
- <?php
- namespace Tests\Feature;
- use App\Models\Reclamation;
- use App\Models\Reservation;
- use App\Models\Role;
- use App\Models\Shortage;
- use App\Models\SparePart;
- use App\Models\SparePartOrder;
- use App\Models\User;
- use Illuminate\Foundation\Testing\RefreshDatabase;
- use Tests\TestCase;
- class SparePartReservationControllerTest extends TestCase
- {
- use RefreshDatabase;
- protected $seed = true;
- private User $adminUser;
- private User $managerUser;
- protected function setUp(): void
- {
- parent::setUp();
- $this->adminUser = User::factory()->create(['role' => Role::ADMIN]);
- $this->managerUser = User::factory()->create(['role' => Role::MANAGER]);
- }
- // ==================== forReclamation (JSON API) ====================
- public function test_guest_cannot_get_reservations_for_reclamation(): void
- {
- $reclamation = Reclamation::factory()->create();
- $response = $this->get(route('spare_part_reservations.for_reclamation', $reclamation->id));
- $response->assertRedirect(route('login'));
- }
- public function test_admin_can_get_reservations_for_reclamation(): void
- {
- $sparePart = SparePart::factory()->create(['article' => 'SP-RESRV-001']);
- $order = SparePartOrder::factory()->inStock()->withQuantity(10)->forSparePart($sparePart)->create();
- $reclamation = Reclamation::factory()->create();
- Reservation::factory()->create([
- 'spare_part_id' => $sparePart->id,
- 'spare_part_order_id' => $order->id,
- 'reclamation_id' => $reclamation->id,
- 'reserved_qty' => 3,
- 'status' => Reservation::STATUS_ACTIVE,
- ]);
- $response = $this->actingAs($this->adminUser)
- ->getJson(route('spare_part_reservations.for_reclamation', $reclamation->id));
- $response->assertStatus(200);
- $response->assertJsonStructure([
- 'reservations' => [['id', 'spare_part_id', 'article', 'reserved_qty', 'status']],
- ]);
- $response->assertJsonFragment(['spare_part_id' => $sparePart->id]);
- }
- public function test_manager_can_get_reservations_for_reclamation(): void
- {
- $reclamation = Reclamation::factory()->create();
- $response = $this->actingAs($this->managerUser)
- ->getJson(route('spare_part_reservations.for_reclamation', $reclamation->id));
- $response->assertStatus(200);
- $response->assertJsonStructure(['reservations']);
- }
- public function test_returns_only_active_reservations_for_reclamation(): void
- {
- $sparePart = SparePart::factory()->create();
- $order = SparePartOrder::factory()->inStock()->withQuantity(20)->forSparePart($sparePart)->create();
- $reclamation = Reclamation::factory()->create();
- // Активный резерв
- Reservation::factory()->create([
- 'spare_part_id' => $sparePart->id,
- 'spare_part_order_id' => $order->id,
- 'reclamation_id' => $reclamation->id,
- 'reserved_qty' => 5,
- 'status' => Reservation::STATUS_ACTIVE,
- ]);
- // Отменённый резерв — не должен попасть
- Reservation::factory()->create([
- 'spare_part_id' => $sparePart->id,
- 'spare_part_order_id' => $order->id,
- 'reclamation_id' => $reclamation->id,
- 'reserved_qty' => 2,
- 'status' => Reservation::STATUS_CANCELLED,
- ]);
- $response = $this->actingAs($this->adminUser)
- ->getJson(route('spare_part_reservations.for_reclamation', $reclamation->id));
- $response->assertStatus(200);
- $this->assertCount(1, $response->json('reservations'));
- }
- // ==================== shortagesForReclamation (JSON API) ====================
- public function test_guest_cannot_get_shortages_for_reclamation(): void
- {
- $reclamation = Reclamation::factory()->create();
- $response = $this->get(route('spare_part_reservations.shortages_for_reclamation', $reclamation->id));
- $response->assertRedirect(route('login'));
- }
- public function test_admin_can_get_shortages_for_reclamation(): void
- {
- $sparePart = SparePart::factory()->create(['article' => 'SP-SHORTAGE-001']);
- $reclamation = Reclamation::factory()->create();
- Shortage::factory()->create([
- 'spare_part_id' => $sparePart->id,
- 'reclamation_id' => $reclamation->id,
- 'required_qty' => 10,
- 'reserved_qty' => 3,
- 'missing_qty' => 7,
- 'status' => Shortage::STATUS_OPEN,
- ]);
- $response = $this->actingAs($this->adminUser)
- ->getJson(route('spare_part_reservations.shortages_for_reclamation', $reclamation->id));
- $response->assertStatus(200);
- $response->assertJsonStructure([
- 'shortages' => [['id', 'spare_part_id', 'article', 'required_qty', 'missing_qty', 'status']],
- ]);
- $response->assertJsonFragment(['spare_part_id' => $sparePart->id]);
- }
- public function test_manager_can_get_shortages_for_reclamation(): void
- {
- $reclamation = Reclamation::factory()->create();
- $response = $this->actingAs($this->managerUser)
- ->getJson(route('spare_part_reservations.shortages_for_reclamation', $reclamation->id));
- $response->assertStatus(200);
- $response->assertJsonStructure(['shortages']);
- }
- // ==================== cancel ====================
- public function test_guest_cannot_cancel_reservation(): void
- {
- $reservation = Reservation::factory()->active()->create();
- $response = $this->post(route('spare_part_reservations.cancel', $reservation));
- $response->assertRedirect(route('login'));
- }
- public function test_admin_can_cancel_active_reservation(): void
- {
- $sparePart = SparePart::factory()->create();
- $order = SparePartOrder::factory()->inStock()->withQuantity(10)->forSparePart($sparePart)->create();
- $reclamation = Reclamation::factory()->create();
- $reservation = Reservation::factory()->create([
- 'spare_part_id' => $sparePart->id,
- 'spare_part_order_id' => $order->id,
- 'reclamation_id' => $reclamation->id,
- 'reserved_qty' => 5,
- 'status' => Reservation::STATUS_ACTIVE,
- ]);
- $response = $this->actingAs($this->adminUser)
- ->post(route('spare_part_reservations.cancel', $reservation), [
- 'reason' => 'Тестовая отмена',
- ]);
- $response->assertRedirect();
- $response->assertSessionHas('success');
- $this->assertDatabaseHas('reservations', [
- 'id' => $reservation->id,
- 'status' => Reservation::STATUS_CANCELLED,
- ]);
- }
- public function test_manager_can_cancel_active_reservation(): void
- {
- $sparePart = SparePart::factory()->create();
- $order = SparePartOrder::factory()->inStock()->withQuantity(10)->forSparePart($sparePart)->create();
- $reclamation = Reclamation::factory()->create();
- $reservation = Reservation::factory()->create([
- 'spare_part_id' => $sparePart->id,
- 'spare_part_order_id' => $order->id,
- 'reclamation_id' => $reclamation->id,
- 'reserved_qty' => 3,
- 'status' => Reservation::STATUS_ACTIVE,
- ]);
- $response = $this->actingAs($this->managerUser)
- ->post(route('spare_part_reservations.cancel', $reservation));
- $response->assertRedirect();
- $response->assertSessionHas('success');
- }
- public function test_cannot_cancel_inactive_reservation(): void
- {
- $reservation = Reservation::factory()->cancelled()->create();
- $response = $this->actingAs($this->adminUser)
- ->post(route('spare_part_reservations.cancel', $reservation));
- $response->assertRedirect();
- $response->assertSessionHas('error');
- // Статус не изменился
- $this->assertDatabaseHas('reservations', [
- 'id' => $reservation->id,
- 'status' => Reservation::STATUS_CANCELLED,
- ]);
- }
- public function test_cannot_cancel_issued_reservation(): void
- {
- $reservation = Reservation::factory()->issued()->create();
- $response = $this->actingAs($this->adminUser)
- ->post(route('spare_part_reservations.cancel', $reservation));
- $response->assertRedirect();
- $response->assertSessionHas('error');
- $this->assertDatabaseHas('reservations', [
- 'id' => $reservation->id,
- 'status' => Reservation::STATUS_ISSUED,
- ]);
- }
- // ==================== issue ====================
- public function test_guest_cannot_issue_reservation(): void
- {
- $reservation = Reservation::factory()->active()->create();
- $response = $this->post(route('spare_part_reservations.issue', $reservation));
- $response->assertRedirect(route('login'));
- }
- public function test_admin_can_issue_active_reservation(): void
- {
- $sparePart = SparePart::factory()->create();
- $order = SparePartOrder::factory()->inStock()->forSparePart($sparePart)->create([
- 'ordered_quantity' => 20,
- 'available_qty' => 20,
- ]);
- $reclamation = Reclamation::factory()->create();
- $reservation = Reservation::factory()->create([
- 'spare_part_id' => $sparePart->id,
- 'spare_part_order_id' => $order->id,
- 'reclamation_id' => $reclamation->id,
- 'reserved_qty' => 5,
- 'status' => Reservation::STATUS_ACTIVE,
- ]);
- $response = $this->actingAs($this->adminUser)
- ->post(route('spare_part_reservations.issue', $reservation), [
- 'note' => 'Тестовое списание',
- ]);
- $response->assertRedirect();
- $response->assertSessionHas('success');
- $this->assertDatabaseHas('reservations', [
- 'id' => $reservation->id,
- 'status' => Reservation::STATUS_ISSUED,
- ]);
- $this->assertDatabaseHas('spare_part_orders', [
- 'id' => $order->id,
- 'available_qty' => 15,
- ]);
- }
- public function test_manager_can_issue_active_reservation(): void
- {
- $sparePart = SparePart::factory()->create();
- $order = SparePartOrder::factory()->inStock()->forSparePart($sparePart)->create([
- 'ordered_quantity' => 10,
- 'available_qty' => 10,
- ]);
- $reclamation = Reclamation::factory()->create();
- $reservation = Reservation::factory()->create([
- 'spare_part_id' => $sparePart->id,
- 'spare_part_order_id' => $order->id,
- 'reclamation_id' => $reclamation->id,
- 'reserved_qty' => 3,
- 'status' => Reservation::STATUS_ACTIVE,
- ]);
- $response = $this->actingAs($this->managerUser)
- ->post(route('spare_part_reservations.issue', $reservation));
- $response->assertRedirect();
- $response->assertSessionHas('success');
- }
- public function test_cannot_issue_inactive_reservation(): void
- {
- $reservation = Reservation::factory()->cancelled()->create();
- $response = $this->actingAs($this->adminUser)
- ->post(route('spare_part_reservations.issue', $reservation));
- $response->assertRedirect();
- $response->assertSessionHas('error');
- }
- // ==================== issueAllForReclamation ====================
- public function test_guest_cannot_issue_all_for_reclamation(): void
- {
- $reclamation = Reclamation::factory()->create();
- $response = $this->post(route('spare_part_reservations.issue_all', $reclamation->id));
- $response->assertRedirect(route('login'));
- }
- public function test_admin_can_issue_all_for_reclamation(): void
- {
- $sparePart = SparePart::factory()->create();
- $order = SparePartOrder::factory()->inStock()->forSparePart($sparePart)->create([
- 'ordered_quantity' => 50,
- 'available_qty' => 50,
- ]);
- $reclamation = Reclamation::factory()->create();
- // Два активных резерва для рекламации
- Reservation::factory()->create([
- 'spare_part_id' => $sparePart->id,
- 'spare_part_order_id' => $order->id,
- 'reclamation_id' => $reclamation->id,
- 'reserved_qty' => 5,
- 'status' => Reservation::STATUS_ACTIVE,
- ]);
- Reservation::factory()->create([
- 'spare_part_id' => $sparePart->id,
- 'spare_part_order_id' => $order->id,
- 'reclamation_id' => $reclamation->id,
- 'reserved_qty' => 3,
- 'status' => Reservation::STATUS_ACTIVE,
- ]);
- $response = $this->actingAs($this->adminUser)
- ->post(route('spare_part_reservations.issue_all', $reclamation->id));
- $response->assertRedirect();
- $response->assertSessionHas('success');
- // Все резервы рекламации должны быть списаны
- $this->assertDatabaseMissing('reservations', [
- 'reclamation_id' => $reclamation->id,
- 'status' => Reservation::STATUS_ACTIVE,
- ]);
- }
- public function test_manager_can_issue_all_for_reclamation(): void
- {
- $reclamation = Reclamation::factory()->create();
- $response = $this->actingAs($this->managerUser)
- ->post(route('spare_part_reservations.issue_all', $reclamation->id));
- $response->assertRedirect();
- $response->assertSessionHas('success');
- }
- // ==================== cancelAllForReclamation ====================
- public function test_guest_cannot_cancel_all_for_reclamation(): void
- {
- $reclamation = Reclamation::factory()->create();
- $response = $this->post(route('spare_part_reservations.cancel_all', $reclamation->id));
- $response->assertRedirect(route('login'));
- }
- public function test_admin_can_cancel_all_for_reclamation(): void
- {
- $sparePart = SparePart::factory()->create();
- $order = SparePartOrder::factory()->inStock()->withQuantity(30)->forSparePart($sparePart)->create();
- $reclamation = Reclamation::factory()->create();
- Reservation::factory()->create([
- 'spare_part_id' => $sparePart->id,
- 'spare_part_order_id' => $order->id,
- 'reclamation_id' => $reclamation->id,
- 'reserved_qty' => 4,
- 'status' => Reservation::STATUS_ACTIVE,
- ]);
- Reservation::factory()->create([
- 'spare_part_id' => $sparePart->id,
- 'spare_part_order_id' => $order->id,
- 'reclamation_id' => $reclamation->id,
- 'reserved_qty' => 6,
- 'status' => Reservation::STATUS_ACTIVE,
- ]);
- $response = $this->actingAs($this->adminUser)
- ->post(route('spare_part_reservations.cancel_all', $reclamation->id), [
- 'reason' => 'Тестовая массовая отмена',
- ]);
- $response->assertRedirect();
- $response->assertSessionHas('success');
- // Все активные резервы рекламации отменены
- $this->assertDatabaseMissing('reservations', [
- 'reclamation_id' => $reclamation->id,
- 'status' => Reservation::STATUS_ACTIVE,
- ]);
- }
- public function test_manager_can_cancel_all_for_reclamation(): void
- {
- $reclamation = Reclamation::factory()->create();
- $response = $this->actingAs($this->managerUser)
- ->post(route('spare_part_reservations.cancel_all', $reclamation->id));
- $response->assertRedirect();
- $response->assertSessionHas('success');
- }
- public function test_cancel_all_includes_success_message_with_quantity(): void
- {
- $sparePart = SparePart::factory()->create();
- $order = SparePartOrder::factory()->inStock()->withQuantity(20)->forSparePart($sparePart)->create();
- $reclamation = Reclamation::factory()->create();
- Reservation::factory()->create([
- 'spare_part_id' => $sparePart->id,
- 'spare_part_order_id' => $order->id,
- 'reclamation_id' => $reclamation->id,
- 'reserved_qty' => 7,
- 'status' => Reservation::STATUS_ACTIVE,
- ]);
- $response = $this->actingAs($this->adminUser)
- ->post(route('spare_part_reservations.cancel_all', $reclamation->id));
- $response->assertSessionHas('success');
- $this->assertStringContainsString('7', session('success'));
- }
- }
|