Ver código fonte

Added convert to pdf, added archive photos and download

Alexander Musikhin 7 meses atrás
pai
commit
579e9edc74

+ 7 - 0
app/Http/Controllers/OrderController.php

@@ -3,6 +3,7 @@
 namespace App\Http\Controllers;
 
 use App\Http\Requests\Order\StoreOrderRequest;
+use App\Jobs\GenerateFilesPack;
 use App\Jobs\GenerateHandoverPack;
 use App\Jobs\GenerateInstallationPack;
 use App\Models\Dictionary\Area;
@@ -327,4 +328,10 @@ class OrderController extends Controller
         return redirect()->route('order.show', $order)->with(['success' => 'Задача генерации документов создана!']);
     }
 
+    public function generatePhotosPack(Order $order)
+    {
+        GenerateFilesPack::dispatch($order, $order->photos, auth()->user()->id, 'Фото');
+        return redirect()->back()->with(['success' => 'Задача архивации создана!']);
+    }
+
 }

+ 14 - 0
app/Http/Controllers/ReclamationController.php

@@ -5,6 +5,7 @@ namespace App\Http\Controllers;
 use App\Http\Requests\CreateReclamationRequest;
 use App\Http\Requests\StoreReclamationDetailsRequest;
 use App\Http\Requests\StoreReclamationRequest;
+use App\Jobs\GenerateFilesPack;
 use App\Jobs\GenerateReclamationPack;
 use App\Models\File;
 use App\Models\Order;
@@ -215,4 +216,17 @@ class ReclamationController extends Controller
         GenerateReclamationPack::dispatch($reclamation, auth()->user()->id);
         return redirect()->route('reclamations.show', $reclamation)->with(['success' => 'Задача генерации документов создана!']);
     }
+
+    public function generatePhotosBeforePack(Reclamation $reclamation)
+    {
+        GenerateFilesPack::dispatch($reclamation, $reclamation->photos_before, auth()->user()->id, 'Фото проблемы');
+        return redirect()->back()->with(['success' => 'Задача архивации создана!']);
+    }
+
+    public function generatePhotosAfterPack(Reclamation $reclamation)
+    {
+        GenerateFilesPack::dispatch($reclamation, $reclamation->photos_after, auth()->user()->id, 'Фото после');
+        return redirect()->back()->with(['success' => 'Задача архивации создана!']);
+    }
+
 }

+ 46 - 0
app/Jobs/GenerateFilesPack.php

@@ -0,0 +1,46 @@
+<?php
+
+namespace App\Jobs;
+
+use App\Events\SendWebSocketMessageEvent;
+use App\Services\GenerateDocumentsService;
+use Exception;
+use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Foundation\Queue\Queueable;
+use Illuminate\Support\Collection;
+use Illuminate\Support\Facades\Log;
+
+class GenerateFilesPack implements ShouldQueue
+{
+    use Queueable;
+
+    /**
+     * Execute the job.
+     */
+    public function handle(): void
+    {
+        //
+    }
+
+    /**
+     * Create a new job instance.
+     */
+    public function __construct(
+        private readonly Model $model,
+        private readonly Collection $collection,
+        private readonly int $userId,
+        private readonly string $name = 'files',
+    )
+    {
+        try {
+            $file = (new GenerateDocumentsService())->generateFilePack($this->collection, $this->userId, $name);
+            $model->documents()->attach($file->id);
+            Log::info('Generate installation pack finished!');
+            event(new SendWebSocketMessageEvent('Пакет файлов готов!', $this->userId, ['success' => true, 'link' => $file->link]));
+        } catch (Exception $e) {
+            Log::error('Generate installation pack failed! ' . $e->getMessage());
+            event(new SendWebSocketMessageEvent('Ошибка создания пакета файлов! ', $this->userId, ['error' => $e->getMessage()]));
+        }
+    }
+}

+ 56 - 6
app/Services/GenerateDocumentsService.php

@@ -8,6 +8,7 @@ use App\Models\Contract;
 use App\Models\Order;
 use App\Models\Reclamation;
 use Exception;
+use Illuminate\Support\Collection;
 use Illuminate\Support\Facades\Storage;
 use Illuminate\Support\Str;
 use PhpOffice\PhpSpreadsheet\IOFactory;
@@ -338,6 +339,12 @@ class GenerateDocumentsService
         $writer->save(storage_path('app/public/orders/') . $order->id . '/tmp/' . $fileName);
     }
 
+    /**
+     * @param Reclamation $reclamation
+     * @param int $userId
+     * @return string
+     * @throws Exception
+     */
     public function generateReclamationPack(Reclamation $reclamation, int $userId): string
     {
         Storage::disk('public')->makeDirectory('reclamations/' . $reclamation->id . '/tmp/' . $reclamation->order->object_address . '/ФОТО НАРУШЕНИЯ/');
@@ -366,6 +373,9 @@ class GenerateDocumentsService
         return $fileModel?->link ?? '';
     }
 
+    /**
+     * @throws Exception
+     */
     private function generateReclamationOrder(Reclamation $reclamation): void
     {
         $inputFileType = 'Xlsx';
@@ -393,12 +403,19 @@ class GenerateDocumentsService
         // save file
         $fileName = 'Монтажная заявка - ' . $reclamation->order->object_address . '.xlsx';
         $writer = new Xlsx($spreadsheet);
-        Storage::disk('public')->makeDirectory('reclamations/' . $reclamation->id . '/tmp/' . $reclamation->order->object_address);
+        $fd = 'reclamations/' . $reclamation->id . '/tmp/' . $reclamation->order->object_address;
+        Storage::disk('public')->makeDirectory($fd);
+        $fp = storage_path('app/public/reclamations/') . $reclamation->id . '/tmp/' . $reclamation->order->object_address . '/' . $fileName;
+        Storage::disk('public')->delete($fd . '/' . $fileName);
+        $writer->save($fp);
+        PdfConverterClient::convert($fp);
 
-        $writer->save(storage_path('app/public/reclamations/') . $reclamation->id . '/tmp/' . $reclamation->order->object_address . '/' . $fileName);
     }
 
-    public function generateReclamationAct(Reclamation $reclamation): void
+    /**
+     * @throws Exception
+     */
+    private function generateReclamationAct(Reclamation $reclamation): void
     {
         $inputFileType = 'Xlsx';
         $inputFileName = './templates/ReclamationAct.xlsx';
@@ -431,11 +448,18 @@ class GenerateDocumentsService
         // save file
         $fileName = 'Акт - ' . $reclamation->order->object_address . '.xlsx';
         $writer = new Xlsx($spreadsheet);
-        Storage::disk('public')->makeDirectory('reclamations/' . $reclamation->id . '/tmp/' . $reclamation->order->object_address);
-        $writer->save(storage_path('app/public/reclamations/') . $reclamation->id . '/tmp/' . $reclamation->order->object_address . '/' . $fileName);
+        $fd = 'reclamations/' . $reclamation->id . '/tmp/' . $reclamation->order->object_address;
+        Storage::disk('public')->makeDirectory($fd);
+        $fp = storage_path('app/public/reclamations/') . $reclamation->id . '/tmp/' . $reclamation->order->object_address . '/' . $fileName;
+        Storage::disk('public')->delete($fd . '/' . $fileName);
+        $writer->save($fp);
+        PdfConverterClient::convert($fp);
     }
 
-    public function generateReclamationGuarantee(Reclamation $reclamation): void
+    /**
+     * @throws Exception
+     */
+    private function generateReclamationGuarantee(Reclamation $reclamation): void
     {
         $inputFileType = 'Xlsx';
         $inputFileName = './templates/ReclamationGuarantee.xlsx';
@@ -470,7 +494,33 @@ class GenerateDocumentsService
         $fp = storage_path('app/public/reclamations/') . $reclamation->id . '/tmp/' . $reclamation->order->object_address . '/' . $fileName;
         Storage::disk('public')->delete($fd . '/' . $fileName);
         $writer->save($fp);
+        PdfConverterClient::convert($fp);
+
+    }
+
 
+    public function generateFilePack(Collection $files, int $userId, string $name = 'files'): \App\Models\File
+    {
+        $dir = Str::random(2);
+        Storage::disk('public')->makeDirectory('files/' . $dir . '/tmp/');
+
+        // copy files
+        foreach ($files as $file) {
+            $from = $file->path;
+            $to = 'files/' . $dir . '/tmp/' . $file->original_name;
+            if (!Storage::disk('public')->exists($to)) {
+                Storage::disk('public')->copy($from, $to);
+            }
+        }
+
+        // create zip archive
+        $fileModel = (new FileService())->createZipArchive('files/' . $dir . '/tmp', $name .'_' . date('Y-m-d_h-i-s') . '.zip', $userId);
+
+        // remove temp files
+        Storage::disk('public')->deleteDirectory('files/' . $dir . '/tmp');
+
+        // return link
+        return $fileModel;
     }
 
 }

+ 36 - 0
app/Services/PdfConverterClient.php

@@ -0,0 +1,36 @@
+<?php
+
+namespace App\Services;
+
+use Exception;
+use Illuminate\Support\Facades\Http;
+use Illuminate\Support\Facades\Log;
+use Illuminate\Support\Str;
+
+class PdfConverterClient
+{
+    const CONVERTER_ADDRESS = 'http://pdf-converter:5000/convert';
+
+
+    /**
+     * @param string $filePath
+     * @return void
+     * @throws Exception
+     */
+    public static function convert(string $filePath): void
+    {
+        if(!file_exists($filePath)) {
+            throw new Exception("File does not exist");
+        }
+        $response = Http::attach('file', file_get_contents($filePath), '123.xlsx')->post(self::CONVERTER_ADDRESS);
+
+        if($response->successful()) {
+            $newFilename = Str::replace('.xlsx', '.pdf', $filePath);
+            file_put_contents($newFilename, $response->body());
+        } else {
+            Log::error($response->clientError());
+        }
+    }
+
+
+}

+ 20 - 7
docker-compose.yml

@@ -38,13 +38,13 @@ services:
         environment:
             - SERVICE_NAME=app-queue
 
-    # Laravel`s scheduler
-    app-schedule:
-        <<: *base-app
-        command: php artisan schedule:work
-        environment:
-            SERVICE_NAME: ${COMPOSE_PROJECT_NAME}-schedule
-        hostname: ${COMPOSE_PROJECT_NAME}-schedule
+    # Laravel`s scheduler - disabled
+#    app-schedule:
+#        <<: *base-app
+#        command: php artisan schedule:work
+#        environment:
+#            SERVICE_NAME: ${COMPOSE_PROJECT_NAME}-schedule
+#        hostname: ${COMPOSE_PROJECT_NAME}-schedule
 
     # Nginx Service ----------------------------------------------------------------------------------------------------
     webserver:
@@ -105,6 +105,7 @@ services:
             - local
             - prod
 
+    # Websocket service
     websocket:
         build:
             context: docker/simple-ws
@@ -126,6 +127,7 @@ services:
             - local
             - prod
 
+    # Redis service
     redis:
         image: "redis:alpine"
         restart: unless-stopped
@@ -144,6 +146,17 @@ services:
             - local
             - prod
 
+    # Converter service
+    pdf-converter:
+        image: wteja/pdf-converter
+        restart: unless-stopped
+        networks:
+            - str-network
+        hostname: pdf-converter
+        profiles:
+            - local
+            - prod
+
 #Docker Networks
 networks:
     str-network:

+ 8 - 1
resources/views/orders/show.blade.php

@@ -118,8 +118,15 @@ use App\Models\Order;
 
                 <hr>
                 <div class="photo">
-                    <a href="#photos" data-bs-toggle="collapse">Фотографии</a>
+                    <a href="#photos" data-bs-toggle="collapse">Фотографии ({{ $order->photos->count() }})</a>
                     <button class="btn btn-sm text-success" onclick="$('#upl-photo').trigger('click');"><i class="bi bi-plus-circle-fill"></i> Загрузить</button>
+
+                    @if($order->photos->count())
+                        <a href="{{ route('order.generate-photos-pack', $order) }}" class="btn btn-sm text-primary" ><i
+                                    class="bi bi-download"></i> Скачать все
+                        </a>
+                    @endif
+
                     <form action="{{ route('order.upload-photo', $order) }}" enctype="multipart/form-data" method="post" class="visually-hidden">
                         @csrf
                         <input required type="file" id="upl-photo" onchange="$(this).parent().submit()" multiple name="photo[]" class="form-control form-control-sm" accept=".jpg,.jpeg,.png">

+ 11 - 0
resources/views/reclamations/edit.blade.php

@@ -192,6 +192,11 @@
                     <button class="btn btn-sm text-success" onclick="$('#upl-photo-before').trigger('click');"><i
                                 class="bi bi-plus-circle-fill"></i> Загрузить
                     </button>
+                    @if($reclamation->photos_before->count())
+                        <a href="{{ route('reclamation.generate-photos-before-pack', $reclamation) }}" class="btn btn-sm text-primary" ><i
+                                    class="bi bi-download"></i> Скачать все
+                        </a>
+                    @endif
                     <form action="{{ route('reclamations.upload-photo-before', $reclamation) }}"
                           enctype="multipart/form-data" method="post" class="visually-hidden">
                         @csrf
@@ -226,6 +231,12 @@
                     <button class="btn btn-sm text-success" onclick="$('#upl-photo-after').trigger('click');"><i
                                 class="bi bi-plus-circle-fill"></i> Загрузить
                     </button>
+                    @if($reclamation->photos_after->count())
+                        <a href="{{ route('reclamation.generate-photos-after-pack', $reclamation) }}" class="btn btn-sm text-primary" ><i
+                                    class="bi bi-download"></i> Скачать все
+                        </a>
+                    @endif
+
                     <form action="{{ route('reclamations.upload-photo-after', $reclamation) }}"
                           enctype="multipart/form-data" method="post" class="visually-hidden">
                         @csrf

+ 1 - 0
resources/views/responsibles/index.blade.php

@@ -43,6 +43,7 @@
                         @include('partials.select', ['name' => 'area_id', 'title' => 'Район', 'options' => $areas])
                         @include('partials.input', ['name' => 'name', 'title' => 'ФИО', 'required' => true])
                         @include('partials.input', ['name' => 'phone', 'title' => 'Телефон', 'required' => true])
+                        @include('partials.input', ['name' => 'post', 'title' => 'Должность'])
 
                         @include('partials.submit', ['name' => 'Добавить'])
 

+ 3 - 0
routes/web.php

@@ -110,6 +110,9 @@ Route::middleware('auth:web')->group(function () {
     Route::get('order/generate-installation-pack/{order}', [OrderController::class, 'generateInstallationPack'])->name('order.generate-installation-pack');
     Route::get('order/generate-handover-pack/{order}', [OrderController::class, 'generateHandoverPack'])->name('order.generate-handover-pack');
     Route::get('reclamation/generate-reclamation-pack/{reclamation}', [ReclamationController::class, 'generateReclamationPack'])->name('order.generate-reclamation-pack');
+    Route::get('reclamation/generate-photos-before-pack/{reclamation}', [ReclamationController::class, 'generatePhotosBeforePack'])->name('reclamation.generate-photos-before-pack');
+    Route::get('reclamation/generate-photos-after-pack/{reclamation}', [ReclamationController::class, 'generatePhotosAfterPack'])->name('reclamation.generate-photos-after-pack');
+    Route::get('order/generate-photos-pack/{order}', [OrderController::class, 'generatePhotosPack'])->name('order.generate-photos-pack');
 
     // Склад (МАФ)
     Route::get('product_sku', [ProductSKUController::class, 'index'])->name('product_sku.index');

BIN
templates/ReclamationAct.xlsx