adminUser = User::factory()->create(['role' => Role::ADMIN]); $this->managerUser = User::factory()->create(['role' => Role::MANAGER]); } // ==================== Authentication ==================== public function test_guest_cannot_access_reclamations_index(): void { $response = $this->get(route('reclamations.index')); $response->assertRedirect(route('login')); } public function test_authenticated_user_can_access_reclamations_index(): void { $response = $this->actingAs($this->managerUser) ->get(route('reclamations.index')); $response->assertStatus(200); $response->assertViewIs('reclamations.index'); } // ==================== Index ==================== public function test_reclamations_index_displays_reclamations(): void { $reclamation = Reclamation::factory()->create(); $response = $this->actingAs($this->managerUser) ->get(route('reclamations.index')); $response->assertStatus(200); } // ==================== Create ==================== public function test_can_create_reclamation_for_order(): void { $order = Order::factory()->create(); $product = Product::factory()->create(); $productSku = ProductSKU::factory()->create([ 'order_id' => $order->id, 'product_id' => $product->id, ]); $response = $this->actingAs($this->managerUser) ->post(route('reclamations.create', $order), [ 'skus' => [$productSku->id], ]); $response->assertRedirect(); $this->assertDatabaseHas('reclamations', [ 'order_id' => $order->id, 'user_id' => $this->managerUser->id, 'status_id' => Reclamation::STATUS_NEW, ]); } public function test_creating_reclamation_attaches_skus(): void { $order = Order::factory()->create(); $product = Product::factory()->create(); $productSku1 = ProductSKU::factory()->create([ 'order_id' => $order->id, 'product_id' => $product->id, ]); $productSku2 = ProductSKU::factory()->create([ 'order_id' => $order->id, 'product_id' => $product->id, ]); $this->actingAs($this->managerUser) ->post(route('reclamations.create', $order), [ 'skus' => [$productSku1->id, $productSku2->id], ]); $reclamation = Reclamation::where('order_id', $order->id)->first(); $this->assertCount(2, $reclamation->skus); } // ==================== Show ==================== public function test_can_view_reclamation_details(): void { $reclamation = Reclamation::factory()->create(); $response = $this->actingAs($this->managerUser) ->get(route('reclamations.show', $reclamation)); $response->assertStatus(200); $response->assertViewIs('reclamations.edit'); } // ==================== Update ==================== public function test_can_update_reclamation(): void { $reclamation = Reclamation::factory()->create([ 'reason' => 'Старая причина', ]); // Route uses POST, not PUT. All required fields must be sent. $response = $this->actingAs($this->managerUser) ->post(route('reclamations.update', $reclamation), [ 'user_id' => $reclamation->user_id, 'status_id' => $reclamation->status_id, 'create_date' => $reclamation->create_date, 'finish_date' => $reclamation->finish_date, 'reason' => 'Новая причина', 'guarantee' => 'Гарантия', 'whats_done' => 'Что сделано', ]); $response->assertRedirect(route('reclamations.show', $reclamation)); $this->assertDatabaseHas('reclamations', [ 'id' => $reclamation->id, 'reason' => 'Новая причина', ]); } // ==================== Delete ==================== public function test_can_delete_reclamation(): void { $reclamation = Reclamation::factory()->create(); $reclamationId = $reclamation->id; $response = $this->actingAs($this->adminUser) ->delete(route('reclamations.delete', $reclamation)); $response->assertRedirect(route('reclamations.index')); $this->assertDatabaseMissing('reclamations', ['id' => $reclamationId]); } // ==================== Photo Before Management ==================== public function test_can_upload_photo_before(): void { Storage::fake('public'); $reclamation = Reclamation::factory()->create(); // Use create() instead of image() to avoid GD extension requirement $photo = UploadedFile::fake()->create('photo_before.jpg', 100, 'image/jpeg'); $response = $this->actingAs($this->managerUser) ->post(route('reclamations.upload-photo-before', $reclamation), [ 'photo' => [$photo], ]); $response->assertRedirect(); $this->assertCount(1, $reclamation->fresh()->photos_before); } public function test_can_delete_photo_before(): void { Storage::fake('public'); $reclamation = Reclamation::factory()->create(); $file = File::factory()->create(); $reclamation->photos_before()->attach($file); $response = $this->actingAs($this->adminUser) ->delete(route('reclamations.delete-photo-before', [$reclamation, $file])); $response->assertRedirect(); $this->assertCount(0, $reclamation->fresh()->photos_before); } // ==================== Photo After Management ==================== public function test_can_upload_photo_after(): void { Storage::fake('public'); $reclamation = Reclamation::factory()->create(); // Use create() instead of image() to avoid GD extension requirement $photo = UploadedFile::fake()->create('photo_after.jpg', 100, 'image/jpeg'); $response = $this->actingAs($this->managerUser) ->post(route('reclamations.upload-photo-after', $reclamation), [ 'photo' => [$photo], ]); $response->assertRedirect(); $this->assertCount(1, $reclamation->fresh()->photos_after); } public function test_can_delete_photo_after(): void { Storage::fake('public'); $reclamation = Reclamation::factory()->create(); $file = File::factory()->create(); $reclamation->photos_after()->attach($file); // This route requires admin role $response = $this->actingAs($this->adminUser) ->delete(route('reclamations.delete-photo-after', [$reclamation, $file])); $response->assertRedirect(); $this->assertCount(0, $reclamation->fresh()->photos_after); } // ==================== Document Management ==================== public function test_can_upload_document(): void { Storage::fake('public'); $reclamation = Reclamation::factory()->create(); $document = UploadedFile::fake()->create('document.pdf', 100); $response = $this->actingAs($this->managerUser) ->post(route('reclamations.upload-document', $reclamation), [ 'document' => [$document], ]); $response->assertRedirect(); $this->assertCount(1, $reclamation->fresh()->documents); } public function test_can_delete_document(): void { Storage::fake('public'); $reclamation = Reclamation::factory()->create(); $file = File::factory()->create(); $reclamation->documents()->attach($file); // This route requires admin role $response = $this->actingAs($this->adminUser) ->delete(route('reclamations.delete-document', [$reclamation, $file])); $response->assertRedirect(); $this->assertCount(0, $reclamation->fresh()->documents); } // ==================== Act Management ==================== public function test_can_upload_act(): void { Storage::fake('public'); $reclamation = Reclamation::factory()->create(); $act = UploadedFile::fake()->create('act.pdf', 100); $response = $this->actingAs($this->managerUser) ->post(route('reclamations.upload-act', $reclamation), [ 'acts' => [$act], ]); $response->assertRedirect(); $this->assertCount(1, $reclamation->fresh()->acts); } public function test_can_delete_act(): void { Storage::fake('public'); $reclamation = Reclamation::factory()->create(); $file = File::factory()->create(); $reclamation->acts()->attach($file); // This route requires admin role $response = $this->actingAs($this->adminUser) ->delete(route('reclamations.delete-act', [$reclamation, $file])); $response->assertRedirect(); $this->assertCount(0, $reclamation->fresh()->acts); } // ==================== Spare Parts Reservation ==================== public function test_update_spare_parts_creates_reservations(): void { $reclamation = Reclamation::factory()->create(); $sparePart = SparePart::factory()->create(); // Create available stock SparePartOrder::factory() ->inStock() ->withDocuments(false) ->withQuantity(10) ->forSparePart($sparePart) ->create(); $response = $this->actingAs($this->managerUser) ->post(route('reclamations.update-spare-parts', $reclamation), [ 'rows' => [ [ 'spare_part_id' => $sparePart->id, 'quantity' => 3, 'with_documents' => false, ], ], ]); $response->assertRedirect(); // Check spare part is attached $this->assertTrue($reclamation->fresh()->spareParts->contains($sparePart->id)); // Check reservation was created $this->assertDatabaseHas('reservations', [ 'reclamation_id' => $reclamation->id, 'spare_part_id' => $sparePart->id, 'reserved_qty' => 3, 'status' => Reservation::STATUS_ACTIVE, ]); } public function test_update_spare_parts_cancels_removed_reservations(): void { $reclamation = Reclamation::factory()->create(); $sparePart = SparePart::factory()->create(); $order = SparePartOrder::factory() ->inStock() ->withDocuments(false) ->withQuantity(10) ->forSparePart($sparePart) ->create(); // Create existing reservation Reservation::factory() ->active() ->withQuantity(5) ->withDocuments(false) ->fromOrder($order) ->forReclamation($reclamation) ->create(); // Attach spare part $reclamation->spareParts()->attach($sparePart->id, [ 'quantity' => 5, 'with_documents' => false, 'reserved_qty' => 5, ]); // Send empty rows to remove spare part $response = $this->actingAs($this->managerUser) ->post(route('reclamations.update-spare-parts', $reclamation), [ 'rows' => [], ]); $response->assertRedirect(); // Check spare part is detached $this->assertFalse($reclamation->fresh()->spareParts->contains($sparePart->id)); // Check reservation was cancelled $this->assertDatabaseHas('reservations', [ 'reclamation_id' => $reclamation->id, 'spare_part_id' => $sparePart->id, 'status' => Reservation::STATUS_CANCELLED, ]); } // ==================== Details Management ==================== public function test_update_details_creates_reclamation_detail(): void { $reclamation = Reclamation::factory()->create(); $response = $this->actingAs($this->managerUser) ->post(route('reclamations.update-details', $reclamation), [ 'name' => ['Деталь 1', 'Деталь 2'], 'quantity' => ['2', '3'], // Controller casts to int, send as strings like form data ]); $response->assertRedirect(); $response->assertSessionHasNoErrors(); $this->assertDatabaseHas('reclamation_details', [ 'reclamation_id' => $reclamation->id, 'name' => 'Деталь 1', 'quantity' => 2, ]); $this->assertDatabaseHas('reclamation_details', [ 'reclamation_id' => $reclamation->id, 'name' => 'Деталь 2', 'quantity' => 3, ]); } public function test_update_details_removes_detail_with_zero_quantity(): void { $reclamation = Reclamation::factory()->create(); ReclamationDetail::create([ 'reclamation_id' => $reclamation->id, 'name' => 'Деталь для удаления', 'quantity' => 5, ]); $response = $this->actingAs($this->managerUser) ->post(route('reclamations.update-details', $reclamation), [ 'name' => ['Деталь для удаления'], 'quantity' => ['0'], // Send as string like form data ]); $response->assertRedirect(); $this->assertDatabaseMissing('reclamation_details', [ 'reclamation_id' => $reclamation->id, 'name' => 'Деталь для удаления', ]); } // ==================== Generation ==================== public function test_can_generate_reclamation_pack(): void { $reclamation = Reclamation::factory()->create(); $response = $this->actingAs($this->managerUser) ->get(route('order.generate-reclamation-pack', $reclamation)); $response->assertRedirect(); $response->assertSessionHas('success'); } public function test_can_generate_photos_before_pack(): void { $reclamation = Reclamation::factory()->create(); $response = $this->actingAs($this->managerUser) ->get(route('reclamation.generate-photos-before-pack', $reclamation)); $response->assertRedirect(); $response->assertSessionHas('success'); } public function test_can_generate_photos_after_pack(): void { $reclamation = Reclamation::factory()->create(); $response = $this->actingAs($this->managerUser) ->get(route('reclamation.generate-photos-after-pack', $reclamation)); $response->assertRedirect(); $response->assertSessionHas('success'); } }