Alexander Musikhin 1 день назад
Родитель
Сommit
068058d427

+ 9 - 0
app/Http/Controllers/ProductSKUController.php

@@ -80,6 +80,15 @@ class ProductSKUController extends Controller
 //        dump($q->toRawSql());
         $this->data['products_sku'] = $q->paginate($this->data['per_page'])->withQueryString();
         $this->data['nav'] = $nav;
+        $this->data['maf_registry_files'] = $request->user()?->hasRole([Role::ADMIN, Role::ASSISTANT_HEAD])
+            ? File::query()
+                ->with('user:id,name')
+                ->where('path', 'like', 'export/maf-registry/%')
+                ->latest()
+                ->limit(20)
+                ->get()
+            : collect();
+
         return view('products_sku.index', $this->data);
     }
 

+ 24 - 4
app/Jobs/ExportMafRegistryJob.php

@@ -4,10 +4,10 @@ namespace App\Jobs;
 
 use App\Events\SendWebSocketMessageEvent;
 use App\Services\ExportMafRegistryService;
-use Exception;
 use Illuminate\Contracts\Queue\ShouldQueue;
 use Illuminate\Foundation\Queue\Queueable;
 use Illuminate\Support\Facades\Log;
+use Throwable;
 
 class ExportMafRegistryJob implements ShouldQueue
 {
@@ -26,9 +26,29 @@ class ExportMafRegistryJob implements ShouldQueue
             $link = (new ExportMafRegistryService())->handle($this->userId, $this->updNumber, $this->year);
             Log::info('ExportMafRegistry job done!');
             event(new SendWebSocketMessageEvent('Реестр на оплату сформирован!', $this->userId, ['success' => true, 'link' => $link]));
-        } catch (Exception $e) {
-            Log::error('ExportMafRegistry job failed! ' . $e->getMessage());
-            event(new SendWebSocketMessageEvent('Ошибка формирования реестра на оплату! ', $this->userId, ['error' => $e->getMessage()]));
+        } catch (Throwable $e) {
+            $this->notifyFailure($e);
         }
     }
+
+    public function failed(Throwable $e): void
+    {
+        $this->notifyFailure($e);
+    }
+
+    private function notifyFailure(Throwable $e): void
+    {
+        Log::error('ExportMafRegistry job failed.', [
+            'user_id' => $this->userId,
+            'upd_number' => $this->updNumber,
+            'year' => $this->year,
+            'exception' => $e,
+        ]);
+
+        event(new SendWebSocketMessageEvent(
+            'Ошибка формирования реестра на оплату! ' . $e->getMessage(),
+            $this->userId,
+            ['error' => $e->getMessage()]
+        ));
+    }
 }

+ 34 - 0
resources/views/products_sku/index.blade.php

@@ -100,6 +100,40 @@
                         @include('partials.input', ['name' => 'upd_number', 'title' => 'Номер УПД', 'required' => true])
                         @include('partials.submit', ['name' => 'Сформировать'])
                     </form>
+
+                    <hr>
+
+                    <h6 class="mb-2">Сформированные реестры</h6>
+                    @if($maf_registry_files->isNotEmpty())
+                        <div class="table-responsive">
+                            <table class="table table-sm align-middle mb-0">
+                                <thead>
+                                    <tr>
+                                        <th>Файл</th>
+                                        <th>Дата</th>
+                                        <th>Пользователь</th>
+                                        <th class="text-end">Скачать</th>
+                                    </tr>
+                                </thead>
+                                <tbody>
+                                    @foreach($maf_registry_files as $registryFile)
+                                        <tr>
+                                            <td>{{ $registryFile->original_name }}</td>
+                                            <td>{{ $registryFile->created_at?->format('d.m.Y H:i') }}</td>
+                                            <td>{{ $registryFile->user?->name ?? '' }}</td>
+                                            <td class="text-end">
+                                                <a href="{{ $registryFile->link }}" class="btn btn-sm btn-outline-primary" target="_blank">
+                                                    <i class="bi bi-download"></i>
+                                                </a>
+                                            </td>
+                                        </tr>
+                                    @endforeach
+                                </tbody>
+                            </table>
+                        </div>
+                    @else
+                        <div class="text-muted">Сформированных реестров пока нет.</div>
+                    @endif
                 </div>
             </div>
         </div>

+ 42 - 0
tests/Feature/ProductSKUControllerTest.php

@@ -4,6 +4,7 @@ namespace Tests\Feature;
 
 use App\Jobs\ExportMafJob;
 use App\Jobs\ExportMafRegistryJob;
+use App\Models\File;
 use App\Models\Order;
 use App\Models\Permission;
 use App\Models\Product;
@@ -121,6 +122,47 @@ class ProductSKUControllerTest extends TestCase
         $response->assertViewIs('products_sku.index');
     }
 
+    public function test_admin_sees_generated_maf_registry_files_on_index(): void
+    {
+        $registryFile = File::factory()->create([
+            'user_id' => $this->adminUser->id,
+            'path' => 'export/maf-registry/registry.xlsx',
+            'link' => url('/storage/export/maf-registry/registry.xlsx'),
+            'original_name' => 'registry.xlsx',
+            'is_generated' => true,
+        ]);
+        File::factory()->create([
+            'path' => 'export/orders/orders.xlsx',
+            'original_name' => 'orders.xlsx',
+        ]);
+
+        $response = $this->actingAs($this->adminUser)
+            ->get(route('product_sku.index'));
+
+        $response->assertOk();
+        $response->assertViewHas('maf_registry_files', function ($files) use ($registryFile): bool {
+            return $files->contains('id', $registryFile->id)
+                && $files->doesntContain('original_name', 'orders.xlsx');
+        });
+        $response->assertSee('registry.xlsx');
+    }
+
+    public function test_manager_does_not_receive_generated_maf_registry_files(): void
+    {
+        File::factory()->create([
+            'path' => 'export/maf-registry/registry.xlsx',
+            'original_name' => 'registry.xlsx',
+            'is_generated' => true,
+        ]);
+
+        $response = $this->actingAs($this->managerUser)
+            ->get(route('product_sku.index'));
+
+        $response->assertOk();
+        $response->assertViewHas('maf_registry_files', fn ($files): bool => $files->isEmpty());
+        $response->assertDontSee('registry.xlsx');
+    }
+
     public function test_maf_filter_options_include_empty_marker_for_factory_number(): void
     {
         ProductSKU::factory()->create(['factory_number' => null]);