Browse Source

feat(schedule): add multi-year support for schedule navigation

- Add year parameter to all schedule navigation links and forms
- Allow date picker to work across all years (2000-2100 range)
- Include year in export form to maintain context
- Ensure year is passed through week/month navigation controls
- Validate year range and default to current year if invalid
Alexander Musikhin 3 tuần trước cách đây
mục cha
commit
51a756d17e

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

@@ -188,6 +188,17 @@ class OrderController extends Controller
     public function show(Request $request, int $order)
     {
         $this->data['order'] = Order::query()->withoutGlobalScopes()->find($order);
+
+        if ($request->boolean('sync_year') && $this->data['order']) {
+            $previousYear = year();
+            $targetYear = (int)$this->data['order']->year;
+
+            if ($previousYear !== $targetYear) {
+                session(['year' => $targetYear]);
+                session()->flash('warning', "Год переключен: {$previousYear} → {$targetYear} (по году площадки).");
+            }
+        }
+
         $this->data['previous_url'] = $this->resolvePreviousUrl(
             $request,
             'previous_url_orders',

+ 22 - 11
app/Http/Controllers/ScheduleController.php

@@ -36,18 +36,23 @@ class ScheduleController extends Controller
         $this->data['areas'] = Area::query()->get()->pluck('name', 'id');
         $this->data['brigadiers'] = User::query()->where('role', Role::BRIGADIER)->get()->pluck('name', 'id');
 
+        $this->data['scheduleYear'] = (int)$request->get('year', date('Y'));
+        if ($this->data['scheduleYear'] < 2000 || $this->data['scheduleYear'] > 2100) {
+            $this->data['scheduleYear'] = (int)date('Y');
+        }
+
         $tab = $request->get('tab', 'week');
         $this->data['activeTab'] = in_array($tab, ['week', 'month'], true) ? $tab : 'week';
 
         $this->data['weekNumber'] = $request->get('week' ,date('W'));
         $weekDates = [
-            'mon' => DateHelper::getDateOfWeek(year(), $this->data['weekNumber']),
-            'tue' => DateHelper::getDateOfWeek(year(), $this->data['weekNumber'], 2),
-            'wed' => DateHelper::getDateOfWeek(year(), $this->data['weekNumber'], 3),
-            'thu' => DateHelper::getDateOfWeek(year(), $this->data['weekNumber'], 4),
-            'fri' => DateHelper::getDateOfWeek(year(), $this->data['weekNumber'], 5),
-            'sat' => DateHelper::getDateOfWeek(year(), $this->data['weekNumber'], 6),
-            'sun' => DateHelper::getDateOfWeek(year(), $this->data['weekNumber'], 7),
+            'mon' => DateHelper::getDateOfWeek($this->data['scheduleYear'], $this->data['weekNumber']),
+            'tue' => DateHelper::getDateOfWeek($this->data['scheduleYear'], $this->data['weekNumber'], 2),
+            'wed' => DateHelper::getDateOfWeek($this->data['scheduleYear'], $this->data['weekNumber'], 3),
+            'thu' => DateHelper::getDateOfWeek($this->data['scheduleYear'], $this->data['weekNumber'], 4),
+            'fri' => DateHelper::getDateOfWeek($this->data['scheduleYear'], $this->data['weekNumber'], 5),
+            'sat' => DateHelper::getDateOfWeek($this->data['scheduleYear'], $this->data['weekNumber'], 6),
+            'sun' => DateHelper::getDateOfWeek($this->data['scheduleYear'], $this->data['weekNumber'], 7),
         ];
         $this->data['weekDates'] = $weekDates;
 
@@ -72,11 +77,12 @@ class ScheduleController extends Controller
             if (preg_match('/^(0[1-9]|1[0-2])$/', $monthParam)) {
                 $monthNumber = (int)$monthParam;
             } elseif (preg_match('/^(\\d{4})-(0[1-9]|1[0-2])$/', $monthParam, $matches)) {
+                $this->data['scheduleYear'] = (int)$matches[1];
                 $monthNumber = (int)$matches[2];
             }
         }
 
-        $monthStart = Carbon::createFromDate(year(), $monthNumber, 1)->startOfMonth();
+        $monthStart = Carbon::createFromDate($this->data['scheduleYear'], $monthNumber, 1)->startOfMonth();
         $monthEnd = $monthStart->copy()->endOfMonth();
 
         $gridStart = $monthStart->copy()->startOfWeek(Carbon::MONDAY);
@@ -88,6 +94,7 @@ class ScheduleController extends Controller
                 'date' => $cursor->toDateString(),
                 'day' => (int)$cursor->format('j'),
                 'week' => (int)$cursor->isoWeek(),
+                'weekYear' => (int)$cursor->isoWeekYear(),
                 'inMonth' => $cursor->month === $monthStart->month,
                 'isToday' => $cursor->isToday(),
             ];
@@ -127,8 +134,9 @@ class ScheduleController extends Controller
         $this->data['monthGrid'] = array_chunk($monthDays, 7);
         $this->data['monthLabel'] = $monthStart->isoFormat('MMMM YYYY');
         $this->data['monthNumber'] = $monthNumber;
-        $this->data['monthPrev'] = $monthNumber > 1 ? str_pad((string)($monthNumber - 1), 2, '0', STR_PAD_LEFT) : null;
-        $this->data['monthNext'] = $monthNumber < 12 ? str_pad((string)($monthNumber + 1), 2, '0', STR_PAD_LEFT) : null;
+        $this->data['monthPrev'] = $monthStart->copy()->subMonth()->format('Y-m');
+        $this->data['monthNext'] = $monthStart->copy()->addMonth()->format('Y-m');
+        $this->data['monthValue'] = $monthStart->format('Y-m');
         $this->data['monthScheduleColors'] = $monthScheduleColors;
         $this->data['monthBrigadierLegend'] = array_values($monthBrigadierLegend);
 
@@ -328,7 +336,10 @@ class ScheduleController extends Controller
             ->get();
 
         ExportScheduleJob::dispatch($schedules, $request->user()->id);
-        return redirect()->route('schedule.index', ['week' => (int) $request->validated()['week']])
+        return redirect()->route('schedule.index', [
+            'week' => (int) $request->validated()['week'],
+            'year' => (int) $request->input('year', date('Y')),
+        ])
             ->with(['success' => 'Задача генерации графика создана!']);
 
     }

+ 1 - 0
app/Http/Requests/ExportScheduleRequest.php

@@ -25,6 +25,7 @@ class ExportScheduleRequest extends FormRequest
             'start_date'    => 'required|date_format:Y-m-d',
             'end_date'      => 'required|date_format:Y-m-d',
             'week'          => 'required|string',
+            'year'          => 'nullable|integer|min:2000|max:2100',
         ];
     }
 }

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

@@ -4,7 +4,7 @@
     <div class="px-3">
         <div class="row mb-2">
             <div class="col-6 d-flex align-items-center">
-                <h3 class="mb-0">МАФ {{ $product->common_name ?? 'Новый МАФ' }}</h3>
+                <h3 class="mb-0">МАФ {{ $product->common_name ?? 'Новый МАФ' }} ({{ $product->year ?? year() }})</h3>
             </div>
             <div class="col-6 text-end d-flex align-items-center justify-content-end gap-2">
                 @if(isset($product) && hasRole('admin'))

+ 5 - 3
resources/views/catalog/index.blade.php

@@ -3,10 +3,13 @@
 @section('content')
 
     <div class="row mb-3 catalog">
-        <div class="col-md-6">
+        <div class="col-md-4">
             <h3>Каталог</h3>
         </div>
-        <div class="col-md-6 text-end">
+        <div class="col-md-4 text-center">
+            <div class="fw-bold fs-2 lh-1">{{ year() }}</div>
+        </div>
+        <div class="col-md-4 text-end">
             @if(hasRole('admin'))
                 <button type="button" class="btn btn-sm mb-1 btn-primary" data-bs-toggle="modal" data-bs-target="#importModal">
                     Импорт
@@ -85,4 +88,3 @@
         @dump($errors)
     @endif
 @endsection
-

+ 11 - 0
resources/views/layouts/app.blade.php

@@ -46,6 +46,17 @@
                 </div>
             @endforeach
         @endif
+
+        @if($message = session('warning'))
+            @php
+                if(!is_array($message)) $message = [$message];
+            @endphp
+            @foreach($message as $m)
+                <div class="main-alert alert alert-warning" role="alert">
+                    {{ $m }}
+                </div>
+            @endforeach
+        @endif
     </div>
 
     <div id="app">

+ 1 - 1
resources/views/maf_orders/edit.blade.php

@@ -6,7 +6,7 @@
         <div class="col-xl-6 mb-3 mb-xl-0">
             <form class="row" action="{{ route('maf_order.update', $maf_order) }}" method="post">
                 <div class="">
-                    <h4>Заказ МАФ</h4>
+                    <h4>Заказ МАФ ({{ $maf_order->year }})</h4>
                     @csrf
 
                     <input type="hidden" id="product_id" name="product_id" value="{{ $maf_order->product_id }}">

+ 5 - 2
resources/views/maf_orders/index.blade.php

@@ -3,10 +3,13 @@
 @section('content')
 
     <div class="row mb-3">
-        <div class="col-6">
+        <div class="col-md-4">
             <h3>Заказы МАФ</h3>
         </div>
-        <div class="col-6 text-end">
+        <div class="col-md-4 text-center">
+            <div class="fw-bold fs-2 lh-1">{{ year() }}</div>
+        </div>
+        <div class="col-md-4 text-end">
             <button type="button" class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#addModal">
                 Добавить
             </button>

+ 9 - 4
resources/views/orders/index.blade.php

@@ -2,14 +2,19 @@
 
 @section('content')
     <div class="row mb-3">
-        <div class="col-6">
+        <div class="col-md-4">
             <h3>Площадки</h3>
         </div>
-        @if(hasRole('admin'))
-            <div class="col-6 text-end">
+        <div class="col-md-4 text-center">
+            <div class="fw-bold fs-2 lh-1">{{ year() }}</div>
+        </div>
+        <div class="col-md-4 text-end">
+            @if(hasRole('admin'))
                 <a href="{{ route('order.create') }}" class="btn btn-sm btn-primary">Создать</a>
                 <a href="#" class="btn btn-sm btn-primary" onclick="$('#export-orders').submit()">Экспорт</a>
-            </div>
+            @endif
+        </div>
+        @if(hasRole('admin'))
             <form class="d-none" method="post" action="{{ route('order.export') }}" id="export-orders">
                 @csrf
             </form>

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

@@ -10,7 +10,7 @@
         <div class="row mb-2">
             <div class="col-md-6">
                 <h3>
-                    Площадка {{ $order->object_address }}
+                    Площадка {{ $order->object_address }} ({{ $order->year }})
                     <div class="badge text-bg-{{ Order::STATUS_COLOR[$order->order_status_id] }}">{{ $order->orderStatus->name }}</div>
                 </h3>
             </div>

+ 1 - 2
resources/views/products_sku/edit.blade.php

@@ -5,7 +5,7 @@
     <div class="px-3">
         <div class="row">
             <div class="col-xl-6">
-                <h4>МАФ на складе</h4>
+                <h4>МАФ на складе ({{ $product_sku->year }})</h4>
             </div>
             <div class="col-xl-6 text-end">
                 @if(isset($product_sku) && hasRole('admin'))
@@ -58,4 +58,3 @@
             </div>
         </form>
 @endsection
-

+ 5 - 2
resources/views/products_sku/index.blade.php

@@ -3,10 +3,13 @@
 @section('content')
 
     <div class="row mb-3">
-        <div class="col-6">
+        <div class="col-md-4">
             <h3>МАФ</h3>
         </div>
-        <div class="col-6 text-end">
+        <div class="col-md-4 text-center">
+            <div class="fw-bold fs-2 lh-1">{{ year() }}</div>
+        </div>
+        <div class="col-md-4 text-end">
             @if(hasRole('admin'))
                 <button type="button" class="btn btn-sm mb-1 btn-primary" data-bs-toggle="modal" data-bs-target="#importModal">
                     Импорт

+ 1 - 1
resources/views/reclamations/edit.blade.php

@@ -35,7 +35,7 @@
                     <input type="hidden" id="order_id" name="order_id" value="{{ $reclamation->order_id}}">
                     <input type="hidden" name="previous_url" value="{{ $previous_url ?? '' }}">
 
-                    @include('partials.link', ['title' => 'Площадка', 'href' => route('order.show', $reclamation->order_id), 'text' => $reclamation->order->common_name ?? ''])
+                    @include('partials.link', ['title' => 'Площадка', 'href' => route('order.show', ['order' => $reclamation->order_id, 'sync_year' => 1]), 'text' => $reclamation->order->common_name ?? ''])
                     @include('partials.select', ['name' => 'status_id', 'title' => 'Статус', 'options' => $statuses, 'value' => $reclamation->status_id ?? old('status_id'), 'disabled' => !hasRole('admin,manager')])
                     @include('partials.select', ['name' => 'user_id', 'title' => 'Менеджер', 'options' => $users, 'value' => $reclamation->user_id ?? old('user_id') ?? auth()->user()->id, 'disabled' => !hasRole('admin,manager')])
                     @include('partials.input', ['name' => 'maf_installation_year', 'title' => 'Год установки МАФ', 'type' => 'text', 'value' => $reclamation->order->year, 'disabled' => true])

+ 9 - 0
resources/views/reports/index.blade.php

@@ -16,6 +16,15 @@
 @endphp
 
 @section('content')
+    <div class="row mb-3">
+        <div class="col-md-4">
+            <h3>Отчеты</h3>
+        </div>
+        <div class="col-md-4 text-center">
+            <div class="fw-bold fs-2 lh-1">{{ year() }}</div>
+        </div>
+        <div class="col-md-4 text-end"></div>
+    </div>
 
     <ul class="nav nav-tabs" id="myTab" role="tablist">
         <li class="nav-item" role="presentation">

+ 20 - 16
resources/views/schedule/index.blade.php

@@ -10,11 +10,11 @@
                 <ul class="nav nav-tabs justify-content-end">
                     <li class="nav-item">
                         <a class="nav-link @if($activeTab === 'week') active @endif"
-                           href="{{ route('schedule.index', ['week' => $weekNumber, 'tab' => 'week']) }}">Неделя</a>
+                           href="{{ route('schedule.index', ['week' => $weekNumber, 'tab' => 'week', 'year' => $scheduleYear]) }}">Неделя</a>
                     </li>
                     <li class="nav-item">
                         <a class="nav-link @if($activeTab === 'month') active @endif"
-                           href="{{ route('schedule.index', ['month' => str_pad((string)$monthNumber, 2, '0', STR_PAD_LEFT), 'tab' => 'month']) }}">Месяц</a>
+                           href="{{ route('schedule.index', ['month' => $monthValue, 'tab' => 'month', 'year' => $scheduleYear]) }}">Месяц</a>
                     </li>
                 </ul>
             </div>
@@ -26,7 +26,7 @@
                     <div class="d-flex flex-row justify-content-end">
                         <div class="p-2">
                             <button @disabled($weekNumber == 1) class="btn btn-sm btn-primary"
-                                    onclick="document.location = '{{ route('schedule.index', ['week' => $weekNumber - 1, 'tab' => 'week']) }}'">
+                                    onclick="document.location = '{{ route('schedule.index', ['week' => $weekNumber - 1, 'tab' => 'week', 'year' => $scheduleYear]) }}'">
                                 <i class="bi bi-arrow-left"></i>
                             </button>
                         </div>
@@ -34,15 +34,15 @@
                         <div class="p-2">
                             <input type="number" value="{{ $weekNumber }}"
                                    class="form-control form-control-sm week-number-input" name="weekNumber"
-                                   onchange="document.location = '{{ route('schedule.index') }}?tab=week&week='+this.value"
+                                   onchange="document.location = '{{ route('schedule.index') }}?tab=week&year={{ $scheduleYear }}&week='+this.value"
                                    min="1" max="53" title="№ недели">
                         </div>
 
                         <div class="p-2">
-                            <input type="date" min="{{ year() . '-01-01' }}" max="{{ year() . '-12-31' }}"
+                            <input type="date" min="2000-01-01" max="2100-12-31"
                                    class="form-control form-control-sm" value="{{ $weekDates['mon'] }}"
                                    title="начало недели" name="monday" id="fromDate"
-                                   onchange="document.location = '{{ route('schedule.index') }}?tab=week&week=' + getWeekNumber(this.value)">
+                                   onchange="document.location = '{{ route('schedule.index') }}?tab=week&year=' + this.value.substring(0,4) + '&week=' + getWeekNumber(this.value)">
                         </div>
                         <div class="p-2 d-none d-md-block">
                             <input type="date" disabled name="sunday"
@@ -52,7 +52,7 @@
 
                         <div class="p-2">
                             <button @disabled($weekNumber > 52) class="btn btn-sm btn-primary"
-                                    onclick="document.location = '{{ route('schedule.index', ['week' => $weekNumber + 1, 'tab' => 'week']) }}'">
+                                    onclick="document.location = '{{ route('schedule.index', ['week' => $weekNumber + 1, 'tab' => 'week', 'year' => $scheduleYear]) }}'">
                                 <i class="bi bi-arrow-right"></i>
                             </button>
                         </div>
@@ -63,6 +63,7 @@
                                 <input type="hidden" name="start_date" value="{{ $weekDates['mon'] }}">
                                 <input type="hidden" name="end_date" value="{{ $weekDates['sun'] }}">
                                 <input type="hidden" name="week" value="{{ $weekNumber }}">
+                                <input type="hidden" name="year" value="{{ $scheduleYear }}">
                                 <button type="submit" class="btn btn-sm btn-primary" id="exportScheduleButton">Экспорт</button>
                             </form>
                         </div>
@@ -120,7 +121,7 @@
                                 class="align-middle">{{ $schedule?->area?->name }}</td>
                             <td style="background: {{ $schedule->brigadier->color }}" class="align-middle">
                                 @if($schedule->order_id)
-                                <a href="{{ route('order.show', $schedule->order_id) }}">{{ $schedule->object_address }}</a>
+                                <a href="{{ route('order.show', ['order' => $schedule->order_id, 'sync_year' => 1]) }}">{{ $schedule->object_address }}</a>
                                 @else
                                     {{ $schedule->object_address }}
                                 @endif
@@ -177,21 +178,21 @@
             <div class="d-flex justify-content-between align-items-center mb-3">
                 <div class="d-flex align-items-center">
                     <button class="btn btn-sm btn-primary me-2" @disabled(!$monthPrev)
-                            onclick="document.location = '{{ route('schedule.index', ['tab' => 'month', 'month' => $monthPrev ?? str_pad((string)$monthNumber, 2, '0', STR_PAD_LEFT)]) }}'">
+                            onclick="document.location = '{{ route('schedule.index', ['tab' => 'month', 'month' => $monthPrev]) }}'">
                         <i class="bi bi-arrow-left"></i>
                     </button>
                     <div class="h5 mb-0 text-capitalize">{{ $monthLabel }}</div>
                     <button class="btn btn-sm btn-primary ms-2" @disabled(!$monthNext)
-                            onclick="document.location = '{{ route('schedule.index', ['tab' => 'month', 'month' => $monthNext ?? str_pad((string)$monthNumber, 2, '0', STR_PAD_LEFT)]) }}'">
+                            onclick="document.location = '{{ route('schedule.index', ['tab' => 'month', 'month' => $monthNext]) }}'">
                         <i class="bi bi-arrow-right"></i>
                     </button>
                 </div>
                 <div class="d-flex align-items-center">
                     <label for="monthSelect" class="me-2 small">Месяц</label>
                     <input id="monthSelect" type="month" class="form-control form-control-sm"
-                           min="{{ year() . '-01' }}" max="{{ year() . '-12' }}"
-                           value="{{ year() . '-' . str_pad((string)$monthNumber, 2, '0', STR_PAD_LEFT) }}"
-                           onchange="document.location='{{ route('schedule.index') }}?tab=month&month=' + this.value.substring(5,7)">
+                           min="2000-01" max="2100-12"
+                           value="{{ $monthValue }}"
+                           onchange="document.location='{{ route('schedule.index') }}?tab=month&month=' + this.value">
                 </div>
             </div>
 
@@ -213,9 +214,10 @@
                         <tr>
                             @foreach($week as $day)
                                 <td class="schedule-day @if(!$day['inMonth']) schedule-outside @endif @if($day['isToday']) schedule-today @endif"
-                                    data-week="{{ $day['week'] }}">
+                                    data-week="{{ $day['week'] }}"
+                                    data-week-year="{{ $day['weekYear'] }}">
                                     <a class="schedule-day-link"
-                                       href="{{ route('schedule.index', ['week' => $day['week'], 'tab' => 'week']) }}"
+                                       href="{{ route('schedule.index', ['week' => $day['week'], 'tab' => 'week', 'year' => $day['weekYear']]) }}"
                                        title="Открыть неделю №{{ $day['week'] }}">
                                         {{ $day['day'] }}
                                     </a>
@@ -350,8 +352,10 @@
     <script type="module">
         $('.schedule-day').on('dblclick', function () {
             let week = $(this).attr('data-week');
+            let year = $(this).attr('data-week-year');
             if (!week) return;
-            document.location = '{{ route('schedule.index') }}?tab=week&week=' + week;
+            if (!year) year = '{{ $scheduleYear }}';
+            document.location = '{{ route('schedule.index') }}?tab=week&year=' + year + '&week=' + week;
         });
 
         @if(hasRole('admin'))