Добавлены роуты для отчетов
This commit is contained in:
@@ -3,13 +3,18 @@
|
||||
namespace App\Http\Controllers\Web;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\CustomMeasure;
|
||||
use App\Models\Department;
|
||||
use App\Models\MisLpuDoctor;
|
||||
use App\Models\ReportDocument;
|
||||
use App\Models\User;
|
||||
use App\Services\Analytics\AnalyticsQuery;
|
||||
use App\Services\Analytics\DataSetRegistry;
|
||||
use App\Services\Analytics\Export\AnalyticsExcelExport;
|
||||
use App\Services\Analytics\Export\AnalyticsPdfExport;
|
||||
use App\Services\Analytics\Measure;
|
||||
use App\Services\Analytics\ReportPresetRegistry;
|
||||
use App\Services\DateRangeService;
|
||||
use App\Services\Reports\Contracts\ReportDefinition;
|
||||
use App\Services\Reports\Export\ReportExcelExport;
|
||||
use App\Services\Reports\Export\ReportPdfExport;
|
||||
use App\Services\Reports\ReportRegistry;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Inertia\Inertia;
|
||||
@@ -18,92 +23,359 @@ use Maatwebsite\Excel\Facades\Excel;
|
||||
class ReportsController extends Controller
|
||||
{
|
||||
public function __construct(
|
||||
protected ReportRegistry $reportRegistry,
|
||||
protected DataSetRegistry $datasets,
|
||||
protected ReportPresetRegistry $presets,
|
||||
protected DateRangeService $dateRangeService,
|
||||
) {}
|
||||
|
||||
public function index(Request $request)
|
||||
/** Каталог: системные шаблоны + сохранённые отчёты филиала. */
|
||||
public function index()
|
||||
{
|
||||
$user = Auth::user();
|
||||
$available = $this->reportRegistry->availableFor($user);
|
||||
|
||||
abort_if(empty($available), 403);
|
||||
|
||||
$department = $this->resolveDepartment($request, $user);
|
||||
$dateRange = $this->dateRangeService->getDateRangeFromRequest($request, $user);
|
||||
|
||||
// Без явного типа в запросе показываем только список отчётов — без предпросмотра.
|
||||
$type = $request->query('type');
|
||||
$definition = $type ? $this->reportRegistry->find($type, $user) : null;
|
||||
$payload = $definition?->build($department, $dateRange);
|
||||
|
||||
return Inertia::render('Reports/Index', [
|
||||
'reportTypes' => array_map(fn (ReportDefinition $d) => [
|
||||
'code' => $d->code(),
|
||||
'label' => $d->label(),
|
||||
'audience' => $this->audienceLabel($d->requiredPermissions()),
|
||||
], $available),
|
||||
'departments' => collect($user->availableDepartments())->map(fn (Department $d) => [
|
||||
'id' => $d->department_id,
|
||||
'name' => $d->name_full ?? $d->name_short,
|
||||
])->values()->all(),
|
||||
'selectedType' => $definition?->code(),
|
||||
'selectedDepartmentId' => $department->department_id,
|
||||
'isHeadOrAdmin' => $user->isSeniorStaff(),
|
||||
'date' => [
|
||||
$dateRange->start()->getTimestampMs(),
|
||||
$dateRange->end()->getTimestampMs(),
|
||||
],
|
||||
'payload' => $payload ? [
|
||||
'title' => $payload->title,
|
||||
'meta' => $payload->meta,
|
||||
'sections' => array_map(fn ($section) => [
|
||||
'title' => $section->title,
|
||||
'columns' => $section->columns,
|
||||
'rows' => $section->rows,
|
||||
], $payload->sections),
|
||||
] : null,
|
||||
$documents = ReportDocument::with('creator')->latest('updated_at')->get()->map(fn (ReportDocument $d) => [
|
||||
'id' => $d->id,
|
||||
'name' => $d->name,
|
||||
'description' => $d->description,
|
||||
'datasetLabel' => $this->datasets->has($d->dataset) ? $this->datasets->get($d->dataset)->label() : $d->dataset,
|
||||
'author' => $d->creator?->name,
|
||||
'updatedAt' => $d->updated_at?->format('d.m.Y'),
|
||||
]);
|
||||
|
||||
return Inertia::render('Analytics/Index', [
|
||||
'documents' => $documents,
|
||||
'presets' => $this->presets->all(),
|
||||
'categories' => $this->presets->categories(),
|
||||
'canManage' => $this->canManage($user),
|
||||
]);
|
||||
}
|
||||
|
||||
/** Редактор нового отчёта (с нуля или из пресета). */
|
||||
public function create(Request $request)
|
||||
{
|
||||
$user = Auth::user();
|
||||
abort_unless($this->canManage($user), 403);
|
||||
|
||||
$preset = $request->query('preset') ? $this->presets->find($request->query('preset')) : null;
|
||||
$config = $preset['config'] ?? $this->presets->find('blank')['config'];
|
||||
|
||||
return $this->renderBuilder($request, $user, [
|
||||
'document' => null,
|
||||
'name' => $preset && $preset['key'] !== 'blank' ? $preset['label'] : 'Новый отчёт',
|
||||
'description' => '',
|
||||
'config' => $config,
|
||||
]);
|
||||
}
|
||||
|
||||
/** Редактор существующего отчёта. */
|
||||
public function show(Request $request, ReportDocument $document)
|
||||
{
|
||||
$user = Auth::user();
|
||||
|
||||
return $this->renderBuilder($request, $user, [
|
||||
'document' => ['id' => $document->id],
|
||||
'name' => $document->name,
|
||||
'description' => $document->description,
|
||||
// dataset хранится отдельной колонкой — возвращаем его внутри config для редактора
|
||||
'config' => [...($document->config ?? []), 'dataset' => $document->dataset],
|
||||
]);
|
||||
}
|
||||
|
||||
/** Построение отчёта (живой предпросмотр) — JSON. */
|
||||
public function run(Request $request)
|
||||
{
|
||||
$user = Auth::user();
|
||||
$query = $this->buildQuery($request, $user);
|
||||
|
||||
return response()->json($this->datasets->get($query->datasetKey)->run($query)->toArray());
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$user = Auth::user();
|
||||
abort_unless($this->canManage($user), 403);
|
||||
|
||||
$document = ReportDocument::create([
|
||||
...$this->validateDocument($request),
|
||||
'created_by' => Auth::id(),
|
||||
]);
|
||||
|
||||
return redirect("/reports/{$document->id}")->with('success', 'Отчёт сохранён');
|
||||
}
|
||||
|
||||
public function update(Request $request, ReportDocument $document)
|
||||
{
|
||||
$user = Auth::user();
|
||||
abort_unless($this->canManage($user), 403);
|
||||
|
||||
$document->update($this->validateDocument($request));
|
||||
|
||||
return back()->with('success', 'Отчёт сохранён');
|
||||
}
|
||||
|
||||
public function destroy(ReportDocument $document)
|
||||
{
|
||||
abort_unless($this->canManage(Auth::user()), 403);
|
||||
|
||||
$document->delete();
|
||||
|
||||
return redirect('/reports')->with('success', 'Отчёт удалён');
|
||||
}
|
||||
|
||||
public function duplicate(ReportDocument $document)
|
||||
{
|
||||
abort_unless($this->canManage(Auth::user()), 403);
|
||||
|
||||
$copy = $document->replicate(['created_by']);
|
||||
$copy->name = $document->name.' (копия)';
|
||||
$copy->created_by = Auth::id();
|
||||
$copy->save();
|
||||
|
||||
return redirect("/reports/{$copy->id}")->with('success', 'Создана копия');
|
||||
}
|
||||
|
||||
public function exportExcel(Request $request)
|
||||
{
|
||||
[$definition, $department, $dateRange] = $this->resolveExportContext($request);
|
||||
$result = $this->buildResult($request);
|
||||
|
||||
$payload = $definition->build($department, $dateRange);
|
||||
|
||||
return Excel::download(new ReportExcelExport($payload), $this->fileName($definition, 'xlsx'));
|
||||
return Excel::download(new AnalyticsExcelExport($result['result'], $result['title']), $this->fileName($result['title'], 'xlsx'));
|
||||
}
|
||||
|
||||
public function exportPdf(Request $request)
|
||||
{
|
||||
[$definition, $department, $dateRange] = $this->resolveExportContext($request);
|
||||
$result = $this->buildResult($request);
|
||||
|
||||
$payload = $definition->build($department, $dateRange);
|
||||
|
||||
return ReportPdfExport::render($payload)->download($this->fileName($definition, 'pdf'));
|
||||
return AnalyticsPdfExport::render($result['result'], $result['title'])->download($this->fileName($result['title'], 'pdf'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{0: ReportDefinition, 1: Department, 2: \App\Services\DateRange}
|
||||
*/
|
||||
private function resolveExportContext(Request $request): array
|
||||
// --- helpers ---
|
||||
|
||||
private function renderBuilder(Request $request, User $user, array $payload): \Inertia\Response
|
||||
{
|
||||
$user = Auth::user();
|
||||
$type = $request->query('type');
|
||||
$definition = $type ? $this->reportRegistry->find($type, $user) : null;
|
||||
|
||||
abort_if($definition === null, 403);
|
||||
|
||||
$department = $this->resolveDepartment($request, $user);
|
||||
$dateRange = $this->dateRangeService->getDateRangeFromRequest($request, $user);
|
||||
|
||||
return [$definition, $department, $dateRange];
|
||||
return Inertia::render('Analytics/Builder', [
|
||||
...$payload,
|
||||
'datasets' => $this->datasetsMetadata($department),
|
||||
'presets' => $this->presets->all(),
|
||||
'categories' => $this->presets->categories(),
|
||||
'departments' => collect($user->misDepartments)->map(fn (Department $d) => [
|
||||
'id' => $d->department_id,
|
||||
'name' => $d->name_full ?? $d->name_short,
|
||||
])->values()->all(),
|
||||
'selectedDepartmentId' => $department->department_id,
|
||||
'isHeadOrAdmin' => $user->isSeniorStaff(),
|
||||
'canManage' => $this->canManage($user),
|
||||
'date' => [$dateRange->start()->getTimestampMs(), $dateRange->end()->getTimestampMs()],
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Метаданные датасетов: + пользовательские показатели и динамические опции
|
||||
* фильтров (например, список врачей отделения).
|
||||
*
|
||||
* @return array<int,array<string,mixed>>
|
||||
*/
|
||||
private function datasetsMetadata(Department $department): array
|
||||
{
|
||||
$meta = $this->datasets->metadata();
|
||||
$options = []; // ленивый кэш динамических опций по источнику
|
||||
|
||||
foreach ($meta as &$dataset) {
|
||||
foreach ($this->customMeasuresFor($dataset['key']) as $custom) {
|
||||
$dataset['measures'][] = ['key' => $custom->key, 'label' => $custom->label, 'unit' => $custom->unit];
|
||||
}
|
||||
|
||||
foreach ($dataset['filters'] as &$filter) {
|
||||
$source = $filter['optionsSource'] ?? null;
|
||||
if ($source === 'doctors' || $source === 'nurse_doctors') {
|
||||
$options[$source] ??= $this->staffOptions($department, $source === 'nurse_doctors' ? 'nurse' : 'duty');
|
||||
$filter['options'] = $options[$source];
|
||||
}
|
||||
}
|
||||
unset($filter);
|
||||
}
|
||||
|
||||
return $meta;
|
||||
}
|
||||
|
||||
/**
|
||||
* Врачи/медсёстры отделения из справочника МИС (по должностям в МИС-отделении).
|
||||
* Список не зависит от того, проставлен ли врач в уже сданных сменах — он готов
|
||||
* к работе сразу, фильтрация идёт по rf_lpudoctor_id = LPUDoctorID.
|
||||
*
|
||||
* @param 'duty'|'nurse' $kind
|
||||
* @return array<int,array{label:string,value:int}>
|
||||
*/
|
||||
private function staffOptions(Department $department, string $kind): array
|
||||
{
|
||||
$misIds = array_filter([$department->rf_mis_department_id]);
|
||||
|
||||
if ($misIds === []) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$prvdIds = $kind === 'nurse' ? [1567, 1629] : [];
|
||||
$fallback = $kind === 'nurse' ? 'Медсестра' : 'Врач';
|
||||
|
||||
return MisLpuDoctor::active()
|
||||
->whereNotIn('LPUDoctorID', [0, 1])
|
||||
->whereHas('prvds', function ($q) use ($misIds, $prvdIds) {
|
||||
$q->whereIn('rf_DepartmentID', $misIds)
|
||||
->whereDate('D_END', '2222-01-01 00:00:00.000000')
|
||||
->when($prvdIds !== [], fn ($qq) => $qq->whereIn('rf_PRVDID', $prvdIds));
|
||||
})
|
||||
->orderBy('FAM_V')
|
||||
->get(['LPUDoctorID', 'FAM_V', 'IM_V', 'OT_V'])
|
||||
->map(fn ($d) => [
|
||||
'label' => trim("{$d->FAM_V} {$d->IM_V} {$d->OT_V}") ?: "{$fallback} #{$d->LPUDoctorID}",
|
||||
'value' => (int) $d->LPUDoctorID,
|
||||
])
|
||||
->all();
|
||||
}
|
||||
|
||||
private function buildQuery(Request $request, User $user): AnalyticsQuery
|
||||
{
|
||||
$datasetKey = (string) $request->input('dataset');
|
||||
abort_unless($this->datasets->has($datasetKey), 422);
|
||||
|
||||
$dataset = $this->datasets->get($datasetKey);
|
||||
$customMeasures = $this->customMeasuresFor($datasetKey);
|
||||
$dimKeys = array_map(fn ($d) => $d->key, $dataset->dimensions());
|
||||
$measureKeys = [
|
||||
...array_map(fn ($m) => $m->key, $dataset->measures()),
|
||||
...array_map(fn (Measure $m) => $m->key, $customMeasures),
|
||||
];
|
||||
$filterKeys = array_map(fn ($f) => $f->key, $dataset->filters());
|
||||
|
||||
$dimensions = array_values(array_intersect((array) $request->input('dimensions', []), $dimKeys));
|
||||
$measures = array_values(array_intersect((array) $request->input('measures', []), $measureKeys));
|
||||
|
||||
$filters = [];
|
||||
foreach ((array) $request->input('filters', []) as $filter) {
|
||||
if (isset($filter['key']) && in_array($filter['key'], $filterKeys, true)) {
|
||||
$filters[] = ['key' => $filter['key'], 'value' => $filter['value'] ?? null];
|
||||
}
|
||||
}
|
||||
|
||||
$mode = $request->input('mode') === 'dynamics' ? 'dynamics' : 'period';
|
||||
$detalization = in_array($request->input('detalization'), ['day', 'week', 'month'], true)
|
||||
? $request->input('detalization') : 'month';
|
||||
|
||||
return new AnalyticsQuery(
|
||||
datasetKey: $datasetKey,
|
||||
dimensions: $dimensions,
|
||||
measures: $measures,
|
||||
filters: $filters,
|
||||
mode: $mode,
|
||||
detalization: $detalization,
|
||||
department: $this->resolveDepartment($request, $user),
|
||||
dateRange: $this->dateRangeService->getDateRangeFromRequest($request, $user),
|
||||
chart: (array) $request->input('chart', []),
|
||||
customMeasures: $customMeasures,
|
||||
user: $user,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Пользовательские показатели датасета как derived-Measure (базовый показатель × коэффициент).
|
||||
*
|
||||
* @return Measure[]
|
||||
*/
|
||||
private function customMeasuresFor(string $datasetKey): array
|
||||
{
|
||||
if (! $this->datasets->has($datasetKey)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$baseByKey = [];
|
||||
foreach ($this->datasets->get($datasetKey)->measures() as $measure) {
|
||||
$baseByKey[$measure->key] = $measure;
|
||||
}
|
||||
|
||||
$result = [];
|
||||
foreach (CustomMeasure::where('dataset', $datasetKey)->get() as $custom) {
|
||||
$base = $baseByKey[$custom->base_measure] ?? null;
|
||||
if ($base === null) {
|
||||
continue;
|
||||
}
|
||||
$factor = (float) ($custom->factor ?? 1);
|
||||
$result[] = new Measure('custom_'.$custom->id, $custom->name, $custom->unit, "({$base->select}) * {$factor}");
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function storeMeasure(Request $request)
|
||||
{
|
||||
$user = Auth::user();
|
||||
abort_unless($this->canManage($user), 403);
|
||||
|
||||
$datasetKeys = array_keys($this->datasets->all());
|
||||
$data = $request->validate([
|
||||
'name' => 'required|string|max:50',
|
||||
'dataset' => 'required|in:'.implode(',', $datasetKeys),
|
||||
'base_measure' => 'required|string',
|
||||
'factor' => 'nullable|numeric',
|
||||
'unit' => 'nullable|in:count,percent,money',
|
||||
]);
|
||||
|
||||
$baseKeys = array_map(fn ($m) => $m->key, $this->datasets->get($data['dataset'])->measures());
|
||||
abort_unless(in_array($data['base_measure'], $baseKeys, true), 422);
|
||||
|
||||
CustomMeasure::create([
|
||||
...$data,
|
||||
'factor' => $data['factor'] ?? 1,
|
||||
'created_by' => Auth::id(),
|
||||
]);
|
||||
|
||||
return back()->with('success', 'Показатель создан');
|
||||
}
|
||||
|
||||
/** @return array{result: \App\Services\Analytics\AnalyticsResult, title: string} */
|
||||
private function buildResult(Request $request): array
|
||||
{
|
||||
$user = Auth::user();
|
||||
$query = $this->buildQuery($request, $user);
|
||||
$title = (string) ($request->input('name') ?: $this->datasets->get($query->datasetKey)->label());
|
||||
|
||||
return ['result' => $this->datasets->get($query->datasetKey)->run($query), 'title' => $title];
|
||||
}
|
||||
|
||||
private function validateDocument(Request $request): array
|
||||
{
|
||||
$datasetKeys = array_keys($this->datasets->all());
|
||||
|
||||
$validated = $request->validate([
|
||||
'name' => 'required|string|max:50',
|
||||
'description' => 'nullable|string|max:200',
|
||||
'dataset' => 'required|string|in:'.implode(',', $datasetKeys),
|
||||
'config' => 'required|array',
|
||||
'period' => 'nullable|array',
|
||||
]);
|
||||
|
||||
// Чистим config от ключей, которых нет в датасете.
|
||||
$dataset = $this->datasets->get($validated['dataset']);
|
||||
$dimKeys = array_map(fn ($d) => $d->key, $dataset->dimensions());
|
||||
$measureKeys = [
|
||||
...array_map(fn ($m) => $m->key, $dataset->measures()),
|
||||
...array_map(fn (Measure $m) => $m->key, $this->customMeasuresFor($validated['dataset'])),
|
||||
];
|
||||
|
||||
$config = $validated['config'];
|
||||
$config['dimensions'] = array_values(array_intersect($config['dimensions'] ?? [], $dimKeys));
|
||||
$config['measures'] = array_values(array_intersect($config['measures'] ?? [], $measureKeys));
|
||||
$config['mode'] = ($config['mode'] ?? 'period') === 'dynamics' ? 'dynamics' : 'period';
|
||||
$config['detalization'] = in_array($config['detalization'] ?? 'month', ['day', 'week', 'month'], true) ? $config['detalization'] : 'month';
|
||||
$validated['config'] = $config;
|
||||
|
||||
return $validated;
|
||||
}
|
||||
|
||||
private function resolveDepartment(Request $request, User $user): Department
|
||||
{
|
||||
$departmentId = $request->query('departmentId');
|
||||
$departmentId = $request->input('departmentId', $request->query('departmentId'));
|
||||
$available = collect($user->availableDepartments());
|
||||
|
||||
if ($departmentId && $available->contains(fn (Department $d) => (int) $d->department_id === (int) $departmentId)) {
|
||||
@@ -113,26 +385,14 @@ class ReportsController extends Controller
|
||||
return $user->department ?? $available->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int,string> $permissions
|
||||
*/
|
||||
private function audienceLabel(array $permissions): string
|
||||
private function canManage(User $user): bool
|
||||
{
|
||||
if (empty($permissions)) {
|
||||
return 'Доступен всем';
|
||||
}
|
||||
|
||||
$labels = [
|
||||
'report.view' => 'Дежурный врач',
|
||||
'nurse.report.view' => 'Старшая медсестра',
|
||||
];
|
||||
|
||||
return implode(' · ', array_map(fn ($p) => $labels[$p] ?? $p, $permissions));
|
||||
return $user->isAdmin() || $user->isChiefDoctor() || $user->isDeputyChief();
|
||||
}
|
||||
|
||||
private function fileName(ReportDefinition $definition, string $extension): string
|
||||
private function fileName(string $title, string $extension): string
|
||||
{
|
||||
$slug = str_replace([' ', ':', '/', '\\'], '_', $definition->label());
|
||||
$slug = str_replace([' ', ':', '/', '\\'], '_', $title);
|
||||
|
||||
return sprintf('%s_%s.%s', $slug, now('Asia/Yakutsk')->format('Ymd_His'), $extension);
|
||||
}
|
||||
|
||||
@@ -85,13 +85,6 @@ const dividerColor = computed(() => themeVars.value.dividerColor)
|
||||
:tag="Link"
|
||||
href="/admin/replication"
|
||||
/>
|
||||
<ActionTile
|
||||
:icon="TbReportAnalytics"
|
||||
title="Шаблоны отчётов"
|
||||
description="Конструктор пользовательских отчётов"
|
||||
:tag="Link"
|
||||
href="/admin/report-templates"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Назад -->
|
||||
|
||||
@@ -61,14 +61,6 @@ Route::prefix('admin')->middleware(['auth'])->group(function () {
|
||||
Route::delete('/schedules/{schedule}', [\App\Http\Controllers\Web\Admin\ReplicationController::class, 'destroySchedule']);
|
||||
});
|
||||
|
||||
Route::prefix('report-templates')->group(function () {
|
||||
Route::get('/', [\App\Http\Controllers\Web\Admin\ReportTemplateController::class, 'index']);
|
||||
Route::get('/new', [\App\Http\Controllers\Web\Admin\ReportTemplateController::class, 'create']);
|
||||
Route::post('/new', [\App\Http\Controllers\Web\Admin\ReportTemplateController::class, 'store']);
|
||||
Route::get('/{template}', [\App\Http\Controllers\Web\Admin\ReportTemplateController::class, 'edit']);
|
||||
Route::put('/{template}', [\App\Http\Controllers\Web\Admin\ReportTemplateController::class, 'update']);
|
||||
Route::delete('/{template}', [\App\Http\Controllers\Web\Admin\ReportTemplateController::class, 'destroy']);
|
||||
});
|
||||
});
|
||||
|
||||
Route::prefix('statistic')->middleware(['auth'])->group(function () {
|
||||
@@ -79,8 +71,16 @@ Route::prefix('statistic')->middleware(['auth'])->group(function () {
|
||||
|
||||
Route::prefix('reports')->middleware(['auth'])->group(function () {
|
||||
Route::get('/', [\App\Http\Controllers\Web\ReportsController::class, 'index'])->name('reports.index');
|
||||
Route::get('/export/excel', [\App\Http\Controllers\Web\ReportsController::class, 'exportExcel'])->name('reports.export.excel');
|
||||
Route::get('/export/pdf', [\App\Http\Controllers\Web\ReportsController::class, 'exportPdf'])->name('reports.export.pdf');
|
||||
Route::get('/new', [\App\Http\Controllers\Web\ReportsController::class, 'create'])->name('reports.create');
|
||||
Route::post('/run', [\App\Http\Controllers\Web\ReportsController::class, 'run'])->name('reports.run');
|
||||
Route::post('/measures', [\App\Http\Controllers\Web\ReportsController::class, 'storeMeasure'])->name('reports.measures.store');
|
||||
Route::post('/export/excel', [\App\Http\Controllers\Web\ReportsController::class, 'exportExcel'])->name('reports.export.excel');
|
||||
Route::post('/export/pdf', [\App\Http\Controllers\Web\ReportsController::class, 'exportPdf'])->name('reports.export.pdf');
|
||||
Route::post('/', [\App\Http\Controllers\Web\ReportsController::class, 'store'])->name('reports.store');
|
||||
Route::get('/{document}', [\App\Http\Controllers\Web\ReportsController::class, 'show'])->name('reports.show');
|
||||
Route::put('/{document}', [\App\Http\Controllers\Web\ReportsController::class, 'update'])->name('reports.update');
|
||||
Route::delete('/{document}', [\App\Http\Controllers\Web\ReportsController::class, 'destroy'])->name('reports.destroy');
|
||||
Route::post('/{document}/duplicate', [\App\Http\Controllers\Web\ReportsController::class, 'duplicate'])->name('reports.duplicate');
|
||||
});
|
||||
|
||||
Route::get('/logout', [\App\Http\Controllers\AuthController::class, 'logout'])
|
||||
|
||||
Reference in New Issue
Block a user