Files
econom-calculator/app/Support/MedicalReport/FormTemplateBuilderPage.php
brusnitsyn 3edc8e667e
Some checks failed
tests / ci (8.5) (push) Has been cancelled
linter / quality (push) Has been cancelled
tests / ci (8.3) (push) Has been cancelled
tests / ci (8.4) (push) Has been cancelled
first commit
2026-04-03 17:20:05 +09:00

270 lines
11 KiB
PHP

<?php
namespace App\Support\MedicalReport;
use App\Models\Department;
use App\Models\HospitalUnit;
use App\Models\MedicalReport;
use App\Models\MedicalReportFormTemplate;
class FormTemplateBuilderPage
{
public function __construct(
private readonly TemplateWorkbook $templateWorkbook,
private readonly StructuredTemplateRegistry $structuredTemplateRegistry,
private readonly ReportInputTypeBlueprintFactory $reportInputTypeBlueprintFactory,
) {}
/**
* @return array<string, mixed>
*/
public function pageData(MedicalReport $medicalReport, ?string $selectedDepartmentKey = null): array
{
$departments = $this->templateWorkbook->departments();
$selectedDepartmentKey ??= $departments[0]['key'] ?? null;
$selectedDepartment = $selectedDepartmentKey === null
? null
: collect($departments)->firstWhere('key', $selectedDepartmentKey);
$selectedHospitalUnitId = $selectedDepartment['id'] ?? null;
$databaseTemplateKeys = MedicalReportFormTemplate::query()
->with('hospitalUnit:id,slug')
->get()
->map(fn (MedicalReportFormTemplate $template): ?string => $template->hospitalUnit?->slug ?: $template->department_key)
->filter()
->values()
->all();
$databaseTemplate = $selectedDepartmentKey === null
? null
: MedicalReportFormTemplate::query()
->where(function ($query) use ($selectedHospitalUnitId, $selectedDepartmentKey): void {
if ($selectedHospitalUnitId !== null) {
$query->where('hospital_unit_id', $selectedHospitalUnitId)
->orWhere('department_key', $selectedDepartmentKey);
return;
}
$query->where('department_key', $selectedDepartmentKey);
})
->first();
$effectiveTemplate = $selectedDepartmentKey === null
? null
: $this->structuredTemplateRegistry->templateForDepartment($selectedDepartmentKey);
$blueprintTemplate = $selectedDepartment === null
? null
: $this->reportInputTypeBlueprintFactory->templateForDepartment($selectedDepartment);
$builderStarterTemplate = $selectedDepartment === null
? null
: ($databaseTemplate !== null
? $effectiveTemplate
: ($blueprintTemplate ?? $effectiveTemplate));
$starterSource = $databaseTemplate !== null
? 'database'
: ($blueprintTemplate !== null ? 'blueprint' : 'source');
return [
'report' => [
'id' => $medicalReport->id,
'name' => $medicalReport->name,
'year' => $medicalReport->year,
'updated_at' => $medicalReport->updated_at?->toIso8601String(),
],
'selectedDepartment' => $selectedDepartmentKey,
'departments' => array_map(function (array $department) use ($databaseTemplateKeys): array {
return [
...$department,
'has_builder_template' => in_array($department['key'], $databaseTemplateKeys, true),
];
}, $departments),
'builder' => $selectedDepartment === null ? null : [
'department' => [
'key' => $selectedDepartment['key'],
'name' => $selectedDepartment['name'],
'profile_name' => $selectedDepartment['profile_name'] ?? null,
'report_input_type' => $selectedDepartment['report_input_type'] ?? null,
'report_input_type_label' => $selectedDepartment['report_input_type_label'] ?? null,
'sources' => $selectedDepartment['sources'] ?? [],
],
'template' => $this->builderTemplate(
$selectedDepartment['key'],
$selectedDepartment['name'],
$builderStarterTemplate,
$effectiveTemplate['department_options'] ?? null,
),
'uses_database_template' => $databaseTemplate !== null,
'starter_source' => $starterSource,
],
];
}
/**
* @param array<string, mixed>|null $effectiveTemplate
* @param array<int, array{id: int, name: string}>|null $fallbackDepartmentOptions
* @return array<string, mixed>
*/
private function builderTemplate(
string $departmentKey,
string $departmentName,
?array $effectiveTemplate,
?array $fallbackDepartmentOptions = null,
): array {
$sections = collect($effectiveTemplate['sections'] ?? [])
->map(function (array $section): array {
return [
'key' => (string) $section['key'],
'title' => (string) $section['title'],
'economist_label' => (string) ($section['economist_label'] ?? ''),
'fields' => array_values($section['fields'] ?? []),
'export_metrics' => $this->exportMetrics(
$section['export_metrics'] ?? [],
$section['fields'] ?? [],
),
'default_entries' => array_values($section['default_entries'] ?? [$section['empty_entry'] ?? []]),
];
})
->values()
->all();
if ($sections === [] && $effectiveTemplate !== null) {
$sections = [[
'key' => 'main',
'title' => 'Основной блок',
'economist_label' => $departmentName.' / Основной блок',
'fields' => array_values($effectiveTemplate['fields'] ?? []),
'export_metrics' => $this->exportMetrics(
$effectiveTemplate['export_metrics'] ?? [],
$effectiveTemplate['fields'] ?? [],
),
'default_entries' => array_values($effectiveTemplate['default_entries'] ?? [$effectiveTemplate['empty_entry'] ?? []]),
]];
}
if ($sections === []) {
$sections = [[
'key' => 'main',
'title' => 'Основной блок',
'economist_label' => $departmentName.' / Основной блок',
'fields' => [
['key' => 'name', 'label' => 'Показатель', 'type' => 'text'],
['key' => 'value', 'label' => 'Значение', 'type' => 'number'],
],
'export_metrics' => [
[
'key' => 'value_total',
'label' => 'Значение',
'source_field' => 'value',
'aggregation' => 'sum',
],
],
'default_entries' => [
['name' => '', 'value' => '0'],
],
]];
}
return [
'department' => $departmentKey,
'name' => (string) ($effectiveTemplate['title'] ?? $departmentName),
'description' => (string) ($effectiveTemplate['description'] ?? ''),
'department_options' => $effectiveTemplate['department_options']
?? $fallbackDepartmentOptions
?? $this->departmentOptions(),
'analysis_columns' => $this->analysisColumns(),
'analysis_units' => $this->analysisUnits(),
'schema' => [
'title' => (string) ($effectiveTemplate['title'] ?? $departmentName),
'description' => (string) ($effectiveTemplate['description'] ?? ''),
'sections' => $sections,
],
];
}
/**
* @return list<array{id: int, name: string}>
*/
private function departmentOptions(): array
{
return Department::query()
->where('is_active', true)
->orderBy('name')
->get(['id', 'name'])
->map(fn (Department $department): array => [
'id' => $department->id,
'name' => $department->name,
])
->all();
}
/**
* @param list<array<string, mixed>> $configuredMetrics
* @param list<array<string, mixed>> $fields
* @return list<array{key: string, label: string, source_field: string, aggregation: string}>
*/
private function exportMetrics(array $configuredMetrics, array $fields): array
{
if ($configuredMetrics !== []) {
return collect($configuredMetrics)
->map(fn (array $metric): array => [
'key' => (string) ($metric['key'] ?? ''),
'label' => (string) ($metric['label'] ?? ''),
'source_field' => (string) ($metric['source_field'] ?? ''),
'aggregation' => (string) ($metric['aggregation'] ?? 'sum'),
'analysis_column' => ($metric['analysis_column'] ?? null) !== null ? (string) $metric['analysis_column'] : null,
'row_mode' => ($metric['row_mode'] ?? null) !== null ? (string) $metric['row_mode'] : null,
'target_unit_slug' => ($metric['target_unit_slug'] ?? null) !== null ? (string) $metric['target_unit_slug'] : null,
])
->all();
}
$hasDepartmentSelect = collect($fields)
->contains(fn (array $field): bool => ($field['type'] ?? null) === 'department-select');
return collect($fields)
->filter(fn (array $field): bool => ($field['type'] ?? 'text') === 'number')
->map(fn (array $field): array => [
'key' => (string) $field['key'].'_total',
'label' => (string) $field['label'],
'source_field' => (string) $field['key'],
'aggregation' => 'sum',
'analysis_column' => null,
'row_mode' => $hasDepartmentSelect ? 'entry_department' : 'fixed_unit',
'target_unit_slug' => null,
])
->values()
->all();
}
/**
* @return list<array{key: string, label: string, coordinate: string}>
*/
private function analysisColumns(): array
{
return collect(config('medical-report.economist_analysis_columns', []))
->filter(fn (mixed $column): bool => is_array($column))
->map(fn (array $column): array => [
'key' => (string) ($column['key'] ?? ''),
'label' => (string) ($column['label'] ?? ''),
'coordinate' => (string) ($column['coordinate'] ?? ''),
])
->filter(fn (array $column): bool => $column['key'] !== '' && $column['label'] !== '')
->values()
->all();
}
/**
* @return list<array{slug: string, name: string}>
*/
private function analysisUnits(): array
{
return HospitalUnit::query()
->where('is_active', true)
->orderBy('name')
->get(['slug', 'name'])
->map(fn (HospitalUnit $hospitalUnit): array => [
'slug' => $hospitalUnit->slug,
'name' => $hospitalUnit->name,
])
->all();
}
}