Alexander Musikhin преди 2 седмици
родител
ревизия
87c09e741b

+ 1 - 1
app/Http/Controllers/UserController.php

@@ -107,7 +107,7 @@ class UserController extends Controller
      */
     public function destroy(User $user, DeleteUser $request)
     {
-        if($user == $request->user()) {
+        if($user->is($request->user())) {
             return redirect()->route('user.index')->with(['danger' => 'Нельзя удалить самого себя!']);
         } else {
             $user->delete();

+ 1 - 1
resources/views/catalog/edit.blade.php

@@ -36,7 +36,7 @@
                 @csrf
                 <div class="row">
                     <div class="col-xl-6">
-                        @include('partials.input', ['name' => 'year', 'title' => 'Год', 'value' => $product->year, 'disabled' => true])
+                        @include('partials.input', ['name' => 'year', 'title' => 'Год', 'value' => $product->year ?? year(), 'disabled' => true])
                         @include('partials.input', ['name' => 'article', 'title' => 'Артикул', 'required' => true, 'value' => $product->article ?? '', 'disabled' => !hasRole('admin'), 'disabled' => !hasRole('admin')])
                         @include('partials.input', ['name' => 'nomenclature_number', 'title' => 'Номер номенклатуры', 'required' => true, 'value' => $product->nomenclature_number ?? '', 'disabled' => !hasRole('admin')])
                         @include('partials.input', ['name' => 'name_tz', 'title' => 'Наименование по ТЗ', 'required' => true, 'value' => $product->name_tz ?? '', 'disabled' => !hasRole('admin')])

+ 222 - 0
tests/Feature/ProductControllerTest.php

@@ -0,0 +1,222 @@
+<?php
+
+namespace Tests\Feature;
+
+use App\Models\Product;
+use App\Models\Role;
+use App\Models\User;
+use Illuminate\Foundation\Testing\RefreshDatabase;
+use Tests\TestCase;
+
+class ProductControllerTest extends TestCase
+{
+    use RefreshDatabase;
+
+    protected bool $seed = true;
+
+    protected User $adminUser;
+    protected User $managerUser;
+
+    protected function setUp(): void
+    {
+        parent::setUp();
+        $this->adminUser = User::factory()->create(['role' => Role::ADMIN]);
+        $this->managerUser = User::factory()->create(['role' => Role::MANAGER]);
+    }
+
+    private function validProductData(): array
+    {
+        return [
+            'article' => 'TEST-001',
+            'name_tz' => 'Тестовая горка',
+            'type_tz' => 'Горка',
+            'nomenclature_number' => '123456',
+            'sizes' => '1000x2000x1500',
+            'manufacturer' => 'ООО Тест',
+            'manufacturer_name' => 'ООО Тест Производитель',
+            'unit' => 'шт',
+            'type' => 'standard',
+            'product_price' => 100000,
+            'installation_price' => 20000,
+            'total_price' => 120000,
+            'note' => 'тестовое примечание',
+            'year' => date('Y'),
+        ];
+    }
+
+    // --- Guest redirects (3) ---
+
+    public function test_guest_cannot_access_catalog_index(): void
+    {
+        $response = $this->get(route('catalog.index'));
+
+        $response->assertRedirect(route('login'));
+    }
+
+    public function test_guest_cannot_access_catalog_create(): void
+    {
+        $response = $this->get(route('catalog.create'));
+
+        $response->assertRedirect(route('login'));
+    }
+
+    public function test_guest_cannot_store_product(): void
+    {
+        $response = $this->post(route('catalog.store'), $this->validProductData());
+
+        $response->assertRedirect(route('login'));
+    }
+
+    // --- Authorization (2) ---
+
+    public function test_manager_cannot_create_product(): void
+    {
+        $response = $this->actingAs($this->managerUser)
+            ->post(route('catalog.store'), $this->validProductData());
+
+        $response->assertForbidden();
+    }
+
+    public function test_manager_cannot_delete_product(): void
+    {
+        $product = Product::factory()->create();
+
+        $response = $this->actingAs($this->managerUser)
+            ->delete(route('catalog.delete', $product));
+
+        $response->assertForbidden();
+    }
+
+    // --- Index (2) ---
+
+    public function test_admin_can_access_catalog_index(): void
+    {
+        $response = $this->actingAs($this->adminUser)
+            ->get(route('catalog.index'));
+
+        $response->assertOk();
+        $response->assertViewIs('catalog.index');
+    }
+
+    public function test_manager_can_access_catalog_index(): void
+    {
+        $response = $this->actingAs($this->managerUser)
+            ->get(route('catalog.index'));
+
+        $response->assertOk();
+        $response->assertViewIs('catalog.index');
+    }
+
+    // --- Create form (1) ---
+
+    public function test_admin_can_access_catalog_create(): void
+    {
+        $response = $this->actingAs($this->adminUser)
+            ->get(route('catalog.create'));
+
+        $response->assertOk();
+        $response->assertViewIs('catalog.edit');
+    }
+
+    // --- Show (2) ---
+
+    public function test_admin_can_view_product(): void
+    {
+        $product = Product::factory()->create();
+
+        $response = $this->actingAs($this->adminUser)
+            ->get(route('catalog.show', $product));
+
+        $response->assertOk();
+        $response->assertViewIs('catalog.edit');
+    }
+
+    public function test_manager_can_view_product(): void
+    {
+        $product = Product::factory()->create();
+
+        $response = $this->actingAs($this->managerUser)
+            ->get(route('catalog.show', $product));
+
+        $response->assertOk();
+        $response->assertViewIs('catalog.edit');
+    }
+
+    // --- Store (3) ---
+
+    public function test_admin_can_create_product(): void
+    {
+        $data = $this->validProductData();
+
+        $response = $this->actingAs($this->adminUser)
+            ->post(route('catalog.store'), $data);
+
+        $response->assertRedirect();
+        $this->assertDatabaseHas('products', ['article' => $data['article']]);
+    }
+
+    public function test_store_product_requires_article(): void
+    {
+        $data = $this->validProductData();
+        unset($data['article']);
+
+        $response = $this->actingAs($this->adminUser)
+            ->post(route('catalog.store'), $data);
+
+        $response->assertSessionHasErrors('article');
+    }
+
+    public function test_store_product_requires_name_tz(): void
+    {
+        $data = $this->validProductData();
+        unset($data['name_tz']);
+
+        $response = $this->actingAs($this->adminUser)
+            ->post(route('catalog.store'), $data);
+
+        $response->assertSessionHasErrors('name_tz');
+    }
+
+    // --- Update (2) ---
+
+    public function test_admin_can_update_product(): void
+    {
+        $product = Product::factory()->create();
+        $data = $this->validProductData();
+        $data['article'] = 'UPDATED-001';
+        $data['name_tz'] = 'Обновлённая горка';
+
+        $response = $this->actingAs($this->adminUser)
+            ->post(route('catalog.update', $product), $data);
+
+        $response->assertRedirect();
+        $this->assertDatabaseHas('products', [
+            'id' => $product->id,
+            'article' => 'UPDATED-001',
+            'name_tz' => 'Обновлённая горка',
+        ]);
+    }
+
+    public function test_manager_cannot_update_product(): void
+    {
+        $product = Product::factory()->create();
+
+        $response = $this->actingAs($this->managerUser)
+            ->post(route('catalog.update', $product), $this->validProductData());
+
+        $response->assertForbidden();
+    }
+
+    // --- Delete (1) ---
+
+    public function test_admin_can_delete_product(): void
+    {
+        $product = Product::factory()->create();
+
+        $response = $this->actingAs($this->adminUser)
+            ->delete(route('catalog.delete', $product));
+
+        $response->assertRedirect();
+        $this->assertSoftDeleted('products', ['id' => $product->id]);
+    }
+}

+ 184 - 0
tests/Feature/ProductSKUControllerTest.php

@@ -0,0 +1,184 @@
+<?php
+
+namespace Tests\Feature;
+
+use App\Jobs\ExportMafJob;
+use App\Models\Order;
+use App\Models\Product;
+use App\Models\ProductSKU;
+use App\Models\Role;
+use App\Models\User;
+use Illuminate\Foundation\Testing\RefreshDatabase;
+use Illuminate\Support\Facades\Bus;
+use Tests\TestCase;
+
+class ProductSKUControllerTest extends TestCase
+{
+    use RefreshDatabase;
+
+    protected $seed = true;
+
+    private User $adminUser;
+    private User $managerUser;
+    private User $brigadierUser;
+
+    protected function setUp(): void
+    {
+        parent::setUp();
+
+        $this->adminUser = User::factory()->create(['role' => Role::ADMIN]);
+        $this->managerUser = User::factory()->create(['role' => Role::MANAGER]);
+        $this->brigadierUser = User::factory()->create(['role' => Role::BRIGADIER]);
+    }
+
+    // ==================== Guest redirects ====================
+
+    public function test_guest_cannot_access_product_sku_index(): void
+    {
+        $response = $this->get(route('product_sku.index'));
+
+        $response->assertRedirect(route('login'));
+    }
+
+    public function test_guest_cannot_access_product_sku_show(): void
+    {
+        $sku = ProductSKU::factory()->create();
+
+        $response = $this->get(route('product_sku.show', $sku));
+
+        $response->assertRedirect(route('login'));
+    }
+
+    public function test_guest_cannot_update_product_sku(): void
+    {
+        $sku = ProductSKU::factory()->create();
+
+        $response = $this->post(route('product_sku.update', $sku), []);
+
+        $response->assertRedirect(route('login'));
+    }
+
+    // ==================== Authorization ====================
+
+    public function test_brigadier_cannot_access_product_sku_index(): void
+    {
+        $response = $this->actingAs($this->brigadierUser)
+            ->get(route('product_sku.index'));
+
+        $response->assertStatus(403);
+    }
+
+    public function test_manager_cannot_export_mafs(): void
+    {
+        $response = $this->actingAs($this->managerUser)
+            ->post(route('mafs.export'));
+
+        $response->assertStatus(403);
+    }
+
+    // ==================== Index ====================
+
+    public function test_admin_can_access_product_sku_index(): void
+    {
+        $response = $this->actingAs($this->adminUser)
+            ->get(route('product_sku.index'));
+
+        $response->assertStatus(200);
+        $response->assertViewIs('products_sku.index');
+    }
+
+    public function test_manager_can_access_product_sku_index(): void
+    {
+        $response = $this->actingAs($this->managerUser)
+            ->get(route('product_sku.index'));
+
+        $response->assertStatus(200);
+        $response->assertViewIs('products_sku.index');
+    }
+
+    // ==================== Show ====================
+
+    public function test_admin_can_view_product_sku(): void
+    {
+        $sku = ProductSKU::factory()->create();
+
+        $response = $this->actingAs($this->adminUser)
+            ->get(route('product_sku.show', $sku));
+
+        $response->assertStatus(200);
+        $response->assertViewIs('products_sku.edit');
+    }
+
+    public function test_manager_can_view_product_sku(): void
+    {
+        $sku = ProductSKU::factory()->create();
+
+        $response = $this->actingAs($this->managerUser)
+            ->get(route('product_sku.show', $sku));
+
+        $response->assertStatus(200);
+        $response->assertViewIs('products_sku.edit');
+    }
+
+    // ==================== Update ====================
+
+    public function test_admin_can_update_product_sku(): void
+    {
+        $product = Product::factory()->create();
+        $order = Order::factory()->create();
+        $sku = ProductSKU::factory()->create([
+            'product_id' => $product->id,
+            'order_id'   => $order->id,
+        ]);
+
+        $response = $this->actingAs($this->adminUser)
+            ->post(route('product_sku.update', $sku), [
+                'product_id'     => $product->id,
+                'order_id'       => $order->id,
+                'status'         => 'shipped',
+                'factory_number' => 'FN-999999',
+                'comment'        => 'Updated by admin',
+            ]);
+
+        $response->assertRedirect();
+        $this->assertDatabaseHas('products_sku', [
+            'id'             => $sku->id,
+            'factory_number' => 'FN-999999',
+            'comment'        => 'Updated by admin',
+        ]);
+    }
+
+    public function test_manager_can_update_product_sku(): void
+    {
+        $product = Product::factory()->create();
+        $order = Order::factory()->create();
+        $sku = ProductSKU::factory()->create([
+            'product_id' => $product->id,
+            'order_id'   => $order->id,
+        ]);
+
+        $response = $this->actingAs($this->managerUser)
+            ->post(route('product_sku.update', $sku), [
+                'product_id'     => $product->id,
+                'order_id'       => $order->id,
+                'status'         => 'needs',
+                'factory_number' => 'FN-777777',
+            ]);
+
+        $response->assertRedirect();
+    }
+
+    // ==================== Export ====================
+
+    public function test_admin_can_export_mafs(): void
+    {
+        Bus::fake();
+
+        $response = $this->actingAs($this->adminUser)
+            ->post(route('mafs.export'));
+
+        $response->assertRedirect(route('product_sku.index'));
+        $response->assertSessionHas('success');
+        Bus::assertDispatched(ExportMafJob::class);
+    }
+}

+ 93 - 0
tests/Feature/ReportControllerTest.php

@@ -0,0 +1,93 @@
+<?php
+
+namespace Tests\Feature;
+
+use App\Models\Role;
+use App\Models\User;
+use Illuminate\Foundation\Testing\RefreshDatabase;
+use Tests\TestCase;
+
+class ReportControllerTest extends TestCase
+{
+    use RefreshDatabase;
+
+    protected bool $seed = true;
+
+    protected User $adminUser;
+    protected User $managerUser;
+    protected User $brigadierUser;
+
+    protected function setUp(): void
+    {
+        parent::setUp();
+        $this->adminUser    = User::factory()->create(['role' => Role::ADMIN]);
+        $this->managerUser  = User::factory()->create(['role' => Role::MANAGER]);
+        $this->brigadierUser = User::factory()->create(['role' => Role::BRIGADIER]);
+    }
+
+    public function test_guest_cannot_access_reports_index(): void
+    {
+        $response = $this->get(route('reports.index'));
+        $response->assertRedirect(route('login'));
+    }
+
+    public function test_brigadier_cannot_access_reports(): void
+    {
+        $response = $this->actingAs($this->brigadierUser)->get(route('reports.index'));
+        $response->assertStatus(403);
+    }
+
+    public function test_admin_can_access_reports_index(): void
+    {
+        $response = $this->actingAs($this->adminUser)->get(route('reports.index'));
+        $response->assertStatus(200);
+        $response->assertViewIs('reports.index');
+    }
+
+    public function test_manager_can_access_reports_index(): void
+    {
+        $response = $this->actingAs($this->managerUser)->get(route('reports.index'));
+        $response->assertStatus(200);
+        $response->assertViewIs('reports.index');
+    }
+
+    public function test_reports_index_contains_required_view_data(): void
+    {
+        $response = $this->actingAs($this->adminUser)->get(route('reports.index'));
+        $response->assertViewHas('totalOrders');
+        $response->assertViewHas('totalMafs');
+        $response->assertViewHas('managers');
+    }
+
+    public function test_reports_index_has_done_orders_count(): void
+    {
+        $response = $this->actingAs($this->adminUser)->get(route('reports.index'));
+        $response->assertViewHas('doneOrders', fn($v) => $v >= 0);
+    }
+
+    public function test_reports_index_has_mount_orders_count(): void
+    {
+        $response = $this->actingAs($this->adminUser)->get(route('reports.index'));
+        $response->assertViewHas('mountOrders', fn($v) => $v >= 0);
+    }
+
+    public function test_reports_index_has_reclamations_data(): void
+    {
+        $response = $this->actingAs($this->adminUser)->get(route('reports.index'));
+        $response->assertViewHas('totalReclamations');
+        $response->assertViewHas('reclamationStatuses');
+    }
+
+    public function test_reports_index_has_by_district_data(): void
+    {
+        $response = $this->actingAs($this->adminUser)->get(route('reports.index'));
+        $response->assertViewHas('byDistrict', fn($v) => is_array($v));
+    }
+
+    public function test_reports_index_has_total_sum(): void
+    {
+        $response = $this->actingAs($this->adminUser)->get(route('reports.index'));
+        $response->assertViewHas('totalSum');
+        $response->assertViewHas('totalDoneSum');
+    }
+}

+ 232 - 0
tests/Feature/UserControllerTest.php

@@ -0,0 +1,232 @@
+<?php
+
+namespace Tests\Feature;
+
+use App\Models\Role;
+use App\Models\User;
+use Illuminate\Foundation\Testing\RefreshDatabase;
+use Tests\TestCase;
+
+class UserControllerTest 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]);
+    }
+
+    private function validUserData(): array
+    {
+        return [
+            'name'     => 'Тест Пользователь',
+            'email'    => 'test_new@example.com',
+            'password' => 'password123',
+            'role'     => Role::MANAGER,
+        ];
+    }
+
+    // ==================== Guest redirects ====================
+
+    public function test_guest_cannot_access_users_index(): void
+    {
+        $response = $this->get(route('user.index'));
+
+        $response->assertRedirect(route('login'));
+    }
+
+    public function test_guest_cannot_access_user_create(): void
+    {
+        $response = $this->get(route('user.create'));
+
+        $response->assertRedirect(route('login'));
+    }
+
+    public function test_guest_cannot_store_user(): void
+    {
+        $response = $this->post(route('user.store'), $this->validUserData());
+
+        $response->assertRedirect(route('login'));
+    }
+
+    public function test_guest_cannot_delete_user(): void
+    {
+        $user = User::factory()->create(['role' => Role::MANAGER]);
+
+        $response = $this->delete(route('user.destroy', $user));
+
+        $response->assertRedirect(route('login'));
+    }
+
+    // ==================== Authorization - manager gets 403 ====================
+
+    public function test_manager_cannot_access_users_index(): void
+    {
+        $response = $this->actingAs($this->managerUser)
+            ->get(route('user.index'));
+
+        $response->assertStatus(403);
+    }
+
+    public function test_manager_cannot_create_user(): void
+    {
+        $response = $this->actingAs($this->managerUser)
+            ->post(route('user.store'), $this->validUserData());
+
+        $response->assertStatus(403);
+    }
+
+    public function test_manager_cannot_delete_user(): void
+    {
+        $user = User::factory()->create(['role' => Role::MANAGER]);
+
+        $response = $this->actingAs($this->managerUser)
+            ->delete(route('user.destroy', $user));
+
+        $response->assertStatus(403);
+    }
+
+    // ==================== Index ====================
+
+    public function test_admin_can_access_users_index(): void
+    {
+        $response = $this->actingAs($this->adminUser)
+            ->get(route('user.index'));
+
+        $response->assertStatus(200);
+        $response->assertViewIs('users.index');
+    }
+
+    // ==================== Create form ====================
+
+    public function test_admin_can_access_user_create(): void
+    {
+        $response = $this->actingAs($this->adminUser)
+            ->get(route('user.create'));
+
+        $response->assertStatus(200);
+        $response->assertViewIs('users.edit');
+    }
+
+    // ==================== Show ====================
+
+    public function test_admin_can_view_user(): void
+    {
+        $user = User::factory()->create(['role' => Role::MANAGER]);
+
+        $response = $this->actingAs($this->adminUser)
+            ->get(route('user.show', $user));
+
+        $response->assertStatus(200);
+        $response->assertViewIs('users.edit');
+    }
+
+    // ==================== Store - create new user ====================
+
+    public function test_admin_can_create_user(): void
+    {
+        $data = $this->validUserData();
+
+        $response = $this->actingAs($this->adminUser)
+            ->post(route('user.store'), $data);
+
+        $response->assertRedirect(route('user.index'));
+        $this->assertDatabaseHas('users', ['email' => $data['email']]);
+    }
+
+    public function test_store_requires_name(): void
+    {
+        $data = $this->validUserData();
+        unset($data['name']);
+
+        $response = $this->actingAs($this->adminUser)
+            ->post(route('user.store'), $data);
+
+        $response->assertSessionHasErrors('name');
+    }
+
+    public function test_store_requires_password_for_new_user(): void
+    {
+        $data = $this->validUserData();
+        unset($data['password']);
+
+        $response = $this->actingAs($this->adminUser)
+            ->post(route('user.store'), $data);
+
+        $response->assertSessionHasErrors('password');
+    }
+
+    // ==================== Update existing user ====================
+
+    public function test_admin_can_update_user(): void
+    {
+        $user = User::factory()->create(['role' => Role::MANAGER]);
+        $newName = 'Обновлённое Имя';
+
+        $response = $this->actingAs($this->adminUser)
+            ->post(route('user.store'), [
+                'id'   => $user->id,
+                'name' => $newName,
+                'role' => Role::MANAGER,
+            ]);
+
+        $response->assertRedirect(route('user.index'));
+        $this->assertDatabaseHas('users', ['id' => $user->id, 'name' => $newName]);
+    }
+
+    // ==================== Delete ====================
+
+    public function test_admin_can_delete_user(): void
+    {
+        $user = User::factory()->create(['role' => Role::MANAGER]);
+
+        $response = $this->actingAs($this->adminUser)
+            ->delete(route('user.destroy', $user));
+
+        $response->assertRedirect(route('user.index'));
+        $this->assertSoftDeleted('users', ['id' => $user->id]);
+    }
+
+    public function test_admin_cannot_delete_self(): void
+    {
+        $response = $this->actingAs($this->adminUser)
+            ->delete(route('user.destroy', $this->adminUser));
+
+        $response->assertRedirect(route('user.index'));
+        $response->assertSessionHas('danger');
+    }
+
+    // ==================== Undelete ====================
+
+    public function test_admin_can_restore_deleted_user(): void
+    {
+        $user = User::factory()->create(['role' => Role::MANAGER]);
+        $user->delete();
+
+        $response = $this->actingAs($this->adminUser)
+            ->post(route('user.undelete', $user->id));
+
+        $response->assertRedirect();
+        $this->assertDatabaseHas('users', ['id' => $user->id, 'deleted_at' => null]);
+    }
+
+    // ==================== Impersonate ====================
+
+    public function test_admin_can_impersonate_user(): void
+    {
+        $targetUser = User::factory()->create(['role' => Role::MANAGER]);
+
+        $response = $this->actingAs($this->adminUser)
+            ->post(route('user.impersonate', $targetUser));
+
+        $response->assertRedirect(route('home'));
+        $response->assertSessionHas('impersonator_id', $this->adminUser->id);
+    }
+}