Переглянути джерело

added fields to products, ttn generation

Alexander Musikhin 2 місяців тому
батько
коміт
92126a7de0

+ 24 - 0
app/Helpers/CountHelper.php

@@ -0,0 +1,24 @@
+<?php
+
+namespace App\Helpers;
+
+class CountHelper
+{
+    /**
+     * @param int $num
+     * @param string $one
+     * @param string $two
+     * @param string $many
+     * @return string
+     */
+    public static function humanCount(int $num, string $one, string $two, string $many): string
+    {
+        if(($num == 0) || ($num > 4)) {
+            return $num . ' ' . $many;
+        }
+        if(($num == 1)) {
+            return $num . ' ' . $one;
+        }
+        return $num . ' ' . $two;
+    }
+}

+ 12 - 1
app/Http/Controllers/OrderController.php

@@ -7,6 +7,7 @@ use App\Http\Requests\Order\StoreOrderRequest;
 use App\Jobs\GenerateFilesPack;
 use App\Jobs\GenerateHandoverPack;
 use App\Jobs\GenerateInstallationPack;
+use App\Jobs\GenerateTtnPack;
 use App\Models\Dictionary\Area;
 use App\Models\Dictionary\District;
 use App\Models\File;
@@ -17,6 +18,7 @@ use App\Models\OrderStatus;
 use App\Models\OrderView;
 use App\Models\ProductSKU;
 use App\Models\Role;
+use App\Models\Ttn;
 use App\Models\User;
 use App\Services\FileService;
 use Illuminate\Http\RedirectResponse;
@@ -342,7 +344,16 @@ class OrderController extends Controller
 
     public function createTtn(CreteTtnRequest $request)
     {
-        dd($request->validated());
+        $ttn = Ttn::query()->create([
+            'year' => date('Y'),
+            'ttn_number'    => Ttn::getTtnNumber() + 1,
+            'order_number'  => $request->order_number,
+            'order_date'    => $request->order_date,
+            'order_sum'     => $request->order_sum,
+            'skus'          => json_encode($request->skus),
+        ]);
+        GenerateTtnPack::dispatch($ttn, auth()->user()->id);
+        return redirect()->back()->with(['success' => 'Задача формирования ТТН создана!']);
     }
 
 }

+ 3 - 0
app/Http/Requests/StoreProductRequest.php

@@ -42,6 +42,9 @@ class StoreProductRequest extends FormRequest
             'certificate_date'      => 'nullable|string',
             'certificate_issuer'    => 'nullable|string',
             'certificate_type'      => 'nullable|string',
+            'weight'                => 'nullable|numeric',
+            'volume'                => 'nullable|nullable',
+            'places'                => 'nullable|integer',
         ];
     }
 }

+ 40 - 0
app/Jobs/GenerateTtnPack.php

@@ -0,0 +1,40 @@
+<?php
+
+namespace App\Jobs;
+
+use App\Events\SendWebSocketMessageEvent;
+use App\Models\Ttn;
+use App\Services\GenerateDocumentsService;
+use Exception;
+use Illuminate\Contracts\Queue\ShouldQueue;
+use Illuminate\Foundation\Queue\Queueable;
+use Illuminate\Support\Facades\Log;
+
+class GenerateTtnPack implements ShouldQueue
+{
+    use Queueable;
+
+    /**
+     * Create a new job instance.
+     */
+    public function __construct(
+        readonly private Ttn $ttn,
+        private readonly int $userId,
+    )
+    {}
+
+    /**
+     * Execute the job.
+     */
+    public function handle(): void
+    {
+        try {
+            $link = (new GenerateDocumentsService())->generateTtnPack($this->ttn, $this->userId);
+            Log::info('Generate TTN pack finished!');
+            event(new SendWebSocketMessageEvent('Пакет ТТН готов!', $this->userId, ['success' => true, 'link' => $link]));
+        } catch (Exception $e) {
+            Log::error('Generate TTN pack failed! ' . $e->getMessage());
+            event(new SendWebSocketMessageEvent('Ошибка создания пакета ТТН! ', $this->userId, ['error' => $e->getMessage()]));
+        }
+    }
+}

+ 3 - 0
app/Models/Product.php

@@ -43,6 +43,9 @@ class Product extends Model
         'certificate_date',
         'certificate_issuer',
         'certificate_type',
+        'weight',
+        'volume',
+        'places',
     ];
 
     // set year attribute to current selected year

+ 30 - 0
app/Models/Ttn.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace App\Models;
+
+use Illuminate\Database\Eloquent\Model;
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
+
+class Ttn extends Model
+{
+    protected $fillable = [
+        'year',
+        'ttn_number',
+        'ttn_number_suffix',
+        'order_number',
+        'order_date',
+        'order_sum',
+        'skus',
+        'file_id',
+    ];
+
+    public function file(): BelongsTo
+    {
+        return $this->belongsTo(File::class);
+    }
+
+    public static function getTtnNumber(): int
+    {
+        return Ttn::query()->where('year', now()->year)->orderBy('ttn_number', 'desc')->first()?->ttn_number ?? 0;
+    }
+}

+ 85 - 0
app/Services/GenerateDocumentsService.php

@@ -2,12 +2,17 @@
 
 namespace App\Services;
 
+use App\Helpers\CountHelper;
 use App\Helpers\DateHelper;
 use App\Helpers\ExcelHelper;
 use App\Models\Contract;
+use App\Models\File;
 use App\Models\Order;
+use App\Models\ProductSKU;
 use App\Models\Reclamation;
+use App\Models\Ttn;
 use Exception;
+use Faker\Guesser\Name;
 use Illuminate\Support\Collection;
 use Illuminate\Support\Facades\Storage;
 use Illuminate\Support\Str;
@@ -523,4 +528,84 @@ class GenerateDocumentsService
         return $fileModel;
     }
 
+    public function generateTtnPack(Ttn $ttn, int $userId): string
+    {
+        $skus = ProductSKU::query()->whereIn('id', json_decode($ttn->skus))->get();
+        $volume = $weight = $places = 0;
+        foreach ($skus as $sku) {
+            if(!isset($order)) {
+                $order = $sku->order;
+            }
+            $volume += $sku->product->volume;
+            $weight += $sku->product->weight;
+            $places += $sku->product->places;
+        }
+
+        $installationDate = ($order->installation_date) ? DateHelper::getHumanDate($order->installation_date, true) : '-';
+        $ttnNumber = ($ttn->ttn_number_suffix) ? $ttn->ttn_number . '-' . $ttn->ttn_number_suffix : $ttn->ttn_number;
+
+        $inputFileType = 'Xlsx';
+        $inputFileName = './templates/Ttn.xlsx';
+
+        $reader = IOFactory::createReader($inputFileType);
+        $spreadsheet = $reader->load($inputFileName);
+        $sheet = $spreadsheet->getActiveSheet();
+
+        $sheet->setCellValue('D8', $installationDate);
+        $sheet->setCellValue('R8', $ttnNumber);
+        $sheet->setCellValue('AF8', DateHelper::getHumanDate($ttn->order_date, true));
+        $sheet->setCellValue('AT8', $ttn->order_number);
+
+        $sheet->setCellValue('B19', $order->object_address ?? '');
+
+        $sheet->setCellValue('AD22', CountHelper::humanCount($places, 'место', 'места', 'мест') . ', способ упаковки: поддон, ящик, картон, полиэтилен');
+
+        $sheet->setCellValue('B24', $weight . ' кг, ' . $volume . ' м.куб');
+
+        $sheet->setCellValue('B36',  $installationDate);
+
+        $sheet->setCellValue('AB57', $installationDate . ' 8-00');
+        $sheet->setCellValue('B59',  $installationDate . ' 8-10');
+        $sheet->setCellValue('AB59', $installationDate . ' 8-40');
+
+        $sheet->setCellValue('B61', $weight . ' кг');
+
+        $sheet->setCellValue('B63', CountHelper::humanCount($places, 'место', 'места', 'мест'));
+
+        $sheet->setCellValue('B75', $order->object_address ?? '');
+        $sheet->setCellValue('AB75',  $installationDate);
+        $sheet->setCellValue('B77',   $installationDate);
+        $sheet->setCellValue('AB77',  $installationDate);
+
+        $sheet->setCellValue('AB79', CountHelper::humanCount($places, 'место', 'места', 'мест'));
+        $sheet->setCellValue('B81', $weight . ' кг');
+        $sheet->setCellValue('B83', $order->brigadier?->name ?? '');
+
+        $sheet->setCellValue('B89', $ttn->order_sum);
+        $sheet->setCellValue('AD89', $ttn->order_sum);
+        $sheet->setCellValue('AS89', $ttn->order_sum);
+
+        // save file
+        $fileName = 'ТТН №' . $ttn->ttn_number . ' от ' . DateHelper::getHumanDate($ttn->order_date) . '.xlsx';
+        $writer = new Xlsx($spreadsheet);
+        $fd = 'ttn/' . $ttn->year;
+        Storage::disk('public')->makeDirectory($fd);
+        $fp = storage_path('app/public/ttn/') . $ttn->year . '/' . $fileName;
+        Storage::disk('public')->delete($fd . '/' . $fileName);
+        $writer->save($fp);
+
+
+        $fileModel = File::query()->updateOrCreate([
+            'link' => url('/storage/') . '/ttn/' . $ttn->year . '/' .$fileName,
+            'path' => $fp . '/' .$fileName,
+            'user_id' => $userId,
+            'original_name' => $fileName,
+            'mime_type' => 'application/xlsx',
+        ]);
+        $ttn->file_id = $fileModel->id;
+        $ttn->save();
+
+        return $fileModel->link ?? '';
+    }
+
 }

+ 35 - 0
database/migrations/2025_09_18_062652_create_ttns_table.php

@@ -0,0 +1,35 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+        Schema::create('ttns', function (Blueprint $table) {
+            $table->id();
+            $table->unsignedInteger('year');
+            $table->unsignedInteger('ttn_number');
+            $table->string('ttn_number_suffix')->default('');
+            $table->string('order_number');
+            $table->date('order_date');
+            $table->string('order_sum');
+            $table->text('skus');
+            $table->foreignId('file_id')->nullable()->constrained('files')->cascadeOnDelete();
+            $table->timestamps();
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::dropIfExists('ttns');
+    }
+};

+ 30 - 0
database/migrations/2025_09_18_070610_add_fields_to_products_table.php

@@ -0,0 +1,30 @@
+<?php
+
+use Illuminate\Database\Migrations\Migration;
+use Illuminate\Database\Schema\Blueprint;
+use Illuminate\Support\Facades\Schema;
+
+return new class extends Migration
+{
+    /**
+     * Run the migrations.
+     */
+    public function up(): void
+    {
+        Schema::table('products', function (Blueprint $table) {
+            $table->float('weight')->default(0);
+            $table->float('volume')->default(0);
+            $table->float('places')->default(1);
+        });
+    }
+
+    /**
+     * Reverse the migrations.
+     */
+    public function down(): void
+    {
+        Schema::table('products', function (Blueprint $table) {
+            $table->dropColumn(['weight', 'volume', 'places']);
+        });
+    }
+};

+ 3 - 0
resources/views/catalog/edit.blade.php

@@ -50,6 +50,9 @@
                         @include('partials.input', ['name' => 'certificate_date', 'title' => 'Дата сертификата', 'type' => 'date', 'value' => $product->certificate_date])
                         @include('partials.input', ['name' => 'certificate_issuer', 'title' => 'Орган сертификации', 'value' => $product->certificate_issuer])
                         @include('partials.input', ['name' => 'certificate_type', 'title' => 'Вид сертификации', 'value' => $product->certificate_type])
+                        @include('partials.input', ['name' => 'weight', 'title' => 'Вес', 'value' => $product->weight,  'type' => 'number', 'step' => '0.01'])
+                        @include('partials.input', ['name' => 'volume', 'title' => 'Объём', 'value' => $product->volume, 'type' => 'number', 'step' => '0.01'])
+                        @include('partials.input', ['name' => 'places', 'title' => 'Кол-во мест', 'value' => $product->places, 'type' => 'number', 'step' => '1'])
 
                         <div class="row mb-2">
                             <label for="note" class="col-form-label my-1">

+ 1 - 0
resources/views/partials/input.blade.php

@@ -12,6 +12,7 @@
                    @disabled($disabled ?? null) @required($required ?? null)
                    @isset($min) min="{{ $min }}" @endisset
                    @isset($max) max="{{ $max }}" @endisset
+                   @isset($step) step="{{ $step }}" @endisset
                    @isset($style) style="{{ $style }}" @endisset
                    @isset($pattern) pattern="{{ $pattern }}" @endisset
                    placeholder="{{ $placeholder ?? ''}}"

BIN
templates/Ttn.xlsx