get(route('medical-reports.index')); $response->assertRedirect(route('login')); }); test('authenticated users can open the medical report workspace', function () { $user = User::factory()->create(); $report = MedicalReport::factory()->create([ 'name' => 'МЕД 2026', ]); /** @var TemplateWorkbook $templateWorkbook */ $templateWorkbook = app(TemplateWorkbook::class); $departmentKey = $templateWorkbook->departments()[0]['key']; $this->actingAs($user); $response = $this->get(route('medical-reports.show', [ 'medicalReport' => $report, 'department' => $departmentKey, ])); $response ->assertOk() ->assertInertia( fn ($page) => $page ->component('medical-reports/Show') ->where('report.id', $report->id) ->where('selectedDepartment', $departmentKey) ->has('departments') ->has('sheets') ); }); test('medical report workspace exposes source driven departments', function () { $user = User::factory()->create(); $report = MedicalReport::factory()->create([ 'input_overrides' => [], ]); /** @var TemplateWorkbook $templateWorkbook */ $templateWorkbook = app(TemplateWorkbook::class); $department = collect($templateWorkbook->departments()) ->firstWhere('name', 'Рентгенологическое отделение'); expect($department)->not->toBeNull(); $this->actingAs($user); $response = $this->get(route('medical-reports.show', [ 'medicalReport' => $report, 'department' => $department['key'], ])); $response->assertInertia( fn ($page) => $page ->where( 'departments', fn ($departments) => collect($departments)->contains( fn (array $department): bool => $department['name'] === 'Рентгенологическое отделение' && ! empty($department['sources']) && in_array($department['sources'][0]['extension'], ['pdf', 'xlsx'], true) ) ) ->where('structuredTemplate.title', 'Рентгенологическое отделение') ->has('structuredTemplate.sections', 5) ); }); test('authenticated users can open the economist summary page', function () { $user = User::factory()->create(); $report = MedicalReport::factory()->create([ 'name' => 'МЕД 2026', 'input_overrides' => [], ]); $this->actingAs($user); $response = $this->get(route('medical-reports.economists', $report)); $response ->assertOk() ->assertInertia( fn ($page) => $page ->component('medical-reports/Economists') ->where('report.id', $report->id) ->has('summarySheet.columns') ->has('summarySheet.rows') ); }); test('authenticated users can open the form builder page', function () { $user = User::factory()->create(); $report = MedicalReport::factory()->create(); /** @var TemplateWorkbook $templateWorkbook */ $templateWorkbook = app(TemplateWorkbook::class); $departmentKey = $templateWorkbook->departments()[0]['key']; $this->actingAs($user); $response = $this->get(route('medical-reports.builder', [ 'medicalReport' => $report, 'department' => $departmentKey, ])); $response->assertOk()->assertInertia( fn ($page) => $page ->component('medical-reports/Builder') ->where('selectedDepartment', $departmentKey) ->where('builder.department.report_input_type', fn ($value) => $value === null || is_string($value)) ->has('builder.template.schema.sections') ); }); test('builder uses configured report input blueprint for reporting units', function () { $user = User::factory()->create(); $report = MedicalReport::factory()->create(); $this->seed([ ProfileSeeder::class, HospitalUnitSeeder::class, ]); /** @var TemplateWorkbook $templateWorkbook */ $templateWorkbook = app(TemplateWorkbook::class); $department = collect($templateWorkbook->departments()) ->firstWhere('name', 'Бактериологическая лаборатория'); $this->actingAs($user); $response = $this->get(route('medical-reports.builder', [ 'medicalReport' => $report, 'department' => $department['key'], ])); $response->assertOk()->assertInertia( fn ($page) => $page ->where('builder.department.report_input_type', 'department_two_metrics') ->where('builder.starter_source', 'blueprint') ->where('builder.template.schema.sections.0.title', 'ОМС по отделениям') ->where('builder.template.schema.sections.1.title', 'Договорные исследования') ->where('builder.template.schema.sections.0.fields.1.label', 'Лаб. ед.') ->where('builder.template.schema.sections.1.fields.0.label', 'Организация') ->where('builder.template.schema.sections.0.default_entries', fn ($entries) => count($entries) === 32) ->where('builder.template.schema.sections.1.default_entries.0.organization_name', 'Мицар') ); }); test('kdl builder blueprint exposes separate sections for emergency planned and immunology reports', function () { $user = User::factory()->create(); $report = MedicalReport::factory()->create(); $this->seed([ ProfileSeeder::class, HospitalUnitSeeder::class, ]); /** @var TemplateWorkbook $templateWorkbook */ $templateWorkbook = app(TemplateWorkbook::class); $department = collect($templateWorkbook->departments()) ->firstWhere('name', 'Клинико-диагностическая лаборатория'); $this->actingAs($user); $response = $this->get(route('medical-reports.builder', [ 'medicalReport' => $report, 'department' => $department['key'], ])); $response->assertOk()->assertInertia( fn ($page) => $page ->where('builder.department.report_input_type', 'department_multi_metrics') ->where('builder.template.schema.sections.0.title', 'Экстренные анализы') ->where('builder.template.schema.sections.1.title', 'Плановая служба') ->where('builder.template.schema.sections.2.title', 'Иммунологические исследования') ->where('builder.template.schema.sections.0.fields.1.label', 'Биох.') ->where('builder.template.schema.sections.0.fields.3.label', 'Гематология') ->where('builder.template.schema.sections.2.fields.1.label', 'Мелк. инф.') ->where('builder.template.schema.sections.2.fields.8.label', 'Иммуноглобулины') ); }); test('functional diagnostics builder blueprint exposes refined report section titles and fields', function () { $user = User::factory()->create(); $report = MedicalReport::factory()->create(); $this->seed([ ProfileSeeder::class, HospitalUnitSeeder::class, DepartmentSeeder::class, ]); /** @var TemplateWorkbook $templateWorkbook */ $templateWorkbook = app(TemplateWorkbook::class); $department = collect($templateWorkbook->departments()) ->firstWhere('name', 'Отделение функциональной диагностики'); $this->actingAs($user); $response = $this->get(route('medical-reports.builder', [ 'medicalReport' => $report, 'department' => $department['key'], ])); $response->assertOk()->assertInertia( fn ($page) => $page ->where('builder.template.schema.sections.0.title', 'Лаборатория клинической нейрофизиологии') ->where('builder.template.schema.sections.0.fields.2.label', 'Эхо-Эс') ->where('builder.template.schema.sections.0.fields.3.label', 'Холтер') ->where('builder.template.schema.sections.0.fields.4.label', 'АОКП') ->where('builder.template.schema.sections.1.title', 'РСЦ и сосудистые исследования') ->where('builder.template.schema.sections.2.title', 'ПДЦ / стационар и исследования на месте') ); }); test('endoscopy structured template blueprint exposes load and broncho sections', function () { $this->seed([ ProfileSeeder::class, HospitalUnitSeeder::class, ]); /** @var TemplateWorkbook $templateWorkbook */ $templateWorkbook = app(TemplateWorkbook::class); /** @var StructuredTemplateRegistry $structuredTemplateRegistry */ $structuredTemplateRegistry = app(StructuredTemplateRegistry::class); $department = collect($templateWorkbook->departments()) ->firstWhere('name', 'Эндоскопическое отделение'); $template = $structuredTemplateRegistry->templateForDepartment($department['key']); expect($template)->not->toBeNull(); expect(data_get($template, 'sections.0.title'))->toBe('Нагрузка по отделениям стационара и поликлиники'); expect(data_get($template, 'sections.1.title'))->toBe('Бронхокабинет / отдельный учет'); expect(data_get($template, 'sections.0.fields.1.label'))->toBe('Гастроскопия / число иссл.'); expect(data_get($template, 'sections.1.fields.5.label'))->toBe('Всего бронхоскопий'); }); test('pathology builder blueprint exposes inpatient ambulatory and autopsy sections with refined labels', function () { $user = User::factory()->create(); $report = MedicalReport::factory()->create(); $this->seed([ ProfileSeeder::class, HospitalUnitSeeder::class, DepartmentSeeder::class, ]); /** @var TemplateWorkbook $templateWorkbook */ $templateWorkbook = app(TemplateWorkbook::class); $department = collect($templateWorkbook->departments()) ->firstWhere('name', 'Патологоанатомическое отделение'); $this->actingAs($user); $response = $this->get(route('medical-reports.builder', [ 'medicalReport' => $report, 'department' => $department['key'], ])); $response->assertOk()->assertInertia( fn ($page) => $page ->where('builder.template.schema.sections.0.title', 'Стационар / гистологические исследования') ->where('builder.template.schema.sections.1.title', 'Амбулаторно / внешние источники') ->where('builder.template.schema.sections.2.fields.0.label', 'Всего аутопсий') ->where('builder.template.schema.sections.2.fields.3.label', 'Кусочки при вскрытии') ); }); test('transfusion builder blueprint exposes department load labels', function () { $user = User::factory()->create(); $report = MedicalReport::factory()->create(); $this->seed([ ProfileSeeder::class, HospitalUnitSeeder::class, DepartmentSeeder::class, ]); /** @var TemplateWorkbook $templateWorkbook */ $templateWorkbook = app(TemplateWorkbook::class); $department = collect($templateWorkbook->departments()) ->firstWhere('name', 'Отделение трансфузиологии'); $this->actingAs($user); $response = $this->get(route('medical-reports.builder', [ 'medicalReport' => $report, 'department' => $department['key'], ])); $response->assertOk()->assertInertia( fn ($page) => $page ->where('builder.template.schema.sections.0.title', 'Трансфузионная нагрузка по отделениям') ->where('builder.template.schema.sections.0.fields.1.label', 'Количество трансфузий') ->where('builder.template.schema.sections.0.fields.2.label', 'Плазмаферез') ->where('builder.template.schema.sections.0.default_entries', fn ($entries) => count($entries) === 29) ); }); test('main reanimation builder blueprint exposes department movement report fields', function () { $user = User::factory()->create(); $report = MedicalReport::factory()->create(); $this->seed([ ProfileSeeder::class, HospitalUnitSeeder::class, DepartmentSeeder::class, ]); /** @var TemplateWorkbook $templateWorkbook */ $templateWorkbook = app(TemplateWorkbook::class); $department = collect($templateWorkbook->departments()) ->firstWhere('name', 'Отделение анестезиологии-реанимации с палатами реанимации и интенсивной терапии'); $this->actingAs($user); $response = $this->get(route('medical-reports.builder', [ 'medicalReport' => $report, 'department' => $department['key'], ])); $response->assertOk()->assertInertia( fn ($page) => $page ->where('builder.template.schema.sections.0.title', 'Движение больных по отделениям') ->where('builder.template.schema.sections.0.fields.1.label', 'Больные') ->where('builder.template.schema.sections.0.fields.2.label', 'Проведено койко-дней') ); }); test('opc reanimation builder blueprint exposes unit bed days rows and monthly total', function () { $user = User::factory()->create(); $report = MedicalReport::factory()->create(); $this->seed([ ProfileSeeder::class, HospitalUnitSeeder::class, ]); /** @var TemplateWorkbook $templateWorkbook */ $templateWorkbook = app(TemplateWorkbook::class); $department = collect($templateWorkbook->departments()) ->firstWhere('name', 'Отделение анестезиологии-реанимации с палатами реанимации и интенсивной терапии Областного перинатального центра'); $this->actingAs($user); $response = $this->get(route('medical-reports.builder', [ 'medicalReport' => $report, 'department' => $department['key'], ])); $response->assertOk()->assertInertia( fn ($page) => $page ->where('builder.template.schema.sections.0.title', 'Койко-дни по отделениям ОПЦ') ->where('builder.template.schema.sections.0.default_entries.0.unit_name', 'Родильное отделение') ->where('builder.template.schema.sections.0.default_entries.1.unit_name', 'Патология беременных') ->where('builder.template.schema.sections.1.title', 'Итого за месяц') ->where('builder.template.schema.sections.1.default_entries.0.label', 'Всего') ); }); test('rsc reanimation builder blueprint exposes department movement report fields', function () { $user = User::factory()->create(); $report = MedicalReport::factory()->create(); $this->seed([ ProfileSeeder::class, HospitalUnitSeeder::class, DepartmentSeeder::class, ]); /** @var TemplateWorkbook $templateWorkbook */ $templateWorkbook = app(TemplateWorkbook::class); $department = collect($templateWorkbook->departments()) ->firstWhere('name', 'Отделение анестезиологии-реанимации с палатами реанимации и интенсивной терапии Регионального сосудистого центра'); $this->actingAs($user); $response = $this->get(route('medical-reports.builder', [ 'medicalReport' => $report, 'department' => $department['key'], ])); $response->assertOk()->assertInertia( fn ($page) => $page ->where('builder.template.schema.sections.0.title', 'Движение больных по отделениям РСЦ') ->where('builder.template.schema.sections.0.fields.1.label', 'Больные') ->where('builder.template.schema.sections.0.fields.2.label', 'Проведено койко-дней') ); }); test('operation block builder blueprint exposes monthly totals and operations catalog sections', function () { $user = User::factory()->create(); $report = MedicalReport::factory()->create(); $this->seed([ ProfileSeeder::class, HospitalUnitSeeder::class, DepartmentSeeder::class, ]); /** @var TemplateWorkbook $templateWorkbook */ $templateWorkbook = app(TemplateWorkbook::class); $department = collect($templateWorkbook->departments()) ->firstWhere('name', 'Операционный блок'); $this->actingAs($user); $response = $this->get(route('medical-reports.builder', [ 'medicalReport' => $report, 'department' => $department['key'], ])); $response->assertOk()->assertInertia( fn ($page) => $page ->where('builder.department.report_input_type', 'service_catalog_channels') ->where('builder.template.schema.sections.0.title', 'Свод по отделениям за месяц') ->where('builder.template.schema.sections.1.title', 'Профили и блоки операций') ->where('builder.template.schema.sections.2.title', 'Операции') ->where('builder.template.schema.sections.2.fields.0.label', 'Профиль') ->where('builder.template.schema.sections.2.fields.1.label', 'Наименование операции') ); }); test('radiology builder blueprint uses section-specific row labels', function () { $user = User::factory()->create(); $report = MedicalReport::factory()->create(); $this->seed([ ProfileSeeder::class, HospitalUnitSeeder::class, DepartmentSeeder::class, ]); /** @var TemplateWorkbook $templateWorkbook */ $templateWorkbook = app(TemplateWorkbook::class); $department = collect($templateWorkbook->departments()) ->firstWhere('name', 'Рентгенологическое отделение'); $this->actingAs($user); $response = $this->get(route('medical-reports.builder', [ 'medicalReport' => $report, 'department' => $department['key'], ])); $response->assertOk()->assertInertia( fn ($page) => $page ->where('builder.template.schema.sections.0.fields.0.label', 'Направивший специалист') ->where('builder.template.schema.sections.1.fields.0.label', 'Направивший специалист') ->where('builder.template.schema.sections.2.fields.0.label', 'Отделение / источник') ->where('builder.template.schema.sections.3.fields.0.label', 'Исследование') ); }); test('rhmdil builder blueprint exposes monthly totals thyroid tab and interventions catalog sections', function () { $user = User::factory()->create(); $report = MedicalReport::factory()->create(); $this->seed([ ProfileSeeder::class, HospitalUnitSeeder::class, DepartmentSeeder::class, ]); /** @var TemplateWorkbook $templateWorkbook */ $templateWorkbook = app(TemplateWorkbook::class); $department = collect($templateWorkbook->departments()) ->firstWhere('name', 'РХМДиЛ'); $this->actingAs($user); $response = $this->get(route('medical-reports.builder', [ 'medicalReport' => $report, 'department' => $department['key'], ])); $response->assertOk()->assertInertia( fn ($page) => $page ->where('builder.department.report_input_type', 'service_catalog_channels') ->where('builder.template.schema.sections.0.title', 'Свод по отделениям за месяц') ->where('builder.template.schema.sections.1.title', 'ТАБ щитовидной железы') ->where('builder.template.schema.sections.2.title', 'Каталог вмешательств') ->where('builder.template.schema.sections.1.fields.1.label', 'ОМС / пациенты') ->where('builder.template.schema.sections.2.fields.0.label', 'Группа') ); }); test('maternity builder blueprint exposes deliveries operations and opu section with note field', function () { $user = User::factory()->create(); $report = MedicalReport::factory()->create(); $this->seed([ ProfileSeeder::class, HospitalUnitSeeder::class, DepartmentSeeder::class, ]); /** @var TemplateWorkbook $templateWorkbook */ $templateWorkbook = app(TemplateWorkbook::class); $department = collect($templateWorkbook->departments()) ->firstWhere('name', 'Родильное отделение'); $this->actingAs($user); $response = $this->get(route('medical-reports.builder', [ 'medicalReport' => $report, 'department' => $department['key'], ])); $response->assertOk()->assertInertia( fn ($page) => $page ->where('builder.template.schema.sections.0.title', 'Количество пациентов по подразделениям') ->where('builder.template.schema.sections.1.title', 'Источники поступления пациентов') ->where('builder.template.schema.sections.2.title', 'Роды, операции и ОПУ') ->where('builder.template.schema.sections.2.fields.5.label', 'Примечание') ); }); test('cso builder blueprint exposes main and opc sterilization sections', function () { $user = User::factory()->create(); $report = MedicalReport::factory()->create(); $this->seed([ ProfileSeeder::class, HospitalUnitSeeder::class, DepartmentSeeder::class, ]); /** @var TemplateWorkbook $templateWorkbook */ $templateWorkbook = app(TemplateWorkbook::class); $department = collect($templateWorkbook->departments()) ->firstWhere('name', 'Централизованное стерилизационное отделение'); $this->actingAs($user); $response = $this->get(route('medical-reports.builder', [ 'medicalReport' => $report, 'department' => $department['key'], ])); $response->assertOk()->assertInertia( fn ($page) => $page ->where('builder.template.schema.sections.0.title', 'Основное ЦСО') ->where('builder.template.schema.sections.1.title', 'ЦСО ОПЦ') ->where('builder.template.schema.sections.0.fields.1.label', 'Количество упаковок') ->where('builder.template.schema.sections.1.fields.1.label', 'Количество биксов и пакетов') ); }); test('uzd pdc builder blueprint exposes departments and methods sections', function () { $user = User::factory()->create(); $report = MedicalReport::factory()->create(); $this->seed([ ProfileSeeder::class, HospitalUnitSeeder::class, DepartmentSeeder::class, ]); /** @var TemplateWorkbook $templateWorkbook */ $templateWorkbook = app(TemplateWorkbook::class); $department = collect($templateWorkbook->departments()) ->firstWhere('name', 'Отделение ультразвуковой диагностики'); $this->actingAs($user); $response = $this->get(route('medical-reports.builder', [ 'medicalReport' => $report, 'department' => $department['key'], ])); $response->assertOk()->assertInertia( fn ($page) => $page ->where('builder.template.schema.sections.0.title', 'Исследования по отделениям') ->where('builder.template.schema.sections.1.title', 'Свод по методикам') ->where('builder.template.schema.sections.1.fields.0.label', 'Методика') ->where('builder.template.schema.sections.0.default_entries', fn ($entries) => count($entries) === 32) ->where('builder.template.schema.sections.1.default_entries.0.method_name', 'ЦДК') ); }); test('uzd opc builder blueprint exposes polyclinic stationary and monthly total sections', function () { $user = User::factory()->create(); $report = MedicalReport::factory()->create(); $this->seed([ ProfileSeeder::class, HospitalUnitSeeder::class, DepartmentSeeder::class, ]); /** @var TemplateWorkbook $templateWorkbook */ $templateWorkbook = app(TemplateWorkbook::class); $department = collect($templateWorkbook->departments()) ->firstWhere('name', 'Отделение ультразвуковой диагностики ОПЦ'); $this->actingAs($user); $response = $this->get(route('medical-reports.builder', [ 'medicalReport' => $report, 'department' => $department['key'], ])); $response->assertOk()->assertInertia( fn ($page) => $page ->where('builder.template.schema.sections.0.title', 'Кабинеты УЗД поликлиники ОПЦ') ->where('builder.template.schema.sections.1.title', 'Кабинеты УЗД стационара ОПЦ') ->where('builder.template.schema.sections.2.title', 'Итого за месяц') ->where('builder.template.schema.sections.2.fields.0.label', 'Строка') ->where('builder.template.schema.sections.0.default_entries.0.cabinet_group', 'Каб. №2177, №2178') ->where('builder.template.schema.sections.2.default_entries.0.label', 'Итого за месяц') ); }); test('kdl bacteriology department uses structured row template with database department options', function () { $user = User::factory()->create(); $report = MedicalReport::factory()->create(); $option = Department::factory()->create([ 'name' => 'ОТП', 'code' => 'otp', ]); /** @var TemplateWorkbook $templateWorkbook */ $templateWorkbook = app(TemplateWorkbook::class); $department = collect($templateWorkbook->departments()) ->firstWhere('name', 'Бактериологическая лаборатория'); expect($department)->not->toBeNull(); $this->actingAs($user); $response = $this->get(route('medical-reports.show', [ 'medicalReport' => $report, 'department' => $department['key'], ])); $response->assertInertia( fn ($page) => $page ->where('structuredTemplate.title', 'Бактериологическая лаборатория') ->where( 'structuredTemplate.department_options', fn ($options) => collect($options)->contains( fn (array $item): bool => $item['id'] === $option->id ) ) ->where('structuredTemplate.sections.0.fields.1.label', 'Лаб. ед.') ); }); test('structured department rows are persisted for a medical report', function () { $user = User::factory()->create(); $report = MedicalReport::factory()->create(); $option = Department::factory()->create([ 'name' => 'ОТП', 'code' => 'otp', ]); /** @var TemplateWorkbook $templateWorkbook */ $templateWorkbook = app(TemplateWorkbook::class); /** @var StructuredTemplateRegistry $structuredTemplateRegistry */ $structuredTemplateRegistry = app(StructuredTemplateRegistry::class); $department = collect($templateWorkbook->departments()) ->firstWhere('name', 'Бактериологическая лаборатория'); $template = $structuredTemplateRegistry->templateForDepartment($department['key']); $fieldKeys = collect($template['sections'][0]['fields'])->pluck('key')->values(); $sectionKey = $template['sections'][0]['key']; $this->actingAs($user); $response = $this->put(route('medical-reports.update-structured', $report), [ 'department' => $department['key'], 'template_key' => $template['key'], 'sections' => [ $sectionKey => [ 'entries' => [ [ $fieldKeys[0] => $option->id, $fieldKeys[1] => '297.0', $fieldKeys[2] => 103, ], ], ], ], ]); $response->assertRedirect(); expect(data_get( $report->fresh()->input_overrides, 'structured_departments.'.$department['key'].'.'.$template['key'].'.sections.'.$sectionKey.'.entries', ))->toBe([ [ $fieldKeys[0] => (string) $option->id, $fieldKeys[1] => '297.0', $fieldKeys[2] => '103', ], ]); }); test('section totals are persisted for radiology and exposed to economists', function () { $user = User::factory()->create(); $report = MedicalReport::factory()->create(); /** @var TemplateWorkbook $templateWorkbook */ $templateWorkbook = app(TemplateWorkbook::class); /** @var StructuredTemplateRegistry $structuredTemplateRegistry */ $structuredTemplateRegistry = app(StructuredTemplateRegistry::class); $department = collect($templateWorkbook->departments()) ->firstWhere('name', 'Рентгенологическое отделение'); $template = $structuredTemplateRegistry->templateForDepartment($department['key']); $this->actingAs($user); $response = $this->put(route('medical-reports.update-structured', $report), [ 'department' => $department['key'], 'template_key' => $template['key'], 'sections' => [ 'adult_polyclinic' => [ 'entries' => [ [ 'study_name' => 'Терапевт', 'units' => '10.5', 'people_count' => '2', 'research_count' => '3', 'copies_count' => '1', 'barium' => '0', 'contrast' => '0', ], [ 'study_name' => 'Хирург', 'units' => '4.5', 'people_count' => '1', 'research_count' => '2', 'copies_count' => '0', 'barium' => '0', 'contrast' => '1', ], ], ], 'child_polyclinic' => ['entries' => []], 'inpatient' => ['entries' => []], 'ct_115' => ['entries' => []], 'ct_125' => ['entries' => []], ], ]); $response->assertRedirect(); expect(data_get( $report->fresh()->input_overrides, 'structured_departments.'.$department['key'].'.'.$template['key'].'.sections.adult_polyclinic.entries', ))->toBe([ [ 'study_name' => 'Терапевт', 'units' => '10.5', 'people_count' => '2', 'research_count' => '3', 'copies_count' => '1', 'barium' => '0', 'contrast' => '0', ], [ 'study_name' => 'Хирург', 'units' => '4.5', 'people_count' => '1', 'research_count' => '2', 'copies_count' => '0', 'barium' => '0', 'contrast' => '1', ], ]); $economistResponse = $this->get(route('medical-reports.economists', $report)); $economistResponse->assertInertia( fn ($page) => $page ->where('summarySheet.name', 'Итоги отделений') ->where( 'summarySheet.rows', fn ($rows) => collect($rows)->contains( fn (array $row): bool => $row['department'] === 'Рентгенологическое отделение / Поликлиника взрослая' && collect($row['values'])->contains( fn (array $value): bool => $value['name'] === 'Рентген / усл. ед.' && $value['value'] === '15' ) && collect($row['values'])->contains( fn (array $value): bool => $value['name'] === 'Рентген / исследования' && $value['value'] === '5' ) ) ) ); }); test('builder templates can be saved and then used by report input page', function () { $user = User::factory()->create(); $report = MedicalReport::factory()->create(); $this->seed([ ProfileSeeder::class, HospitalUnitSeeder::class, ]); /** @var TemplateWorkbook $templateWorkbook */ $templateWorkbook = app(TemplateWorkbook::class); $department = collect($templateWorkbook->departments()) ->firstWhere('name', 'Бактериологическая лаборатория'); $this->actingAs($user); $response = $this->put(route('medical-reports.upsert-builder', $report), [ 'department' => $department['key'], 'name' => 'КДЛ форма', 'description' => 'Конструктор', 'schema' => [ 'title' => 'КДЛ форма', 'description' => 'Конструктор', 'sections' => [ [ 'key' => 'main', 'title' => 'Основной блок', 'economist_label' => 'КДЛ / Основной блок', 'fields' => [ ['key' => 'department_id', 'label' => 'Отделение', 'type' => 'department-select'], ['key' => 'lab_units', 'label' => 'Лаб. ед.', 'type' => 'number'], ], 'export_metrics' => [ [ 'key' => 'lab_units_total', 'label' => 'Итого лаб. ед.', 'source_field' => 'lab_units', 'aggregation' => 'sum', ], ], 'default_entries' => [ ['department_id' => '', 'lab_units' => '0'], ], ], ], ], ]); $response->assertRedirect(); $template = MedicalReportFormTemplate::query() ->where('department_key', $department['key']) ->first(); $hospitalUnit = HospitalUnit::query() ->where('slug', $department['key']) ->first(); expect($template)->not->toBeNull(); expect($hospitalUnit)->not->toBeNull(); expect($template?->hospital_unit_id)->toBe($hospitalUnit?->id); expect(data_get($template?->schema, 'title'))->toBe('КДЛ форма'); expect(data_get($template?->schema, 'sections.0.key'))->toBe('main'); expect(data_get($template?->schema, 'sections.0.fields.0'))->toBe([ 'key' => 'department_id', 'label' => 'Отделение', 'type' => 'department-select', ]); expect(data_get($template?->schema, 'sections.0.fields.1'))->toBe([ 'key' => 'lab_units', 'label' => 'Лаб. ед.', 'type' => 'number', ]); expect(data_get($template?->schema, 'sections.0.export_metrics.0'))->toBe([ 'key' => 'lab_units_total', 'label' => 'Итого лаб. ед.', 'source_field' => 'lab_units', 'aggregation' => 'sum', 'analysis_column' => null, 'row_mode' => null, 'target_unit_slug' => null, ]); $showResponse = $this->get(route('medical-reports.show', [ 'medicalReport' => $report, 'department' => $department['key'], ])); $showResponse->assertInertia( fn ($page) => $page ->where('structuredTemplate.title', 'КДЛ форма') ->where('structuredTemplate.sections.0.title', 'Основной блок') ->where('structuredTemplate.sections.0.fields.1.label', 'Лаб. ед.') ); }); test('economist summary uses configured export metrics from builder templates', function () { $user = User::factory()->create(); $report = MedicalReport::factory()->create(); $option = Department::factory()->create([ 'name' => 'ОТП', 'code' => 'otp', ]); $this->seed([ ProfileSeeder::class, HospitalUnitSeeder::class, ]); /** @var TemplateWorkbook $templateWorkbook */ $templateWorkbook = app(TemplateWorkbook::class); $department = collect($templateWorkbook->departments()) ->firstWhere('name', 'Бактериологическая лаборатория'); $this->actingAs($user); $this->put(route('medical-reports.upsert-builder', $report), [ 'department' => $department['key'], 'name' => 'КДЛ форма', 'description' => 'Конструктор', 'schema' => [ 'title' => 'КДЛ форма', 'description' => 'Конструктор', 'sections' => [ [ 'key' => 'main', 'title' => 'Основной блок', 'economist_label' => 'Бак лаб / Основной блок', 'fields' => [ ['key' => 'department_id', 'label' => 'Отделение', 'type' => 'department-select'], ['key' => 'lab_units', 'label' => 'Лаб. ед.', 'type' => 'number'], ['key' => 'research_count', 'label' => 'Исследования', 'type' => 'number'], ], 'export_metrics' => [ [ 'key' => 'lab_units_total', 'label' => 'Итого лаб. ед.', 'source_field' => 'lab_units', 'aggregation' => 'sum', ], ], 'default_entries' => [ ['department_id' => '', 'lab_units' => '0', 'research_count' => '0'], ], ], ], ], ])->assertRedirect(); /** @var StructuredTemplateRegistry $structuredTemplateRegistry */ $structuredTemplateRegistry = app(StructuredTemplateRegistry::class); $template = $structuredTemplateRegistry->templateForDepartment($department['key']); $this->put(route('medical-reports.update-structured', $report), [ 'department' => $department['key'], 'template_key' => $template['key'], 'sections' => [ 'main' => [ 'entries' => [ [ 'department_id' => $option->id, 'lab_units' => '297.0', 'research_count' => '103', ], ], ], ], ])->assertRedirect(); $economistResponse = $this->get(route('medical-reports.economists', $report)); $economistResponse->assertInertia( fn ($page) => $page ->where( 'summarySheet.columns', fn ($columns) => collect($columns)->contains( fn (array $column): bool => $column['name'] === 'Итого лаб. ед.' ) ) ->where( 'summarySheet.rows', fn ($rows) => collect($rows)->contains( fn (array $row): bool => $row['department'] === 'Бак лаб / Основной блок' && $row['values'][0]['value'] === '297' ) ) ); }); test('economist analysis matrix maps department rows through hospital unit links and blueprint export metrics', function () { $user = User::factory()->create(); $report = MedicalReport::factory()->create(); $this->seed([ ProfileSeeder::class, HospitalUnitSeeder::class, DepartmentSeeder::class, ]); /** @var TemplateWorkbook $templateWorkbook */ $templateWorkbook = app(TemplateWorkbook::class); /** @var StructuredTemplateRegistry $structuredTemplateRegistry */ $structuredTemplateRegistry = app(StructuredTemplateRegistry::class); $bacteriologyDepartment = collect($templateWorkbook->departments()) ->firstWhere('name', 'Бактериологическая лаборатория'); $physioDepartment = collect($templateWorkbook->departments()) ->firstWhere('name', 'Физиотерапевтическое отделение'); $mappedDepartment = Department::query() ->where('name', 'Гематология') ->firstOrFail(); $bacteriologyTemplate = $structuredTemplateRegistry->templateForDepartment($bacteriologyDepartment['key']); $physioTemplate = $structuredTemplateRegistry->templateForDepartment($physioDepartment['key']); $report->update([ 'input_overrides' => [ 'structured_departments' => [ $bacteriologyDepartment['key'] => [ $bacteriologyTemplate['key'] => [ 'sections' => [ 'oms_departments' => [ 'entries' => [ [ 'department_id' => (string) $mappedDepartment->id, 'lab_units' => '15', 'research_count' => '7', ], ], ], ], ], ], $physioDepartment['key'] => [ $physioTemplate['key'] => [ 'sections' => [ 'main' => [ 'entries' => [ [ 'department_id' => (string) $mappedDepartment->id, 'physio_people' => '1', 'physio_procedures' => '2', 'physio_units' => '3', 'massage_people' => '4', 'massage_procedures' => '5', 'massage_units' => '6', 'exercise_people' => '7', 'exercise_procedures' => '8', 'exercise_units' => '9', ], ], ], ], ], ], ], ], ]); $this->actingAs($user); $economistResponse = $this->get(route('medical-reports.economists', $report)); $economistResponse->assertInertia( fn ($page) => $page ->where('analysisIssues.total_count', 0) ->where( 'analysisSheet.rows', fn ($rows) => collect($rows)->contains(function (array $row): bool { if ($row['unit_name'] !== 'Гематологическое отделение') { return false; } $values = collect($row['values'])->mapWithKeys( fn (array $value): array => [$value['key'] => $value['value']] ); return $values->get('bak_lab_research') === '7' && $values->get('bak_lab_units') === '15' && $values->get('fto_procedures') === '15' && $values->get('fto_units') === '18'; }) ) ); }); test('economist page exposes unmapped department rows as analysis issues', function () { $user = User::factory()->create(); $report = MedicalReport::factory()->create(); $this->seed([ ProfileSeeder::class, HospitalUnitSeeder::class, DepartmentSeeder::class, ]); /** @var TemplateWorkbook $templateWorkbook */ $templateWorkbook = app(TemplateWorkbook::class); /** @var StructuredTemplateRegistry $structuredTemplateRegistry */ $structuredTemplateRegistry = app(StructuredTemplateRegistry::class); $bacteriologyDepartment = collect($templateWorkbook->departments()) ->firstWhere('name', 'Бактериологическая лаборатория'); $bacteriologyTemplate = $structuredTemplateRegistry->templateForDepartment($bacteriologyDepartment['key']); $unmappedDepartment = Department::query() ->where('name', 'ОТП') ->firstOrFail(); $report->update([ 'input_overrides' => [ 'structured_departments' => [ $bacteriologyDepartment['key'] => [ $bacteriologyTemplate['key'] => [ 'sections' => [ 'oms_departments' => [ 'entries' => [ [ 'department_id' => (string) $unmappedDepartment->id, 'lab_units' => '2', 'research_count' => '1', ], ], ], ], ], ], ], ], ]); $this->actingAs($user); $economistResponse = $this->get(route('medical-reports.economists', $report)); $economistResponse->assertInertia( fn ($page) => $page ->where('analysisIssues.total_count', 1) ->where( 'analysisIssues.unmapped_departments', fn ($issues) => collect($issues)->contains( fn (array $issue): bool => $issue['department'] === 'Бактериологическая лаборатория' && $issue['section'] === 'ОМС по отделениям' && $issue['source_department'] === 'ОТП' ) ) ); });