*/ public function pageData( MedicalReport $medicalReport, ?string $selectedDepartmentKey = null, ?string $selectedSheetKey = null, ): array { $definition = $this->templateWorkbook->definition(); $departments = $definition['departments']; $selectedDepartmentKey ??= $departments[0]['key'] ?? null; $structuredTemplate = $selectedDepartmentKey === null ? null : $this->structuredTemplate($medicalReport, $selectedDepartmentKey); $availableSheets = $structuredTemplate === null ? array_values(array_filter( array_map( fn (string $sheetKey): array => $this->sheetSummary($medicalReport, $sheetKey, $selectedDepartmentKey), $definition['order'], ), fn (array $sheet): bool => $sheet['editable_count'] > 0, )) : []; $selectedSheetKey = $structuredTemplate === null ? ($selectedSheetKey ?? $availableSheets[0]['key'] ?? null) : null; $currentSheet = $structuredTemplate === null ? (collect($availableSheets)->firstWhere('key', $selectedSheetKey) ?? $availableSheets[0] ?? null) : null; return [ 'report' => [ 'id' => $medicalReport->id, 'name' => $medicalReport->name, 'year' => $medicalReport->year, 'updated_at' => $medicalReport->updated_at?->toIso8601String(), ], 'departments' => array_map( fn (array $department): array => $this->departmentSummary($medicalReport, $department), $departments, ), 'selectedDepartment' => $selectedDepartmentKey, 'selectedSheet' => $selectedSheetKey, 'sheets' => $availableSheets, 'structuredTemplate' => $structuredTemplate, 'currentSheet' => $currentSheet === null ? null : [ 'key' => $currentSheet['key'], 'name' => $currentSheet['name'], 'fields' => $this->sheetFields($medicalReport, $currentSheet['key'], $selectedDepartmentKey), ], 'summary' => [ 'department_count' => count($departments), 'sheet_count' => $structuredTemplate === null ? count($availableSheets) : 1, 'filled_count' => $this->filledCount($medicalReport, $selectedDepartmentKey), 'editable_count' => $structuredTemplate === null ? array_sum(array_column($availableSheets, 'editable_count')) : $this->structuredEditableCount($structuredTemplate), ], ]; } /** * @return array */ public function persistedSheetValues(MedicalReport $medicalReport, string $departmentKey, string $sheetKey): array { /** @var array>> $overrides */ $overrides = $medicalReport->input_overrides ?? []; return Arr::get($overrides, $departmentKey.'.'.$sheetKey, []); } /** * @return array */ private function departmentSummary(MedicalReport $medicalReport, array $department): array { $filledCount = $this->filledCount($medicalReport, $department['key']); return [ ...$department, 'filled_count' => $filledCount, 'has_template' => $filledCount > 0 || $this->structuredTemplateRegistry->templateForDepartment($department['key']) !== null || collect($this->templateWorkbook->sheetKeys())->contains( fn (string $sheetKey): bool => ! empty( $this->templateWorkbook->sheet($sheetKey)['fields_by_department'][$department['key']] ?? [] ) ), ]; } /** * @return array */ private function sheetSummary(MedicalReport $medicalReport, string $sheetKey, ?string $departmentKey): array { $sheet = $this->templateWorkbook->sheet($sheetKey); $fields = $departmentKey !== null ? Arr::get($sheet, 'fields_by_department.'.$departmentKey, []) : []; $persistedValues = $departmentKey !== null ? $this->persistedSheetValues($medicalReport, $departmentKey, $sheetKey) : []; return [ 'key' => $sheetKey, 'name' => $sheet['name'], 'editable_count' => count($fields), 'filled_count' => count($persistedValues), ]; } /** * @return list> */ private function sheetFields(MedicalReport $medicalReport, string $sheetKey, ?string $departmentKey): array { if ($departmentKey === null) { return []; } $sheet = $this->templateWorkbook->sheet($sheetKey); $persistedValues = $this->persistedSheetValues($medicalReport, $departmentKey, $sheetKey); return array_values(array_map( function (array $field) use ($persistedValues): array { $currentValue = Arr::get($persistedValues, $field['coordinate'], $field['default']); return [ ...$field, 'description' => implode(' / ', array_filter([ $field['row_label'], $field['column_label'], $field['coordinate'], ])), 'value' => (string) $currentValue, ]; }, Arr::get($sheet, 'fields_by_department.'.$departmentKey, []), )); } private function filledCount(MedicalReport $medicalReport, ?string $departmentKey): int { if ($departmentKey === null) { return 0; } /** @var array>> $overrides */ $overrides = $medicalReport->input_overrides ?? []; $coordinateCount = (int) collect(Arr::get($overrides, $departmentKey, [])) ->sum(fn (array $sheetValues): int => count($sheetValues)); $structuredCount = (int) collect(Arr::get($overrides, 'structured_departments.'.$departmentKey, [])) ->sum(function (array $template): int { $directEntries = count(array_filter( $template['entries'] ?? [], fn (array $entry): bool => $this->entryHasValue($entry), )); $sectionEntries = (int) collect($template['sections'] ?? []) ->sum(fn (array $section): int => count(array_filter( $section['entries'] ?? [], fn (array $entry): bool => $this->entryHasValue($entry), ))); return $directEntries + $sectionEntries; }); return $coordinateCount + $structuredCount; } /** * @return array|null */ private function structuredTemplate(MedicalReport $medicalReport, string $departmentKey): ?array { $template = $this->structuredTemplateRegistry->templateForDepartment($departmentKey); if ($template === null) { return null; } $entries = Arr::get( $medicalReport->input_overrides ?? [], 'structured_departments.'.$departmentKey.'.'.$template['key'].'.entries', [], ); if (isset($template['sections']) && is_array($template['sections'])) { return [ ...$template, 'sections' => array_map(function (array $section) use ($medicalReport, $departmentKey, $template): array { $entries = Arr::get( $medicalReport->input_overrides ?? [], 'structured_departments.'.$departmentKey.'.'.$template['key'].'.sections.'.$section['key'].'.entries', [], ); $resolvedEntries = $entries === [] ? ($section['default_entries'] !== [] ? $section['default_entries'] : [$section['empty_entry']]) : $entries; return [ ...$section, 'entries' => $resolvedEntries, 'totals' => $this->structuredTotals( $resolvedEntries, $section['fields'] ?? [], ), ]; }, $template['sections']), ]; } return [ ...$template, 'entries' => $entries === [] ? ($template['default_entries'] !== [] ? $template['default_entries'] : [$template['empty_entry']]) : $entries, 'totals' => $this->structuredTotals( $entries === [] ? ($template['default_entries'] !== [] ? $template['default_entries'] : [$template['empty_entry']]) : $entries, $template['fields'] ?? [], ), ]; } /** * @param array $template */ private function structuredEditableCount(array $template): int { if (isset($template['sections']) && is_array($template['sections'])) { return (int) collect($template['sections']) ->sum(fn (array $section): int => count($section['entries'] ?? []) * count($section['fields'] ?? [])); } return (int) count($template['entries'] ?? []) * count($template['fields'] ?? []); } /** * @param array $entry */ private function entryHasValue(array $entry): bool { return collect($entry) ->filter(fn (mixed $value): bool => trim((string) $value) !== '') ->isNotEmpty(); } /** * @param list> $entries * @param list> $fields * @return list */ private function structuredTotals(array $entries, array $fields): array { return collect($fields) ->filter(fn (array $field): bool => ($field['type'] ?? 'text') === 'number') ->map(function (array $field) use ($entries): array { $sum = collect($entries)->sum(function (array $entry) use ($field): float { $value = trim((string) ($entry[$field['key']] ?? '')); if ($value === '') { return 0; } return (float) str_replace(',', '.', $value); }); return [ 'key' => $field['key'], 'label' => (string) $field['label'], 'value' => $this->formatNumber($sum), ]; }) ->values() ->all(); } private function formatNumber(float $value): string { if ((float) ((int) $value) === $value) { return (string) ((int) $value); } return rtrim(rtrim(number_format($value, 2, '.', ''), '0'), '.'); } }