Files
econom-calculator/tests/Feature/MedicalReportTest.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

1138 lines
46 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
use App\Models\Department;
use App\Models\HospitalUnit;
use App\Models\MedicalReport;
use App\Models\MedicalReportFormTemplate;
use App\Models\User;
use App\Support\MedicalReport\StructuredTemplateRegistry;
use App\Support\MedicalReport\TemplateWorkbook;
use Database\Seeders\DepartmentSeeder;
use Database\Seeders\HospitalUnitSeeder;
use Database\Seeders\ProfileSeeder;
test('guests are redirected to the login page for medical reports', function () {
$response = $this->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'] === 'ОТП'
)
)
);
});