Quellcode durchsuchen

refactored maf-order-sku relation.

Alexander Musikhin vor 7 Monaten
Ursprung
Commit
6dca0fa1e2

+ 1 - 1
.env.example

@@ -8,7 +8,7 @@ WEB_PORT=8090
 # external port of DB for connect by other soft
 DB_EXT_PORT=3306
 WS_PORT=3090
-WS_ADDR=ws
+WS_ADDR=/ws
 WS_DEBUG_PORT=9229
 MINIO_DEBUG_PORT=39757
 

+ 1 - 0
app/Http/Controllers/Controller.php

@@ -73,6 +73,7 @@ class Controller extends BaseController
             $uniqueValues =  DB::table(($model)->getTable())
                 ->select($column)
                 ->distinct()
+                ->whereNull('deleted_at')
                 ->get()
                 ->pluck($column)
                 ->toArray();

+ 38 - 16
app/Http/Controllers/OrderController.php

@@ -5,11 +5,14 @@ namespace App\Http\Controllers;
 use App\Http\Requests\Order\StoreOrderRequest;
 use App\Models\Dictionary\Area;
 use App\Models\Dictionary\District;
+use App\Models\MafOrder;
 use App\Models\ObjectType;
 use App\Models\Order;
 use App\Models\OrderStatus;
+use App\Models\ProductSKU;
 use App\Models\Role;
 use App\Models\User;
+use Illuminate\Http\RedirectResponse;
 use Illuminate\Http\Request;
 
 class OrderController extends Controller
@@ -34,6 +37,7 @@ class OrderController extends Controller
             'tg_group_name'             => 'Имя группы в ТГ',
             'tg_group_link'             => 'Ссылка на группу в ТГ',
             'products-common_name'      => 'МАФы',
+            'ready_to_mount'            => 'Готов к монтажу',
         ],
         'searchFields' => [
             'comment',
@@ -62,7 +66,7 @@ class OrderController extends Controller
     {
         $model = new Order;
         // fill filters
-        $this->createFilters($model, 'user_id', 'district_id', 'area_id', 'object_type_id', 'brigadier_id', 'order_status_id');
+        $this->createFilters($model, 'user_id', 'district_id', 'area_id', 'object_type_id', 'brigadier_id', 'order_status_id', 'ready_to_mount');
         $this->createDateFilters($model, 'contract_date', 'installation_date');
         $this->data['ranges'] = [];
 
@@ -75,6 +79,10 @@ class OrderController extends Controller
         $q->orderBy($this->data['sortBy'], $this->data['orderBy']);
         $this->data['orders'] = $q->paginate()->withQueryString();
 
+        foreach ($this->data['orders'] as $order) {
+            $order->recalculateReadyToMount();
+        }
+
         return view('orders.index', $this->data);
     }
 
@@ -94,6 +102,7 @@ class OrderController extends Controller
         $data = $request->validated();
 
         $products = $request->validated('products');
+        $products_sku = $request->validated('products_sku');
         $quantities = $request->validated('quantity');
 
         unset($data['products']);
@@ -103,14 +112,22 @@ class OrderController extends Controller
             $order->update($data);
             $order->refresh();
         } else {
+            $data['order_status_id'] = Order::STATUS_NEW;
             $order = Order::query()->create($data);
         }
 
-        if($products && $quantities) {
-            $order->products()->detach();
+        // меняем список товаров заказа только если статус новый
+        if($products && $quantities && ($order->order_status_id == 1)) {
+            // remove from products_sku
+            ProductSKU::query()->where('order_id', $order->id)->delete();
             foreach ($products as $key => $product) {
-                if($quantities[$key] == 0) continue;
-                $order->products()->attach($product, ['quantity' => $quantities[$key]]);
+                for($i = 0; $i < $quantities[$key]; $i++) {
+                    ProductSKU::query()->create([
+                        'order_id' => $order->id,
+                        'product_id' => $product,
+                        'status' => 'требуется'
+                    ]);
+                }
             }
         }
 
@@ -136,18 +153,23 @@ class OrderController extends Controller
     }
 
     /**
-     * Update the specified resource in storage.
+     * Привязка товаров к заказу
+     * @param Order $order
+     * @return RedirectResponse
      */
-    public function update(Request $request, string $id)
+    public function getMafToOrder(Order $order)
     {
-        //
-    }
-
-    /**
-     * Remove the specified resource from storage.
-     */
-    public function destroy(string $id)
-    {
-        //
+        foreach ($order->products_sku as $product_sku) {
+            $mafOrder = MafOrder::query()
+                ->where('product_id', $product_sku->product_id)
+                ->where('in_stock', '>' , 0)
+                ->orderBy('created_at')
+                ->first();
+            $product_sku->update(['maf_order_id' => $mafOrder->id, 'status' => 'отгружен']);
+            $mafOrder->decrement('in_stock');
+            unset($mafOrder, $product_sku);
+        }
+        $order->update(['order_status_id' => Order::STATUS_READY_TO_MOUNT]);
+        return redirect()->route('order.show', $order);
     }
 }

+ 1 - 1
app/Http/Requests/Order/StoreOrderRequest.php

@@ -33,11 +33,11 @@ class StoreOrderRequest extends FormRequest
             'comment'           => 'nullable|string',
             'installation_date' => 'nullable|date',
             'brigadier_id'      => 'nullable|exists:users,id',
-            'order_status_id'   => 'required|exists:order_statuses,id',
             'tg_group_name'     => 'nullable|string',
             'tg_group_link'     => 'nullable|string',
             'products'          => 'nullable|array',
             'quantity'          => 'nullable|array',
+            'products_sku'      => 'nullable|array',
         ];
     }
 }

+ 51 - 5
app/Models/Order.php

@@ -2,11 +2,14 @@
 
 namespace App\Models;
 
+use App\Helpers\Price;
 use App\Models\Dictionary\Area;
 use App\Models\Dictionary\District;
+use Illuminate\Database\Eloquent\Casts\Attribute;
 use Illuminate\Database\Eloquent\Model;
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
 use Illuminate\Database\Eloquent\Relations\BelongsToMany;
+use Illuminate\Database\Eloquent\Relations\HasMany;
 use Illuminate\Database\Eloquent\SoftDeletes;
 use Illuminate\Support\Facades\DB;
 
@@ -15,6 +18,11 @@ class Order extends Model
     use SoftDeletes;
     const DEFAULT_SORT_BY = 'created_at';
 
+    const STATUS_NEW = 1;
+    const STATUS_READY_TO_MOUNT = 2;
+    const STATUS_READY_TO_HAND_OVER = 3;
+    const STATUS_HANDED_OVER = 4;
+
     protected $fillable = [
         'user_id',
         'district_id',
@@ -29,14 +37,17 @@ class Order extends Model
         'order_status_id',
         'tg_group_name',
         'tg_group_link',
+        'ready_to_mount',
     ];
 
-    /**
-     * @return BelongsToMany
-     */
-    public function products(): BelongsToMany
+    public function products_sku(): HasMany
+    {
+        return $this->hasMany(ProductSKU::class, 'order_id', 'id');
+    }
+
+    public function maf_orders(): BelongsTo
     {
-        return $this->belongsToMany(Product::class, 'order_product')->withPivot('quantity');
+        return $this->belongsTo(MafOrder::class, 'maf_order_id');
     }
 
     public function user(): BelongsTo
@@ -68,4 +79,39 @@ class Order extends Model
     {
         return $this->belongsTo(OrderStatus::class);
     }
+
+
+    public function getNeeds(): array
+    {
+        $needs = [];
+        foreach ($this->products_sku as $sku) {
+            $needs[$sku->product_id]['needs'] = (isset($needs[$sku->product_id])) ? $needs[$sku->product_id]['needs'] + 1 : 1;
+        }
+
+        foreach ($needs as $productId => $quantity) {
+            $needs[$productId]['sku'] = MafOrder::query()
+                ->where('maf_orders.product_id', $productId)
+                ->sum('maf_orders.in_stock');
+        }
+        return $needs;
+    }
+
+    public function recalculateReadyToMount(): void
+    {
+        $result = true;
+
+        foreach ($this->getNeeds() as $need) {
+            if($need['sku'] < $need['needs']) {
+                $result = false;
+                break;
+            }
+        }
+
+        if($this->order_status_id > self::STATUS_NEW) {
+            $result = true;
+        }
+
+       $this->update(['ready_to_mount' => ($result) ? 'Да' : 'Нет']);
+    }
 }
+

+ 1 - 8
app/Models/Product.php

@@ -50,13 +50,6 @@ class Product extends Model
         );
     }
 
-    protected function servicePrice(): Attribute
-    {
-        return Attribute::make(
-            get: fn(int $value) => (float) $value / 100,
-            set: fn (float $value) => (int) ($value * 100),
-        );
-    }
 
     protected function totalPrice(): Attribute
     {
@@ -101,7 +94,7 @@ class Product extends Model
      */
     public function orders(): BelongsToMany
     {
-        return $this->belongsToMany(Order::class, 'order_product');
+        return $this->belongsToMany(Order::class, 'products_sku', 'order_id', 'product_id');
     }
 
 

+ 13 - 1
app/Models/ProductSKU.php

@@ -14,7 +14,8 @@ class ProductSKU extends Model
     protected $fillable = [
         'product_id',
         'order_id',
-        'status',
+        'maf_order_id',
+        'status',               // needs - нуждается в данном товаре, related - привязана к MafOrder
         'rfid',
         'factory_number',
         'manufacture_date',
@@ -34,8 +35,19 @@ class ProductSKU extends Model
         return $this->belongsTo(Product::class, 'product_id', 'id');
     }
 
+    /**
+     * @return BelongsTo
+     */
     public function order(): BelongsTo
     {
         return $this->belongsTo(Order::class, 'order_id', 'id');
     }
+
+    /**
+     * @return BelongsTo
+     */
+    public function maf_order(): BelongsTo
+    {
+        return $this->belongsTo(MafOrder::class, 'maf_order_id', 'id');
+    }
 }

+ 2 - 1
app/Models/User.php

@@ -3,12 +3,13 @@
 namespace App\Models;
 
 use Illuminate\Contracts\Auth\MustVerifyEmail;
+use Illuminate\Database\Eloquent\SoftDeletes;
 use Illuminate\Foundation\Auth\User as Authenticatable;
 use Illuminate\Notifications\Notifiable;
 
 class User extends Authenticatable implements MustVerifyEmail
 {
-    use Notifiable;
+    use Notifiable, SoftDeletes;
 
     const DEFAULT_SORT_BY = 'created_at';
 

+ 1 - 0
database/migrations/0001_01_01_000000_create_users_table.php

@@ -21,6 +21,7 @@ return new class extends Migration
             $table->string('role');
             $table->rememberToken();
             $table->timestamps();
+            $table->softDeletes();
         });
 
         Schema::create('password_reset_tokens', function (Blueprint $table) {

+ 1 - 0
database/migrations/2025_03_24_153700_create_orders_table.php

@@ -26,6 +26,7 @@ return new class extends Migration
             $table->foreignId('order_status_id')->constrained('order_statuses')->restrictOnDelete(); // статус объекта
             $table->string('tg_group_name')->nullable();
             $table->string('tg_group_link')->nullable();
+            $table->string('ready_to_mount')->default('Нет');
             $table->timestamps();
             $table->softDeletes();
         });

+ 0 - 29
database/migrations/2025_03_30_101937_create_order_product_table.php

@@ -1,29 +0,0 @@
-<?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('order_product', function (Blueprint $table) {
-            $table->id();
-            $table->foreignId('order_id')->constrained()->cascadeOnDelete();
-            $table->foreignId('product_id')->constrained()->cascadeOnDelete();
-            $table->unsignedMediumInteger('quantity')->default(1);
-        });
-    }
-
-    /**
-     * Reverse the migrations.
-     */
-    public function down(): void
-    {
-        Schema::dropIfExists('order_product');
-    }
-};

+ 10 - 3
database/migrations/2025_03_31_145335_create_products_sku_table.php → database/migrations/2025_04_11_145335_create_products_sku_table.php

@@ -16,11 +16,18 @@ return new class extends Migration
         Schema::create('products_sku', function (Blueprint $table) {
             $table->id();
             $table->foreignId('product_id')                             // id продукта
-                ->constrained('products')->restrictOnDelete();
+                ->constrained('products')
+                ->restrictOnDelete();
+
             $table->foreignId('order_id')                               // id заказа
-                ->nullable()
                 ->constrained('orders')
-                ->nullOnDelete();
+                ->restrictOnDelete();
+
+            $table->foreignId('maf_order_id')
+                ->nullable()
+                ->constrained('maf_orders')
+                ->restrictOnDelete();
+
             $table->string('status')->nullable();                       // ordered, shipped, stock
             $table->string('rfid')->nullable();                         // номер RFID метки
             $table->string('factory_number')->nullable();               // номер заказа на фабрике

+ 5 - 4
database/seeders/OrderStatusSeeder.php

@@ -2,6 +2,7 @@
 
 namespace Database\Seeders;
 
+use App\Models\Order;
 use App\Models\OrderStatus;
 use Illuminate\Database\Seeder;
 
@@ -12,9 +13,9 @@ class OrderStatusSeeder extends Seeder
      */
     public function run(): void
     {
-        OrderStatus::query()->updateOrCreate(['id' => 1], ['name' => 'Новый']);
-        OrderStatus::query()->updateOrCreate(['id' => 2], ['name' => 'Готов к монтажу']);
-        OrderStatus::query()->updateOrCreate(['id' => 3], ['name' => 'Готов к сдаче']);
-        OrderStatus::query()->updateOrCreate(['id' => 4], ['name' => 'Сдан']);
+        OrderStatus::query()->updateOrCreate(['id' => Order::STATUS_NEW], ['name' => 'Новый']);
+        OrderStatus::query()->updateOrCreate(['id' => Order::STATUS_READY_TO_MOUNT], ['name' => 'Готов к монтажу']);
+        OrderStatus::query()->updateOrCreate(['id' => Order::STATUS_READY_TO_HAND_OVER], ['name' => 'Готов к сдаче']);
+        OrderStatus::query()->updateOrCreate(['id' => Order::STATUS_HANDED_OVER], ['name' => 'Сдан']);
     }
 }

+ 1 - 5
resources/sass/app.scss

@@ -40,13 +40,9 @@
 
 .table {
   tr th {
-    background-color: #0dcaf0;
+    background-color: var(--bs-primary-bg-subtle);
     vertical-align: middle;
   }
-
-  tr:nth-child(even) td {
-    background-color: #e5e5e5;
-  }
   td {
     cursor: pointer;
   }

+ 11 - 10
resources/views/orders/edit.blade.php

@@ -22,8 +22,6 @@
 
                 @include('partials.select', ['name' => 'object_type_id', 'title' => 'Тип объекта', 'options' => $objectTypes, 'value' => $order->object_type_id ?? old('object_type_id'), 'required' => true, 'first_empty' => true])
 
-                @include('partials.select', ['name' => 'order_status_id', 'title' => 'Статус', 'options' => $orderStatuses, 'value' => $order->order_status_id ?? old('order_status_id'), 'required' => true])
-
                 @include('partials.input', ['name' => 'contract_date', 'title' => 'Дата договора', 'type' => 'date', 'value' => $order->contract_date ?? old('contract_date')])
 
                 @include('partials.input', ['name' => 'contract_number', 'title' => 'Номер договора', 'value' => $order->contract_number ?? old('contract_number')])
@@ -44,27 +42,30 @@
             <div class="col-xxl-6">
                 <h4>МАФ</h4>
                 <div>
-                    <input type="text" class="form-control mb-2" placeholder="Поиск номенклатуры" id="search_maf">
-                    <select id="select_maf" class="form-select mb-3" multiple></select>
+                    <input type="text" class="form-control mb-2" @disabled($order->order_status_id ?? 0 > 1) placeholder="Поиск номенклатуры" id="search_maf">
+                    <select id="select_maf" class="form-select mb-3" multiple @disabled($order->order_status_id ?? 0 > 1)></select>
                 </div>
 
                 <div id="selected_maf">
                     @isset($order)
                         <div class="changes-message small text-warning visually-hidden">* необходимо сохранить изменения</div>
                     @endisset
-                    @if(isset($order) && $order->products)
-                        @foreach($order->products as $p)
+                    @if(isset($order) && $order->products_sku)
+                        @foreach($order->products_sku as $p)
                             <div class="maf d-flex justify-content-between mb-1">
                                 <div>
-                                    <input type="hidden" class="visually-hidden" name="products[]" value="{{ $p->id }}">
-                                    <div class="p-1">{!! $p->common_name !!}</div>
+                                    <input type="hidden" class="visually-hidden" name="products_sku[]" value="{{ $p->id }}">
+                                    <input type="hidden" class="visually-hidden" name="products[]" value="{{ $p->product->id }}">
+                                    <div class="p-1">{!! $p->product->common_name !!}</div>
                                 </div>
                                 <div class="col-1 d-flex justify-content-end">
                                     <div>
-                                        <input class="form-control text-end form-control-sm quantity" type="number" name="quantity[]" value="{{ $p->pivot->quantity }}">
+                                        <input class="form-control text-end form-control-sm quantity" type="number" name="quantity[]" value="1" @disabled($order->order_status_id > 1)>
                                     </div>
                                     <div class="p-1">
-                                        <i onclick="$(this).parent().parent().parent().remove(); $('.changes-message').removeClass('visually-hidden');" class="bi bi-trash text-danger cursor-pointer"></i>
+                                        @if($order->order_status_id == 1)
+                                            <i onclick="$(this).parent().parent().parent().remove(); $('.changes-message').removeClass('visually-hidden');" class="bi bi-trash text-danger cursor-pointer"></i>
+                                        @endif
                                     </div>
                                 </div>
                             </div>

+ 48 - 15
resources/views/orders/show.blade.php

@@ -8,6 +8,9 @@
                 <h3>Площадка {{ $order->object_address }}</h3>
             </div>
             <div class="col-6 text-end">
+                @if($order->order_status_id == App\Models\Order::STATUS_NEW)
+                    <a href="{{ route('order.get-maf', $order) }}" class="btn btn-primary @disabled($order->ready_to_mount == 'Нет' )" >Привязать МАФы к заказу</a>
+                @endif
                 <a href="{{ route('order.edit', $order) }}" class="btn btn-primary">Редактировать</a>
             </div>
         </div>
@@ -34,23 +37,53 @@
 
             </div>
             <div class="col-xl-8">
-                <h4>МАФ</h4>
+                <h4>МАФы заказа</h4>
 
                 <div id="selected_maf">
-                    @if(isset($order) && $order->products)
-                        @foreach($order->products as $p)
-                            <div class="maf d-flex justify-content-between mb-1">
-                                <div>
-                                    <div class="p-1">{!! $p->common_name !!}</div>
-                                </div>
-                                <div class="col-1 d-flex justify-content-end">
-                                    <div>
-                                        {{ $p->pivot->quantity }}
-                                    </div>
-
-                                </div>
-                            </div>
-                        @endforeach
+                    @if(isset($order) && $order->products_sku)
+                        <table class="table">
+                            <thead>
+                            <tr>
+                                <th>№</th>
+                                <th>МАФ</th>
+                                <th>Статус</th>
+                                <th>Номер заказа МАФ</th>
+                                <th>RFID</th>
+                                <th>Заводской номер</th>
+                                <th>Дата производства</th>
+                                <th>Срок эксплуатации</th>
+                                <th>Склад</th>
+                            </tr>
+                            </thead>
+                            <tbody>
+                            @php
+                                $needs = $order->getNeeds();
+                            @endphp
+                            @foreach($order->products_sku as $p)
+                                <tr>
+                                    <td>{{ $loop->iteration }}</td>
+                                    <td>{!! $p->product->common_name !!}</td>
+                                    <td>{{ $p->status }}</td>
+                                    <td>{{ $p->maf_order?->order_number }}</td>
+                                    <td>{{ $p->rfid }}</td>
+                                    <td>{{ $p->factory_number }}</td>
+                                    <td>{{ $p->manufacture_date }}</td>
+                                    <td>{{ $p->service_life }}</td>
+                                    <td class="text-center">
+                                        @if($order->order_status_id > 1)
+                                            <i class="bi bi-check-all text-success fw-bold"></i>
+                                        @else
+                                            @if($needs[$p->product_id]['sku']-- > 0)
+                                                <i class="bi bi-check text-success fw-bold"></i>
+                                            @else
+                                                <i class="bi bi-x text-danger fw-bold"></i>
+                                            @endif
+                                        @endif
+                                    </td>
+                                </tr>
+                            @endforeach
+                            </tbody>
+                        </table>
                     @endif
                 </div>
 

+ 1 - 0
routes/web.php

@@ -57,6 +57,7 @@ Route::middleware('auth:web')->group(function () {
     Route::get('order/edit/{order}', [OrderController::class, 'edit'])->name('order.edit');
     Route::post('order/store', [OrderController::class, 'store'])->name('order.store');
     Route::post('order/{order}/store', [OrderController::class, 'store'])->name('order.update');
+    Route::get('order/{order}/get-maf', [OrderController::class, 'getMafToOrder'])->name('order.get-maf');
     Route::get('order/destroy', [OrderController::class, 'destroy'])->name('order.destroy');
 
     // Склад