Просмотр исходного кода

Added orders table, refactored getFilters to helper, create new order

Alexander Musikhin 9 месяцев назад
Родитель
Сommit
0fdfbc89b2

+ 34 - 0
app/Helpers/DBHelper.php

@@ -0,0 +1,34 @@
+<?php
+declare(strict_types=1);
+
+namespace App\Helpers;
+
+use Illuminate\Support\Facades\DB;
+use Illuminate\Support\Str;
+
+class DBHelper
+{
+    /**
+     * @param $column
+     * @return array
+     */
+    public static function getFilters($column, $model): array
+    {
+        $uniqueValues =  DB::table((new $model)->getTable())
+            ->select($column)
+            ->distinct()
+            ->get()
+            ->pluck($column)
+            ->toArray();
+        $result = [];
+        foreach ($uniqueValues as $val){
+            if(str_ends_with($column, '_id')) {
+                $relation = Str::camel(str_replace('_id', '', $column));
+                $result[$val] = $model::query()->where($column, '=', $val)->first()->$relation->name;
+            } else {
+                $result[$val] = $val;
+            }
+        }
+        return $result;
+    }
+}

+ 127 - 22
app/Http/Controllers/OrderController.php

@@ -2,12 +2,18 @@
 
 namespace App\Http\Controllers;
 
+use App\Helpers\DateHelper;
+use App\Helpers\DBHelper;
+use App\Http\Requests\Order\StoreOrderRequest;
 use App\Models\Brigadier;
 use App\Models\Dictionary\Area;
 use App\Models\Dictionary\District;
 use App\Models\ObjectType;
+use App\Models\Order;
 use App\Models\OrderStatus;
+use App\Models\User;
 use Illuminate\Http\Request;
+use Illuminate\Support\Str;
 
 class OrderController extends Controller
 {
@@ -15,32 +21,124 @@ class OrderController extends Controller
         'active'    => 'orders',
         'title'     => 'Заказы',
         'id'        => 'orders',
-//        'header'    => [
-//            'id'                        => 'ID',
-//            'year'                      => 'Год',
-//            'nomenclature_number'       => 'Номер номенклатуры',
-//            'article'                   => 'Артикул',
-//            'manufacturer'              => 'Производитель',
-//            'name_tz'                   => 'Наименование ТЗ',
-//            'type_tz'                   => 'Тип по ТЗ',
-//            'type'                      => 'Тип',
-//            'manufacturer_name'         => 'Наименование производителя',
-//            'sizes'                     => 'Размеры',
-//            'price_status'              => 'Статус цены',
-//            'product_price_txt'         => 'Цена товара',
-//            'installation_price_txt'    => 'Цена установки',
-//            'service_price_txt'         => 'Цена обслуживания',
-//            'total_price_txt'           => 'Итоговая цена',
-//            'note'                      => 'Примечания',
-//            'created_at'                => 'Дата создания',
-//        ]
+        'header'    => [
+            'id'                        => 'ID',
+            'user_id'                   => 'Менеджер',
+            'district_id'               => 'Округ',
+            'area_id'                   => 'Район',
+            'object_address'            => 'Адрес объекта',
+            'object_type_id'            => 'Тип объекта',
+            'contract_date'             => 'Дата договора',
+            'contract_number'           => 'Номер договора',
+            'comment'                   => 'Комментарий',
+            'installation_date'         => 'Дата выхода на монтаж',
+            'brigadier_id'              => 'Бригадир',
+            'order_status_id'           => 'Статус',
+            'tg_group_name'             => 'Имя группы в ТГ',
+            'tg_group_link'             => 'Ссылка на группу в ТГ',
+        ]
     ];
 
     /**
      * Display a listing of the resource.
      */
-    public function index()
+    public function index(Request $request)
     {
+        $this->data['searchFields'] = [
+            'comment',
+            'object_address',
+            'tg_group_name',
+            'tg_group_link',
+            'contract_number',
+        ];
+
+        $filters = [
+            'user_id' => [
+                'title' => 'Менеджер',
+                'values' => DBHelper::getFilters('user_id', Order::class),
+            ],
+            'district_id' => [
+                'title' => 'Округ',
+                'values' => DBHelper::getFilters('district_id', Order::class),
+            ],
+            'area_id' => [
+                'title' => 'Район',
+                'values' => DBHelper::getFilters('area_id', Order::class),
+            ],
+            'object_type_id' => [
+                'title' => 'Тип объекта',
+                'values' => DBHelper::getFilters('object_type_id', Order::class),
+            ],
+            'brigadier_id' => [
+                'title' => 'Бригадир',
+                'values' => DBHelper::getFilters('brigadier_id', Order::class),
+            ],
+            'order_status_id' => [
+                'title' => 'Статус',
+                'values' => DBHelper::getFilters('order_status_id', Order::class),
+            ],
+        ];
+        $ranges = [];
+        $dates = [];
+
+        // fill filters
+        $this->data['filters'] = $filters;
+        $this->data['dates'] = $dates;
+        $this->data['ranges'] = $ranges;
+
+        $q = Order::query();
+
+        // accept filters
+        if(!empty($request->filters) && is_array($request->filters)) {
+            foreach ($request->filters as $filterName => $filterValue) {
+                if(!$filterValue) continue;
+
+                if(Str::contains($filterName, 'price')) {
+                    $filterValue = $filterValue * 100;
+                }
+
+                if(Str::endsWith($filterName, '_from')) {
+                    if(is_string($filterValue) && DateHelper::isDate($filterValue)) {
+                        $filterValue .= ' 00:00:00';
+                    }
+                    $q->where(Str::replace('_from', '', $filterName), '>=', $filterValue);
+                } elseif(Str::endsWith($filterName, '_to')) {
+                    if(is_string($filterValue) && DateHelper::isDate($filterValue)) {
+                        $filterValue .= ' 23:59:59';
+                    }
+                    $q->where(Str::replace('_to', '', $filterName), '<=', $filterValue);
+                } else {
+                    $q->where($filterName, '=', $filterValue);
+                }
+            }
+        }
+
+        // accept search
+        if(!empty($request->s)) {
+            $s = $request->s;
+            $searchFields = $this->data['searchFields'];
+            $q->where(function ($query) use ($searchFields, $s) {
+                foreach ($searchFields as $searchField) {
+                    $query->orWhere($searchField, 'LIKE', '%' . $s . '%');
+                }
+            });
+        }
+        // ------- setup sort and order --------------------------------------------------------------------------------
+        $this->data['sortBy'] = (!empty($request->sortBy))
+            ? Str::replace('_txt', '', $request->sortBy) // remove '_txt' fields modifier
+            : Order::DEFAULT_SORT_BY;
+
+        // check for sortBy is valid field
+        $p = new Order();
+        if(!in_array($this->data['sortBy'], array_merge(['id', 'created_at'], $p->getFillable()))) {
+            $this->data['sortBy'] = Order::DEFAULT_SORT_BY;
+        }
+
+        // set order
+        $this->data['orderBy'] = (!empty($request->order)) ? 'desc' : 'asc';
+        $q->orderBy($this->data['sortBy'], $this->data['orderBy']);
+        $this->data['orders'] = $q->paginate()->withQueryString();
+
         return view('orders.index', $this->data);
     }
 
@@ -54,15 +152,22 @@ class OrderController extends Controller
         $this->data['objectTypes'] = ObjectType::query()->get()->pluck('name', 'id');
         $this->data['orderStatuses'] =OrderStatus::query()->get()->pluck('name', 'id');
         $this->data['brigadiers'] = Brigadier::query()->get()->pluck('name', 'id');
+        $this->data['users'] = User::query()->get()->pluck('name', 'id');
         return view('orders.edit', $this->data);
     }
 
     /**
      * Store a newly created resource in storage.
      */
-    public function store(Request $request)
+    public function store(StoreOrderRequest $request)
     {
-        //
+        $data = $request->validated();
+        if(isset($data['id'])) {
+            Order::query()->where('id', $data['id'])->update($data);
+        } else {
+            Order::query()->create($data);
+        }
+        return redirect()->route('order.index');
     }
 
     /**

+ 3 - 2
app/Http/Controllers/ProductController.php

@@ -3,6 +3,7 @@
 namespace App\Http\Controllers;
 
 use App\Helpers\DateHelper;
+use App\Helpers\DBHelper;
 use App\Jobs\Export\ExportCatalog;
 use App\Jobs\Import\ImportCatalog;
 use App\Models\Product;
@@ -44,11 +45,11 @@ class ProductController extends Controller
         $filters = [
             'type_tz' => [
                 'title'     => 'Тип по ТЗ',
-                'values'    => Product::getFilters('type_tz')
+                'values'    => DBHelper::getFilters('type_tz', Product::class)
             ],
             'type' => [
                 'title'     => 'Тип',
-                'values'    => Product::getFilters('type')
+                'values'    => DBHelper::getFilters('type', Product::class)
             ],
         ];
 

+ 41 - 0
app/Http/Requests/Order/StoreOrderRequest.php

@@ -0,0 +1,41 @@
+<?php
+
+namespace App\Http\Requests\Order;
+
+use Illuminate\Foundation\Http\FormRequest;
+
+class StoreOrderRequest extends FormRequest
+{
+    /**
+     * Determine if the user is authorized to make this request.
+     */
+    public function authorize(): bool
+    {
+        return auth()->check();
+    }
+
+    /**
+     * Get the validation rules that apply to the request.
+     *
+     * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
+     */
+    public function rules(): array
+    {
+        return [
+            'id'                => 'nullable|exists:orders,id',
+            'user_id'           => 'required|exists:users,id',
+            'district_id'       => 'required|exists:districts,id',
+            'area_id'           => 'required|exists:areas,id',
+            'object_address'    => 'required|string|min:5',
+            'object_type_id'    => 'required|exists:object_types,id',
+            'contract_date'     => 'nullable|date',
+            'contract_number'   => 'nullable|string',
+            'comment'           => 'nullable|string',
+            'installation_date' => 'nullable|date',
+            'brigadier_id'      => 'nullable|exists:brigadiers,id',
+            'order_status_id'   => 'required|exists:order_statuses,id',
+            'tg_group_name'     => 'nullable|string',
+            'tg_group_link'     => 'nullable|string',
+        ];
+    }
+}

+ 4 - 1
app/Models/Order.php

@@ -6,9 +6,12 @@ use App\Models\Dictionary\Area;
 use App\Models\Dictionary\District;
 use Illuminate\Database\Eloquent\Model;
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
+use Illuminate\Support\Facades\DB;
 
 class Order extends Model
 {
+    const DEFAULT_SORT_BY = 'created_at';
+
     protected $fillable = [
         'user_id',
         'district_id',
@@ -25,7 +28,7 @@ class Order extends Model
         'tg_group_link',
     ];
 
-    public function manager(): BelongsTo
+    public function user(): BelongsTo
     {
         return $this->belongsTo(User::class, 'user_id', 'id');
     }

+ 0 - 14
app/Models/Product.php

@@ -95,18 +95,4 @@ class Product extends Model
         );
     }
 
-    /**
-     * @param $column
-     * @return array
-     */
-    public static function getFilters($column): array
-    {
-        return DB::table('products')
-            ->select($column)
-            ->distinct()
-            ->get()
-            ->pluck($column)
-            ->toArray();
-    }
-
 }

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

@@ -25,7 +25,7 @@ return new class extends Migration
             $table->foreignId('brigadier_id')->constrained('brigadiers')->restrictOnDelete(); // бригадир
             $table->foreignId('order_status_id')->constrained('order_statuses')->restrictOnDelete(); // статус объекта
             $table->string('tg_group_name')->nullable();
-            $table->string('tg_group_id')->nullable();
+            $table->string('tg_group_link')->nullable();
             $table->timestamps();
         });
     }

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

@@ -21,7 +21,7 @@
     @include('partials.table', [
         'id'        => $id,
         'header'    => $header,
-        'data'  => $products
+        'strings'   => $products
     ])
 
     <div class="row pt-3 px-3">

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

@@ -10,30 +10,41 @@
                     <input type="hidden" name="id" value="{{ $order->id }}">
                 @endif
 
-                @include('partials.select', ['name' => 'district_id', 'title' => 'Округ', 'options' => $districts, 'value' => $order?->$district_id ?? null, 'first_empty' => true, 'required' => true])
+                @include('partials.select', ['name' => 'district_id', 'title' => 'Округ', 'options' => $districts, 'value' => $order?->$district_id ?? old('district_id'), 'first_empty' => true, 'required' => true])
 
-                @include('partials.select', ['name' => 'area_id', 'title' => 'Район', 'options' => [] /* $areas */, 'value' => $order?->area_id ?? null, 'required' => true])
+                @include('partials.select', ['name' => 'area_id', 'title' => 'Район', 'options' => $areas, 'value' => $order?->area_id ?? old('area_id'), 'required' => true, 'first_empty' => true])
 
-                @include('partials.input', ['name' => 'object_address', 'title' => 'Адрес объекта', 'value' => $order->object_address ?? '', 'required' => true])
+                @include('partials.input', ['name' => 'object_address', 'title' => 'Адрес объекта', 'value' => $order->object_address ?? old('object_address'), 'required' => true])
 
-                @include('partials.select', ['name' => 'object_type', 'title' => 'Тип объекта', 'options' => $objectTypes, 'value' => $order->object_type ?? null, 'required' => true, 'first_empty' => true])
+                @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.input', ['name' => 'contract_date', 'title' => 'Дата договора', 'type' => 'date', 'value' => $order->contract_date ?? ''])
+                @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_number', 'title' => 'Номер договора', 'value' => $order->contract_number ?? ''])
+                @include('partials.input', ['name' => 'contract_date', 'title' => 'Дата договора', 'type' => 'date', 'value' => $order->contract_date ?? old('contract_date')])
 
-                @include('partials.textarea', ['name' => 'comment', 'title' => 'Комментарий', 'value' => $order->comment ?? ''])
+                @include('partials.input', ['name' => 'contract_number', 'title' => 'Номер договора', 'value' => $order->contract_number ?? old('contract_number')])
 
-                @include('partials.input', ['name' => 'installation_date', 'title' => 'Дата выхода на монтаж', 'type' => 'date', 'value' => $order->installation_date ?? ''])
+                @include('partials.textarea', ['name' => 'comment', 'title' => 'Комментарий', 'value' => $order->comment ?? old('comment')])
 
-                @include('partials.select', ['name' => 'brigadier', 'title' => 'Бригадир', 'options' => $brigadiers, 'value' => $order->brigadier ?? ''])
+                @include('partials.input', ['name' => 'installation_date', 'title' => 'Дата выхода на монтаж', 'type' => 'date', 'value' => $order->installation_date ?? old('installation_date')])
 
-                @include('partials.input', ['name' => 'tg_group_name', 'title' => 'Группа в ТГ', 'value' => $order->tg_group_name ?? ''])
+                @include('partials.select', ['name' => 'brigadier_id', 'title' => 'Бригадир', 'options' => $brigadiers, 'value' => $order->brigadier_id ?? old('brigadier_id')])
+
+                @include('partials.select', ['name' => 'user_id', 'title' => 'Менеджер', 'options' => $users, 'value' => $order->user_id ?? old('user_id') ?? auth()->user()->id])
+
+                @include('partials.input', ['name' => 'tg_group_name', 'title' => 'Название группы в ТГ', 'value' => $order->tg_group_name ?? old('tg_group_name')])
+
+                @include('partials.input', ['name' => 'tg_group_link', 'title' => 'Ссылка на группу в ТГ', 'value' => $order->tg_group_link ?? old('tg_group_link')])
 
                 @include('partials.submit')
             </form>
         </div>
     </div>
+
+    @if($errors->any())
+        @dump($errors->all())
+    @endif
+
 @endsection
 
 @push('scripts')

+ 20 - 3
resources/views/orders/index.blade.php

@@ -1,9 +1,26 @@
 @extends('layouts.app')
 
 @section('content')
-    <a href="{{ route('order.create') }}" class="btn btn-sm btn-primary">Создать</a>
-    <div class="px-md-3 px-2">
-        orders
+    <div class="row mb-3">
+        <div class="col-6">
+            <h3>Заказы</h3>
+        </div>
+        <div class="col-6 text-end">
+            <a href="{{ route('order.create') }}" class="btn btn-sm btn-primary">Создать</a>
+        </div>
+    </div>
+
+
+    @include('partials.table', [
+        'id'        => $id,
+        'header'    => $header,
+        'strings'   => $orders
+    ])
+
+    <div class="row pt-3 px-3">
+        <div class="col-12 pagination">
+            {{ $orders->links() }}
+        </div>
     </div>
 
     @if($errors->any())

+ 12 - 4
resources/views/partials/table.blade.php

@@ -43,10 +43,19 @@
             </tr>
         </thead>
         <tbody>
-        @foreach($products as $product)
+        @foreach($strings as $string)
             <tr>
                 @foreach($header as $headerName => $headerTitle)
-                    <td class="column_{{$headerName}}">{!! $product->$headerName !!}</td>
+                    <td class="column_{{$headerName}}">
+                        @if(str_ends_with($headerName, '_id'))
+                            @php
+                                $relation = str_replace('_id', '', $headerName);
+                            @endphp
+                            {!! $string->$relation?->name; !!}
+                        @else
+                            {!! $string->$headerName !!}
+                        @endif
+                    </td>
                 @endforeach
             </tr>
         @endforeach
@@ -87,13 +96,12 @@
             <div class="modal-body">
                 <form class="filters">
                     @foreach($filters as $filterName => $filter)
-                        @php array_unshift($filter['values'], '') @endphp
+                        @php $filter['values'] = ['' => ''] + $filter['values'] @endphp
                         @include('partials.select', [
                                 'name' => 'filters[' . $filterName . ']',
                                 'title' => $filter['title'],
                                 'options' => $filter['values'],
                                 'value' => request()->filters[$filterName] ?? '',
-                                'key_as_val' => true
                             ])
                     @endforeach
                     @foreach($ranges as $rangeName => $range)