authorizeAccess(); $templates = ReportTemplate::with('creator')->latest()->get()->map(fn (ReportTemplate $t) => [ 'id' => $t->id, 'name' => $t->name, 'sourceLabels' => collect($t->sections ?? []) ->map(fn (array $s) => $this->sources->all()[$s['source']]?->label ?? $s['source']) ->unique() ->values() ->all(), 'sectionsCount' => count($t->sections ?? []), 'requiredPermissions' => $t->required_permissions ?? [], 'creator' => $t->creator?->name, ]); return Inertia::render('Admin/ReportTemplates/Index', ['templates' => $templates]); } public function create() { $this->authorizeAccess(); return Inertia::render('Admin/ReportTemplates/Form', [ 'template' => null, 'sources' => $this->sourcesPayload(), ]); } public function store(Request $request) { $this->authorizeAccess(); ReportTemplate::create([ ...$this->validateTemplate($request), 'created_by' => Auth::id(), ]); return redirect('/admin/report-templates')->with('success', 'Шаблон создан'); } public function edit(ReportTemplate $template) { $this->authorizeAccess(); return Inertia::render('Admin/ReportTemplates/Form', [ 'template' => [ 'id' => $template->id, 'name' => $template->name, 'sections' => $template->sections, 'requiredPermissions' => $template->required_permissions ?? [], ], 'sources' => $this->sourcesPayload(), ]); } public function update(ReportTemplate $template, Request $request) { $this->authorizeAccess(); $template->update($this->validateTemplate($request)); return redirect('/admin/report-templates')->with('success', 'Шаблон сохранён'); } public function destroy(ReportTemplate $template) { $this->authorizeAccess(); $template->delete(); return redirect('/admin/report-templates')->with('success', 'Шаблон удалён'); } private function validateTemplate(Request $request): array { $sourceKeys = array_keys($this->sources->all()); $validated = $request->validate([ 'name' => 'required|string|max:255', 'sections' => 'required|array|min:1', 'sections.*.source' => ['required', 'string', 'in:'.implode(',', $sourceKeys)], 'sections.*.title' => 'nullable|string|max:255', 'sections.*.columns' => 'required|array|min:1', 'sections.*.columns.*' => 'string', 'sections.*.filters' => 'nullable|array', 'sections.*.filters.*.field' => 'required_with:sections.*.filters|string', 'sections.*.filters.*.value' => 'nullable', 'required_permissions' => 'nullable|array', 'required_permissions.*' => 'in:'.implode(',', self::PERMISSION_OPTIONS), ]); // Допускаем только колонки/поля фильтров, реально существующие у источника секции — // защита от рассинхрона формы и произвольных значений в БД. $validated['sections'] = array_map(function (array $section) { $source = $this->sources->get($section['source']); return [ 'source' => $section['source'], 'title' => $section['title'] ?? $source->label, 'columns' => array_values(array_intersect($section['columns'], array_keys($source->columns))), 'filters' => array_values(array_filter( $section['filters'] ?? [], fn (array $filter) => array_key_exists($filter['field'], $source->filterableFields) )), ]; }, $validated['sections']); $validated['required_permissions'] = array_values($validated['required_permissions'] ?? []); return $validated; } private function sourcesPayload(): array { return collect($this->sources->all())->map(fn ($source) => [ 'key' => $source->key, 'label' => $source->label, 'columns' => $source->columns, 'filterableFields' => $source->filterableFields, ])->values()->all(); } private function authorizeAccess(): void { $user = Auth::user(); abort_unless($user->isAdmin() || $user->isChiefDoctor() || $user->isDeputyChief(), 403); } }