Тест для проверки корректности сохранения отчетов

This commit is contained in:
brusnitsyn
2026-06-11 15:30:30 +09:00
parent fe80c29ffa
commit 3a07e96e43

View File

@@ -0,0 +1,582 @@
<?php
use App\Models\ReportDuty;
use App\Models\ReportNurse;
use App\Services\DateRange;
use App\Services\DutyReportService;
use App\Services\NurseReportService;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
beforeEach(function () {
Carbon::setTestNow(Carbon::parse('2026-04-09 08:00:00', 'Asia/Yakutsk'));
foreach ([
'departments',
'users',
'report_statuses',
'report_nurses',
'report_nurse_patients',
'report_nurse_migration_patients',
'report_duties',
'report_duty_patients',
'report_duty_migration_patients',
'report_duty_reanimations',
'mv_medicalhistory_summary',
'mv_migrationpatient_details',
'mv_surgical_operations',
'mv_reanimation_summary',
'observable_medical_histories',
] as $table) {
Schema::dropIfExists($table);
}
Schema::create('departments', function (Blueprint $table) {
$table->id('department_id');
$table->string('name_full');
$table->string('name_short')->nullable();
$table->unsignedBigInteger('rf_mis_department_id')->nullable();
});
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('login')->unique();
$table->string('password');
$table->unsignedBigInteger('rf_department_id')->nullable();
$table->timestamps();
});
Schema::create('report_statuses', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('code')->unique();
$table->timestamps();
});
Schema::create('report_nurses', function (Blueprint $table) {
$table->id();
$table->date('report_date');
$table->dateTime('sent_at')->nullable();
$table->string('period_type')->default('day');
$table->dateTime('period_start')->nullable();
$table->dateTime('period_end')->nullable();
$table->unsignedBigInteger('status_id')->default(1);
$table->unsignedBigInteger('rf_lpudoctor_id')->nullable();
$table->unsignedBigInteger('rf_department_id')->default(1);
$table->unsignedBigInteger('rf_user_id')->nullable();
$table->timestamps();
});
Schema::create('report_nurse_patients', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('report_nurse_id');
$table->string('source_type')->nullable();
$table->bigInteger('original_id');
$table->string('medical_card_number')->nullable();
$table->string('full_name')->nullable();
$table->date('birth_date')->nullable();
$table->dateTime('recipient_date')->nullable();
$table->dateTime('extract_date')->nullable();
$table->dateTime('death_date')->nullable();
$table->boolean('male')->default(true);
$table->integer('urgency_id')->nullable();
$table->integer('hospital_result_id')->nullable();
$table->integer('visit_result_id')->nullable();
$table->text('comment')->nullable();
$table->unsignedBigInteger('user_id')->nullable();
$table->timestamps();
$table->unique(['report_nurse_id', 'source_type', 'original_id']);
});
Schema::create('report_nurse_migration_patients', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('medical_history_id');
$table->bigInteger('original_id')->nullable();
$table->dateTime('ingoing_date')->nullable();
$table->dateTime('out_date')->nullable();
$table->integer('diagnosis_id')->nullable();
$table->string('diagnosis_code')->nullable();
$table->string('diagnosis_name')->nullable();
$table->integer('interrupted_event_id')->nullable();
$table->integer('stationar_branch_id')->nullable();
$table->integer('department_id')->nullable();
$table->integer('visit_result_id')->nullable();
$table->integer('stat_cure_result_id')->nullable();
$table->unsignedBigInteger('user_id')->nullable();
$table->integer('mis_user_id')->nullable();
$table->text('comment')->nullable();
$table->timestamps();
$table->unique(['medical_history_id', 'ingoing_date']);
});
Schema::create('report_duties', function (Blueprint $table) {
$table->id();
$table->date('report_date');
$table->dateTime('sent_at')->nullable();
$table->string('period_type')->default('day');
$table->dateTime('period_start')->nullable();
$table->dateTime('period_end')->nullable();
$table->unsignedBigInteger('status_id')->default(1);
$table->unsignedBigInteger('rf_lpudoctor_id')->nullable();
$table->unsignedBigInteger('rf_department_id')->default(1);
$table->unsignedBigInteger('rf_user_id')->nullable();
$table->timestamps();
});
Schema::create('report_duty_patients', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('report_duty_id');
$table->string('source_type')->nullable();
$table->bigInteger('original_id');
$table->string('medical_card_number')->nullable();
$table->string('full_name')->nullable();
$table->date('birth_date')->nullable();
$table->dateTime('recipient_date')->nullable();
$table->dateTime('extract_date')->nullable();
$table->dateTime('death_date')->nullable();
$table->boolean('male')->default(true);
$table->integer('urgency_id')->nullable();
$table->integer('hospital_result_id')->nullable();
$table->integer('visit_result_id')->nullable();
$table->text('comment')->nullable();
$table->unsignedBigInteger('user_id')->nullable();
$table->timestamps();
$table->unique(['report_duty_id', 'source_type', 'original_id']);
});
Schema::create('report_duty_migration_patients', function (Blueprint $table) {
$table->id();
$table->unsignedBigInteger('medical_history_id');
$table->bigInteger('original_id')->nullable();
$table->dateTime('ingoing_date')->nullable();
$table->dateTime('out_date')->nullable();
$table->integer('diagnosis_id')->nullable();
$table->string('diagnosis_code')->nullable();
$table->string('diagnosis_name')->nullable();
$table->integer('interrupted_event_id')->nullable();
$table->integer('stationar_branch_id')->nullable();
$table->integer('department_id')->nullable();
$table->integer('visit_result_id')->nullable();
$table->integer('stat_cure_result_id')->nullable();
$table->unsignedBigInteger('user_id')->nullable();
$table->integer('mis_user_id')->nullable();
$table->text('comment')->nullable();
$table->timestamps();
$table->unique(['medical_history_id', 'ingoing_date']);
});
Schema::create('report_duty_reanimations', function (Blueprint $table) {
$table->id();
$table->bigInteger('original_id')->nullable();
$table->unsignedBigInteger('migration_patient_id');
$table->unsignedBigInteger('medical_history_id');
$table->dateTime('in_date')->nullable();
$table->dateTime('out_date')->nullable();
$table->text('description')->nullable();
$table->text('comment')->nullable();
$table->integer('stationar_branch_id')->nullable();
$table->integer('migration_stationar_branch_id')->nullable();
$table->integer('migration_department_id')->nullable();
$table->integer('doctor_id')->nullable();
$table->unsignedBigInteger('user_id')->nullable();
$table->integer('mis_user_id')->nullable();
$table->timestamps();
$table->unique(['migration_patient_id', 'in_date']);
});
// === МИС (материализованные представления) ===
Schema::create('mv_medicalhistory_summary', function (Blueprint $table) {
$table->unsignedBigInteger('id')->primary();
$table->string('medical_card_number')->nullable();
$table->string('full_name')->nullable();
$table->date('birth_date')->nullable();
$table->dateTime('recipient_date')->nullable();
$table->dateTime('extract_date')->nullable();
$table->dateTime('death_date')->nullable();
$table->boolean('male')->default(true);
$table->integer('urgency_id')->nullable();
$table->integer('hospital_result_id')->nullable();
$table->integer('visit_result_id')->nullable();
$table->text('comment')->nullable();
});
Schema::create('mv_migrationpatient_details', function (Blueprint $table) {
$table->unsignedBigInteger('id')->primary();
$table->unsignedBigInteger('medical_history_id');
$table->dateTime('ingoing_date')->nullable();
$table->dateTime('out_date')->nullable();
$table->integer('diagnosis_id')->nullable();
$table->string('diagnosis_code')->nullable();
$table->string('diagnosis_name')->nullable();
$table->integer('interrupted_event_id')->nullable();
$table->integer('stationar_branch_id')->nullable();
$table->integer('department_id')->nullable();
$table->integer('visit_result_id')->nullable();
$table->integer('stat_cure_result_id')->nullable();
$table->unsignedBigInteger('user_id')->nullable();
$table->integer('mis_user_id')->nullable();
$table->text('comment')->nullable();
});
Schema::create('mv_surgical_operations', function (Blueprint $table) {
$table->unsignedBigInteger('id')->primary();
$table->unsignedBigInteger('medical_history_id')->nullable();
$table->unsignedBigInteger('migration_patient_id')->nullable();
$table->integer('department_id')->nullable();
$table->dateTime('start_date')->nullable();
$table->dateTime('end_date')->nullable();
$table->integer('urgent_status')->nullable();
});
Schema::create('mv_reanimation_summary', function (Blueprint $table) {
$table->unsignedBigInteger('id')->primary();
$table->unsignedBigInteger('migration_patient_id')->nullable();
$table->unsignedBigInteger('medical_history_id')->nullable();
$table->dateTime('in_date')->nullable();
$table->dateTime('out_date')->nullable();
});
Schema::create('observable_medical_histories', function (Blueprint $table) {
$table->id();
$table->string('source_type')->nullable();
$table->bigInteger('original_id')->nullable();
$table->dateTime('observable_in')->nullable();
$table->dateTime('observable_out')->nullable();
$table->string('observable_reason')->nullable();
$table->string('out_reason')->nullable();
$table->string('medical_card_number')->nullable();
$table->string('full_name')->nullable();
$table->date('birth_date')->nullable();
$table->dateTime('recipient_date')->nullable();
$table->dateTime('extract_date')->nullable();
$table->dateTime('death_date')->nullable();
$table->boolean('male')->default(true);
$table->integer('urgency_id')->nullable();
$table->integer('hospital_result_id')->nullable();
$table->integer('visit_result_id')->nullable();
$table->text('comment')->nullable();
$table->unsignedBigInteger('user_id')->nullable();
$table->timestamps();
});
// Базовые справочники
DB::table('departments')->insert([
'department_id' => 1,
'name_full' => 'Тестовое отделение',
'name_short' => 'ТО',
'rf_mis_department_id' => 100,
]);
DB::table('users')->insert([
'id' => 10,
'name' => 'Дежурный врач',
'login' => 'duty.doctor',
'password' => 'secret',
'rf_department_id' => 1,
]);
DB::table('users')->insert([
'id' => 20,
'name' => 'Медсестра',
'login' => 'nurse',
'password' => 'secret',
'rf_department_id' => 1,
]);
});
afterEach(function () {
Carbon::setTestNow();
\Mockery::close();
});
function dutyDateRange(): DateRange
{
return new DateRange(
startDate: Carbon::parse('2026-04-08 09:00:00', 'Asia/Yakutsk'),
endDate: Carbon::parse('2026-04-09 09:00:00', 'Asia/Yakutsk'),
startDateRaw: '2026-04-08 09:00:00',
endDateRaw: '2026-04-09 09:00:00',
isOneDay: true,
);
}
it('сохраняет пациентов в журнале медсестры', function () {
$reportNurse = ReportNurse::create([
'report_date' => '2026-04-09',
'period_type' => 'day',
'period_start' => '2026-04-08 09:00:00',
'period_end' => '2026-04-09 09:00:00',
'status_id' => 2,
'rf_department_id' => 1,
'rf_user_id' => 20,
]);
$patient = (object) [
'source_type' => 'mis',
'original_id' => 9001,
'medical_card_number' => 'MK-9001',
'full_name' => 'Сидорова Анна Андреевна',
'birth_date' => '1990-02-02',
'recipient_date' => '2026-04-08 10:00:00',
'extract_date' => null,
'death_date' => null,
'male' => false,
'urgency_id' => 1,
'hospital_result_id' => null,
'visit_result_id' => null,
'comment' => 'тестовый пациент',
'migrations' => [
(object) [
'id' => 8801,
'ingoing_date' => '2026-04-08 10:00:00',
'out_date' => null,
'diagnosis_id' => 11,
'diagnosis_code' => 'C00',
'diagnosis_name' => 'Тестовый диагноз',
'interrupted_event_id' => null,
'stationar_branch_id' => 10,
'department_id' => 100,
'visit_result_id' => null,
'stat_cure_result_id' => null,
'user_id' => null,
'mis_user_id' => null,
'comment' => null,
],
],
];
$stats = (new NurseReportService)->saveReportSnapshot($reportNurse->id, [$patient], 20);
expect($stats['saved_patients'])->toBe(1)
->and($stats['saved_migrations'])->toBe(1);
$savedPatient = DB::table('report_nurse_patients')
->where('report_nurse_id', $reportNurse->id)
->where('source_type', 'mis')
->where('original_id', 9001)
->first();
expect($savedPatient)->not->toBeNull()
->and($savedPatient->full_name)->toBe('Сидорова Анна Андреевна')
->and($savedPatient->medical_card_number)->toBe('MK-9001')
->and($savedPatient->user_id)->toBe(20);
$savedMigration = DB::table('report_nurse_migration_patients')
->where('medical_history_id', $savedPatient->id)
->first();
expect($savedMigration)->not->toBeNull()
->and($savedMigration->original_id)->toBe(8801)
->and($savedMigration->diagnosis_code)->toBe('C00')
->and($savedMigration->department_id)->toBe(100);
});
it('сохраняет пациентов дежурного врача из МИС, если отчета медсестры за период нет', function () {
$dateRange = dutyDateRange();
DB::table('mv_medicalhistory_summary')->insert([
'id' => 501,
'medical_card_number' => 'MK-501',
'full_name' => 'Иванов Иван Иванович',
'birth_date' => '1980-01-01',
'recipient_date' => '2026-04-08 10:00:00',
'extract_date' => null,
'death_date' => null,
'male' => true,
'urgency_id' => 2,
'hospital_result_id' => null,
'visit_result_id' => null,
'comment' => null,
]);
DB::table('mv_migrationpatient_details')->insert([
'id' => 601,
'medical_history_id' => 501,
'ingoing_date' => '2026-04-08 10:00:00',
'out_date' => null,
'diagnosis_id' => 10,
'diagnosis_code' => 'A00',
'diagnosis_name' => 'Диагноз из МИС',
'interrupted_event_id' => null,
'stationar_branch_id' => 10,
'department_id' => 100,
'visit_result_id' => null,
'stat_cure_result_id' => null,
'user_id' => null,
'mis_user_id' => null,
'comment' => null,
]);
$reportDuty = ReportDuty::create([
'report_date' => '2026-04-09',
'period_type' => 'day',
'period_start' => $dateRange->startSql(),
'period_end' => $dateRange->endSql(),
'status_id' => 2,
'rf_department_id' => 1,
'rf_user_id' => 10,
]);
$service = app(DutyReportService::class);
$stats = $service->saveSnapshot($dateRange, $reportDuty, 100, 10);
expect($stats['saved_patients'])->toBe(1)
->and($stats['saved_migrations'])->toBe(1);
$savedPatient = DB::table('report_duty_patients')
->where('report_duty_id', $reportDuty->id)
->where('source_type', 'mis')
->where('original_id', 501)
->first();
expect($savedPatient)->not->toBeNull()
->and($savedPatient->full_name)->toBe('Иванов Иван Иванович')
->and($savedPatient->medical_card_number)->toBe('MK-501');
$savedMigration = DB::table('report_duty_migration_patients')
->where('medical_history_id', $savedPatient->id)
->where('original_id', 601)
->first();
expect($savedMigration)->not->toBeNull()
->and($savedMigration->diagnosis_code)->toBe('A00')
->and($savedMigration->department_id)->toBe(100);
});
it('сохраняет пациентов дежурного врача из отчета медсестры, если он есть за период', function () {
$dateRange = dutyDateRange();
// "Чужие" данные из МИС - не должны попасть в отчет дежурного врача,
// т.к. за период есть отчет медсестры
DB::table('mv_medicalhistory_summary')->insert([
'id' => 999,
'medical_card_number' => 'MK-999',
'full_name' => 'Не должен попасть в отчет',
'birth_date' => '1970-01-01',
'recipient_date' => '2026-04-08 10:00:00',
'extract_date' => null,
'death_date' => null,
'male' => true,
'urgency_id' => 1,
'hospital_result_id' => null,
'visit_result_id' => null,
'comment' => null,
]);
DB::table('mv_migrationpatient_details')->insert([
'id' => 998,
'medical_history_id' => 999,
'ingoing_date' => '2026-04-08 10:00:00',
'out_date' => null,
'diagnosis_id' => 90,
'diagnosis_code' => 'X00',
'diagnosis_name' => 'МИС диагноз',
'interrupted_event_id' => null,
'stationar_branch_id' => 10,
'department_id' => 100,
'visit_result_id' => null,
'stat_cure_result_id' => null,
'user_id' => null,
'mis_user_id' => null,
'comment' => null,
]);
$reportNurse = ReportNurse::create([
'report_date' => '2026-04-09',
'period_type' => 'day',
'period_start' => $dateRange->startSql(),
'period_end' => $dateRange->endSql(),
'status_id' => 2,
'rf_department_id' => 1,
'rf_user_id' => 20,
]);
$nursePatientId = DB::table('report_nurse_patients')->insertGetId([
'report_nurse_id' => $reportNurse->id,
'source_type' => 'mis',
'original_id' => 701,
'medical_card_number' => 'MK-701',
'full_name' => 'Петров Петр Петрович',
'birth_date' => '1975-05-05',
'recipient_date' => '2026-04-08 11:00:00',
'extract_date' => null,
'death_date' => null,
'male' => true,
'urgency_id' => 1,
'hospital_result_id' => null,
'visit_result_id' => null,
'comment' => null,
'user_id' => 20,
]);
$nurseMigrationId = DB::table('report_nurse_migration_patients')->insertGetId([
'medical_history_id' => $nursePatientId,
'original_id' => 5555, // MigrationPatientID из МИС
'ingoing_date' => '2026-04-08 11:00:00',
'out_date' => null,
'diagnosis_id' => 20,
'diagnosis_code' => 'B00',
'diagnosis_name' => 'Диагноз медсестры',
'interrupted_event_id' => null,
'stationar_branch_id' => 10,
'department_id' => 100,
'visit_result_id' => null,
'stat_cure_result_id' => null,
'user_id' => null,
'mis_user_id' => null,
'comment' => null,
]);
$reportDuty = ReportDuty::create([
'report_date' => '2026-04-09',
'period_type' => 'day',
'period_start' => $dateRange->startSql(),
'period_end' => $dateRange->endSql(),
'status_id' => 2,
'rf_department_id' => 1,
'rf_user_id' => 10,
]);
$service = app(DutyReportService::class);
$stats = $service->saveSnapshot($dateRange, $reportDuty, 100, 10);
expect($stats['saved_patients'])->toBe(1)
->and($stats['saved_migrations'])->toBe(1);
// Пациент из отчета медсестры сохранен
$savedPatient = DB::table('report_duty_patients')
->where('report_duty_id', $reportDuty->id)
->where('original_id', 701)
->first();
expect($savedPatient)->not->toBeNull()
->and($savedPatient->source_type)->toBe('mis')
->and($savedPatient->full_name)->toBe('Петров Петр Петрович')
->and($savedPatient->medical_card_number)->toBe('MK-701');
$savedMigration = DB::table('report_duty_migration_patients')
->where('medical_history_id', $savedPatient->id)
->first();
expect($savedMigration)->not->toBeNull()
->and($savedMigration->original_id)->toBe(5555)
->and($savedMigration->original_id)->not->toBe($nurseMigrationId)
->and($savedMigration->diagnosis_code)->toBe('B00')
->and($savedMigration->department_id)->toBe(100);
// Пациент из МИС не должен попасть в отчет
$misPatient = DB::table('report_duty_patients')
->where('report_duty_id', $reportDuty->id)
->where('original_id', 999)
->first();
expect($misPatient)->toBeNull();
});