Удалил неактуальные тесты
This commit is contained in:
@@ -1,467 +0,0 @@
|
||||
<?php
|
||||
|
||||
use App\Console\Commands\FillReportsFromDate;
|
||||
use App\Application\Reports\ReportSavePathService;
|
||||
use App\Models\Department;
|
||||
use App\Models\User;
|
||||
use App\Services\AutoReportService;
|
||||
use App\Services\DateRange;
|
||||
use App\Services\DateRangeService;
|
||||
use App\Services\PatientService;
|
||||
use App\Services\ReportService;
|
||||
use App\Services\SnapshotService;
|
||||
use App\Services\UnifiedPatientService;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Symfony\Component\Console\Tester\CommandTester;
|
||||
|
||||
beforeEach(function () {
|
||||
Carbon::setTestNow(Carbon::parse('2026-04-09 08:00:00', 'Asia/Yakutsk'));
|
||||
|
||||
foreach ([
|
||||
'departments',
|
||||
'roles',
|
||||
'users',
|
||||
'user_roles',
|
||||
'user_departments',
|
||||
'reports',
|
||||
'metrika_results',
|
||||
'medical_history_snapshots',
|
||||
'department_patients',
|
||||
'unwanted_events',
|
||||
'observation_patients',
|
||||
'stt_stationarbranch',
|
||||
] as $table) {
|
||||
Schema::dropIfExists($table);
|
||||
}
|
||||
|
||||
Schema::create('departments', function (Blueprint $table) {
|
||||
$table->id('department_id');
|
||||
$table->string('name_short')->nullable();
|
||||
$table->integer('rf_mis_department_id')->nullable();
|
||||
$table->integer('rf_department_type')->nullable();
|
||||
});
|
||||
|
||||
Schema::create('roles', function (Blueprint $table) {
|
||||
$table->id('role_id');
|
||||
$table->string('name')->nullable();
|
||||
$table->string('slug');
|
||||
$table->boolean('is_active')->default(true);
|
||||
});
|
||||
|
||||
Schema::create('users', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('login')->unique();
|
||||
$table->string('password');
|
||||
$table->boolean('is_active')->default(true);
|
||||
$table->unsignedBigInteger('rf_lpudoctor_id')->nullable();
|
||||
$table->unsignedBigInteger('rf_department_id')->nullable();
|
||||
$table->unsignedBigInteger('current_role_id')->nullable();
|
||||
$table->rememberToken()->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('user_roles', function (Blueprint $table) {
|
||||
$table->id('user_role_id');
|
||||
$table->unsignedBigInteger('rf_user_id');
|
||||
$table->unsignedBigInteger('rf_role_id');
|
||||
$table->boolean('is_active')->default(true);
|
||||
$table->boolean('is_default')->default(false);
|
||||
});
|
||||
|
||||
Schema::create('user_departments', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('rf_user_id');
|
||||
$table->unsignedBigInteger('rf_department_id');
|
||||
$table->boolean('is_favorite')->default(false);
|
||||
$table->integer('order')->default(0);
|
||||
$table->string('user_name')->nullable();
|
||||
});
|
||||
|
||||
Schema::create('reports', function (Blueprint $table) {
|
||||
$table->id('report_id');
|
||||
$table->date('created_at');
|
||||
$table->dateTime('sent_at')->nullable();
|
||||
$table->unsignedBigInteger('rf_department_id');
|
||||
$table->unsignedBigInteger('rf_user_id')->nullable();
|
||||
$table->unsignedBigInteger('rf_lpudoctor_id')->nullable();
|
||||
});
|
||||
|
||||
Schema::create('metrika_results', function (Blueprint $table) {
|
||||
$table->id('metrika_result_id');
|
||||
$table->unsignedBigInteger('rf_report_id');
|
||||
$table->unsignedBigInteger('rf_metrika_item_id');
|
||||
$table->string('value')->nullable();
|
||||
});
|
||||
|
||||
Schema::create('medical_history_snapshots', function (Blueprint $table) {
|
||||
$table->id('medical_history_snapshot_id');
|
||||
$table->unsignedBigInteger('rf_report_id');
|
||||
$table->unsignedBigInteger('rf_medicalhistory_id')->nullable();
|
||||
$table->unsignedBigInteger('rf_department_patient_id')->nullable();
|
||||
$table->string('patient_type');
|
||||
$table->string('patient_uid')->nullable();
|
||||
$table->string('patient_source_type')->nullable();
|
||||
$table->string('patient_kind')->nullable();
|
||||
$table->string('full_name')->nullable();
|
||||
$table->date('birth_date')->nullable();
|
||||
$table->string('diagnosis_code')->nullable();
|
||||
$table->string('diagnosis_name')->nullable();
|
||||
$table->dateTime('admitted_at')->nullable();
|
||||
$table->string('outcome_type')->nullable();
|
||||
$table->dateTime('outcome_at')->nullable();
|
||||
$table->boolean('is_manual')->default(false);
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('department_patients', function (Blueprint $table) {
|
||||
$table->id('department_patient_id');
|
||||
$table->unsignedBigInteger('rf_department_id');
|
||||
$table->string('source_type')->default('manual');
|
||||
$table->unsignedBigInteger('rf_medicalhistory_id')->nullable();
|
||||
$table->string('full_name');
|
||||
$table->date('birth_date')->nullable();
|
||||
$table->string('patient_kind')->nullable();
|
||||
$table->string('diagnosis_code')->nullable();
|
||||
$table->string('diagnosis_name')->nullable();
|
||||
$table->dateTime('admitted_at')->nullable();
|
||||
$table->boolean('is_current')->default(true);
|
||||
$table->string('outcome_type')->nullable();
|
||||
$table->dateTime('outcome_at')->nullable();
|
||||
$table->unsignedBigInteger('created_by')->nullable();
|
||||
$table->dateTime('linked_to_mis_at')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('unwanted_events', function (Blueprint $table) {
|
||||
$table->id('unwanted_event_id');
|
||||
$table->unsignedBigInteger('rf_report_id')->nullable();
|
||||
});
|
||||
|
||||
Schema::create('observation_patients', function (Blueprint $table) {
|
||||
$table->id('observation_patient_id');
|
||||
$table->unsignedBigInteger('rf_report_id')->nullable();
|
||||
$table->unsignedBigInteger('rf_department_id')->nullable();
|
||||
$table->unsignedBigInteger('rf_medicalhistory_id')->nullable();
|
||||
$table->unsignedBigInteger('rf_department_patient_id')->nullable();
|
||||
});
|
||||
|
||||
Schema::create('stt_stationarbranch', function (Blueprint $table) {
|
||||
$table->integer('StationarBranchID')->primary();
|
||||
$table->integer('rf_DepartmentID');
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
Carbon::setTestNow();
|
||||
\Mockery::close();
|
||||
});
|
||||
|
||||
function autoFillRange(): DateRange
|
||||
{
|
||||
return new DateRange(
|
||||
startDate: Carbon::parse('2026-04-08 06:00:00', 'Asia/Yakutsk'),
|
||||
endDate: Carbon::parse('2026-04-09 06:00:00', 'Asia/Yakutsk'),
|
||||
startDateRaw: '2026-04-08 06:00:00',
|
||||
endDateRaw: '2026-04-09 06:00:00',
|
||||
isOneDay: true,
|
||||
);
|
||||
}
|
||||
|
||||
it('builds auto fill payload from the same patient metrics that are stored in reports', function () {
|
||||
DB::table('stt_stationarbranch')->insert([
|
||||
'StationarBranchID' => 10,
|
||||
'rf_DepartmentID' => 100,
|
||||
]);
|
||||
|
||||
$department = new Department;
|
||||
$department->department_id = 10;
|
||||
$department->rf_mis_department_id = 100;
|
||||
|
||||
$user = new class extends User
|
||||
{
|
||||
public function isHeadOfDepartment()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function isAdmin()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
$user->id = 15;
|
||||
$user->rf_lpudoctor_id = 5015;
|
||||
|
||||
$patientService = \Mockery::mock(PatientService::class);
|
||||
$unifiedPatientService = \Mockery::mock(UnifiedPatientService::class);
|
||||
$unifiedPatientService
|
||||
->shouldReceive('getLivePatientCountByStatus')
|
||||
->once()->with($department, $user, 'plan', \Mockery::type(DateRange::class), 10, true)->andReturn(11);
|
||||
$unifiedPatientService
|
||||
->shouldReceive('getLivePatientCountByStatus')
|
||||
->once()->with($department, $user, 'emergency', \Mockery::type(DateRange::class), 10, true)->andReturn(7);
|
||||
$unifiedPatientService
|
||||
->shouldReceive('getLivePatientCountByStatus')
|
||||
->once()->with($department, $user, 'recipient', \Mockery::type(DateRange::class), 10)->andReturn(4);
|
||||
$unifiedPatientService
|
||||
->shouldReceive('getLivePatientCountByStatus')
|
||||
->once()->with($department, $user, 'outcome-discharged', \Mockery::type(DateRange::class), 10)->andReturn(3);
|
||||
$unifiedPatientService
|
||||
->shouldReceive('getLivePatientCountByStatus')
|
||||
->once()->with($department, $user, 'outcome-transferred', \Mockery::type(DateRange::class), 10)->andReturn(2);
|
||||
$unifiedPatientService
|
||||
->shouldReceive('getLivePatientCountByStatus')
|
||||
->once()->with($department, $user, 'outcome-deceased', \Mockery::type(DateRange::class), 10)->andReturn(1);
|
||||
$unifiedPatientService
|
||||
->shouldReceive('getLivePatientCountByStatus')
|
||||
->once()->with($department, $user, 'current', \Mockery::type(DateRange::class), 10)->andReturn(21);
|
||||
$patientService
|
||||
->shouldReceive('getSurgicalPatients')
|
||||
->once()->with('plan', 10, \Mockery::type(DateRange::class), true)->andReturn(8);
|
||||
$patientService
|
||||
->shouldReceive('getSurgicalPatients')
|
||||
->once()->with('emergency', 10, \Mockery::type(DateRange::class), true)->andReturn(5);
|
||||
|
||||
$service = new ReportService(
|
||||
app(DateRangeService::class),
|
||||
$unifiedPatientService,
|
||||
$patientService,
|
||||
\Mockery::mock(SnapshotService::class),
|
||||
\Mockery::mock(\App\Services\StatisticsService::class)
|
||||
);
|
||||
|
||||
$payload = $service->buildAutoFillReportPayload($user, $department, autoFillRange());
|
||||
|
||||
expect($payload['departmentId'])->toBe(10)
|
||||
->and($payload['userId'])->toBe(5015)
|
||||
->and($payload['metrics']['metrika_item_4'])->toBe(11)
|
||||
->and($payload['metrics']['metrika_item_12'])->toBe(7)
|
||||
->and($payload['metrics']['metrika_item_13'])->toBe(2)
|
||||
->and($payload['metrics']['metrika_item_8'])->toBe(21)
|
||||
->and($payload['metrics']['metrika_item_7'])->toBe(4);
|
||||
});
|
||||
|
||||
it('creates auto-filled report through report service with auto flag and scoped department user', function () {
|
||||
$department = new Department;
|
||||
$department->department_id = 10;
|
||||
$department->rf_mis_department_id = 100;
|
||||
|
||||
$user = new User;
|
||||
$user->id = 15;
|
||||
$user->rf_lpudoctor_id = 5015;
|
||||
$user->rf_department_id = 999;
|
||||
|
||||
$payload = ['departmentId' => 10, 'metrics' => ['metrika_item_4' => 11], 'dates' => [1, 2]];
|
||||
|
||||
$reportService = \Mockery::mock(ReportService::class);
|
||||
$reportService
|
||||
->shouldReceive('buildAutoFillReportPayload')
|
||||
->once()
|
||||
->withArgs(function (User $scopedUser, Department $argDepartment, DateRange $dateRange) use ($department) {
|
||||
return $argDepartment->department_id === $department->department_id
|
||||
&& $dateRange->endSql() === '2026-04-09 06:00:00'
|
||||
&& $scopedUser !== null
|
||||
&& $scopedUser->rf_department_id === 10
|
||||
&& $scopedUser->department->department_id === 10;
|
||||
})
|
||||
->andReturn($payload);
|
||||
$reportService
|
||||
->shouldReceive('storeReport')
|
||||
->once()
|
||||
->withArgs(function (array $data, User $scopedUser, bool $fillableAuto) use ($payload) {
|
||||
return $data === $payload
|
||||
&& $fillableAuto === true
|
||||
&& $scopedUser->rf_department_id === 10
|
||||
&& $scopedUser->department->department_id === 10;
|
||||
})
|
||||
->andReturn(new \App\Models\Report);
|
||||
|
||||
$service = new AutoReportService(
|
||||
$reportService,
|
||||
app(DateRangeService::class),
|
||||
\Mockery::mock(ReportSavePathService::class),
|
||||
);
|
||||
|
||||
expect($service->createReportForDate($user, $department, autoFillRange(), false))->toBeTrue();
|
||||
});
|
||||
|
||||
it('force recreation removes previous report scoped data before storing a new auto-filled report', function () {
|
||||
$department = new Department;
|
||||
$department->department_id = 10;
|
||||
$department->rf_mis_department_id = 100;
|
||||
|
||||
$user = new User;
|
||||
$user->id = 15;
|
||||
$user->rf_lpudoctor_id = 5015;
|
||||
$user->rf_department_id = 10;
|
||||
|
||||
DB::table('reports')->insert([
|
||||
'report_id' => 55,
|
||||
'created_at' => '2026-04-09',
|
||||
'sent_at' => '2026-04-09 06:00:00',
|
||||
'rf_department_id' => 10,
|
||||
'rf_user_id' => 15,
|
||||
'rf_lpudoctor_id' => 5015,
|
||||
]);
|
||||
|
||||
DB::table('metrika_results')->insert([
|
||||
'rf_report_id' => 55,
|
||||
'rf_metrika_item_id' => 4,
|
||||
'value' => '99',
|
||||
]);
|
||||
|
||||
DB::table('medical_history_snapshots')->insert([
|
||||
'rf_report_id' => 55,
|
||||
'rf_medicalhistory_id' => 100,
|
||||
'patient_type' => 'plan',
|
||||
'patient_uid' => 'mis:100',
|
||||
'patient_source_type' => 'mis',
|
||||
'patient_kind' => 'plan',
|
||||
'full_name' => 'Old Snapshot',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
DB::table('unwanted_events')->insert([
|
||||
'rf_report_id' => 55,
|
||||
]);
|
||||
|
||||
DB::table('observation_patients')->insert([
|
||||
'rf_report_id' => 55,
|
||||
'rf_department_id' => 10,
|
||||
'rf_medicalhistory_id' => 100,
|
||||
]);
|
||||
|
||||
$payload = ['departmentId' => 10, 'metrics' => ['metrika_item_4' => 11], 'dates' => [1, 2]];
|
||||
|
||||
$reportService = \Mockery::mock(ReportService::class);
|
||||
$reportService
|
||||
->shouldReceive('buildAutoFillReportPayload')
|
||||
->once()
|
||||
->andReturn($payload);
|
||||
$reportService
|
||||
->shouldReceive('storeReport')
|
||||
->once()
|
||||
->andReturn(new \App\Models\Report);
|
||||
|
||||
$service = new AutoReportService(
|
||||
$reportService,
|
||||
app(DateRangeService::class),
|
||||
\Mockery::mock(ReportSavePathService::class),
|
||||
);
|
||||
|
||||
expect($service->createReportForDate($user, $department, autoFillRange(), true))->toBeTrue()
|
||||
->and(DB::table('reports')->where('report_id', 55)->exists())->toBeFalse()
|
||||
->and(DB::table('metrika_results')->where('rf_report_id', 55)->exists())->toBeFalse()
|
||||
->and(DB::table('medical_history_snapshots')->where('rf_report_id', 55)->exists())->toBeFalse()
|
||||
->and(DB::table('unwanted_events')->where('rf_report_id', 55)->exists())->toBeFalse()
|
||||
->and(DB::table('observation_patients')->where('rf_report_id', 55)->exists())->toBeFalse();
|
||||
});
|
||||
|
||||
it('reports fill command chooses doctor by default and honors explicit user option', function () {
|
||||
DB::table('departments')->insert([
|
||||
'department_id' => 10,
|
||||
'name_short' => 'Отд. 10',
|
||||
'rf_mis_department_id' => 100,
|
||||
]);
|
||||
|
||||
DB::table('roles')->insert([
|
||||
['role_id' => 1, 'name' => 'Doctor', 'slug' => 'doctor', 'is_active' => true],
|
||||
['role_id' => 2, 'name' => 'Head', 'slug' => 'head_of_department', 'is_active' => true],
|
||||
]);
|
||||
|
||||
DB::table('users')->insert([
|
||||
[
|
||||
'id' => 101,
|
||||
'name' => 'Doctor A',
|
||||
'login' => 'doc-a',
|
||||
'password' => 'secret',
|
||||
'is_active' => true,
|
||||
'rf_lpudoctor_id' => 1001,
|
||||
'rf_department_id' => 10,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
],
|
||||
[
|
||||
'id' => 102,
|
||||
'name' => 'Head B',
|
||||
'login' => 'head-b',
|
||||
'password' => 'secret',
|
||||
'is_active' => true,
|
||||
'rf_lpudoctor_id' => 1002,
|
||||
'rf_department_id' => 10,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
],
|
||||
[
|
||||
'id' => 103,
|
||||
'name' => 'Doctor C',
|
||||
'login' => 'doc-c',
|
||||
'password' => 'secret',
|
||||
'is_active' => true,
|
||||
'rf_lpudoctor_id' => 1003,
|
||||
'rf_department_id' => 10,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
],
|
||||
]);
|
||||
|
||||
DB::table('user_roles')->insert([
|
||||
['user_role_id' => 1, 'rf_user_id' => 101, 'rf_role_id' => 1, 'is_active' => true, 'is_default' => true],
|
||||
['user_role_id' => 2, 'rf_user_id' => 102, 'rf_role_id' => 2, 'is_active' => true, 'is_default' => true],
|
||||
['user_role_id' => 3, 'rf_user_id' => 103, 'rf_role_id' => 1, 'is_active' => true, 'is_default' => true],
|
||||
]);
|
||||
|
||||
DB::table('user_departments')->insert([
|
||||
['id' => 1, 'rf_user_id' => 101, 'rf_department_id' => 10, 'is_favorite' => true, 'order' => 2, 'user_name' => 'Doctor A'],
|
||||
['id' => 2, 'rf_user_id' => 102, 'rf_department_id' => 10, 'is_favorite' => true, 'order' => 1, 'user_name' => 'Head B'],
|
||||
['id' => 3, 'rf_user_id' => 103, 'rf_department_id' => 10, 'is_favorite' => false, 'order' => 1, 'user_name' => 'Doctor C'],
|
||||
]);
|
||||
|
||||
$autoReportService = \Mockery::mock(AutoReportService::class);
|
||||
$autoReportService
|
||||
->shouldReceive('fillReportsForUser')
|
||||
->once()
|
||||
->withArgs(function (User $user, string $startDate, string $endDate, Department $department, bool $force) {
|
||||
return $user->id === 101
|
||||
&& $startDate === '2026-01-01'
|
||||
&& $endDate === '2026-01-02'
|
||||
&& $department->department_id === 10
|
||||
&& $force === false;
|
||||
})
|
||||
->andReturn(2);
|
||||
$autoReportService
|
||||
->shouldReceive('fillReportsForUser')
|
||||
->once()
|
||||
->withArgs(function (User $user, string $startDate, string $endDate, Department $department, bool $force) {
|
||||
return $user->id === 103
|
||||
&& $startDate === '2026-01-01'
|
||||
&& $endDate === '2026-01-02'
|
||||
&& $department->department_id === 10
|
||||
&& $force === false;
|
||||
})
|
||||
->andReturn(2);
|
||||
|
||||
app()->instance(AutoReportService::class, $autoReportService);
|
||||
|
||||
$command = app(FillReportsFromDate::class);
|
||||
$command->setLaravel(app());
|
||||
$tester = new CommandTester($command);
|
||||
|
||||
expect($tester->execute([
|
||||
'--date' => '2026-01-01',
|
||||
'--end-date' => '2026-01-02',
|
||||
'--department' => 10,
|
||||
]))->toBe(0);
|
||||
|
||||
expect($tester->execute([
|
||||
'--date' => '2026-01-01',
|
||||
'--end-date' => '2026-01-02',
|
||||
'--department' => 10,
|
||||
'--user' => 103,
|
||||
]))->toBe(0);
|
||||
});
|
||||
@@ -1,7 +0,0 @@
|
||||
<?php
|
||||
|
||||
it('returns a successful response', function () {
|
||||
$response = $this->get('/');
|
||||
|
||||
$response->assertStatus(200);
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,150 +0,0 @@
|
||||
<?php
|
||||
|
||||
use App\Domain\Reports\Models\ReportSnapshot;
|
||||
use App\Infrastructure\Reports\Adapters\LegacyReportServiceAdapter;
|
||||
use App\Infrastructure\Reports\Repositories\EloquentReportRepository;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
beforeEach(function () {
|
||||
foreach ([
|
||||
'users',
|
||||
'departments',
|
||||
'department_metrika_defaults',
|
||||
'reports',
|
||||
'metrika_results',
|
||||
'observation_patients',
|
||||
'unwanted_events',
|
||||
] as $table) {
|
||||
Schema::dropIfExists($table);
|
||||
}
|
||||
|
||||
Schema::create('users', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name')->nullable();
|
||||
$table->string('login')->nullable();
|
||||
$table->string('password')->nullable();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
Schema::create('departments', function (Blueprint $table) {
|
||||
$table->id('department_id');
|
||||
$table->string('name_full')->nullable();
|
||||
$table->string('name_short')->nullable();
|
||||
$table->integer('rf_mis_department_id')->nullable();
|
||||
$table->integer('rf_department_type')->nullable();
|
||||
});
|
||||
|
||||
Schema::create('department_metrika_defaults', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('rf_department_id');
|
||||
$table->unsignedBigInteger('rf_metrika_item_id');
|
||||
$table->string('value')->nullable();
|
||||
});
|
||||
|
||||
Schema::create('reports', function (Blueprint $table) {
|
||||
$table->id('report_id');
|
||||
$table->dateTime('created_at');
|
||||
$table->dateTime('sent_at')->nullable();
|
||||
$table->unsignedBigInteger('rf_department_id');
|
||||
$table->unsignedBigInteger('rf_user_id')->nullable();
|
||||
$table->unsignedBigInteger('rf_lpudoctor_id')->nullable();
|
||||
$table->dateTime('period_start')->nullable();
|
||||
$table->dateTime('period_end')->nullable();
|
||||
$table->string('status')->default('draft');
|
||||
});
|
||||
|
||||
Schema::create('metrika_results', function (Blueprint $table) {
|
||||
$table->id('metrika_result_id');
|
||||
$table->unsignedBigInteger('rf_report_id');
|
||||
$table->unsignedBigInteger('rf_metrika_item_id');
|
||||
$table->string('value')->nullable();
|
||||
});
|
||||
|
||||
Schema::create('observation_patients', function (Blueprint $table) {
|
||||
$table->id('observation_patient_id');
|
||||
$table->unsignedBigInteger('rf_report_id')->nullable();
|
||||
$table->unsignedBigInteger('rf_department_id')->nullable();
|
||||
$table->unsignedBigInteger('rf_medicalhistory_id')->nullable();
|
||||
$table->unsignedBigInteger('rf_department_patient_id')->nullable();
|
||||
$table->text('comment')->nullable();
|
||||
});
|
||||
|
||||
Schema::create('unwanted_events', function (Blueprint $table) {
|
||||
$table->id('unwanted_event_id');
|
||||
$table->unsignedBigInteger('rf_report_id')->nullable();
|
||||
$table->text('comment')->nullable();
|
||||
$table->string('title')->nullable();
|
||||
$table->boolean('is_visible')->default(true);
|
||||
$table->timestamps();
|
||||
});
|
||||
|
||||
DB::table('users')->insert([
|
||||
'id' => 15,
|
||||
'name' => 'Doctor',
|
||||
'login' => 'doc',
|
||||
'password' => 'secret',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
DB::table('departments')->insert([
|
||||
'department_id' => 10,
|
||||
'name_full' => 'Department',
|
||||
'name_short' => 'Dept',
|
||||
'rf_mis_department_id' => 100,
|
||||
]);
|
||||
|
||||
DB::table('department_metrika_defaults')->insert([
|
||||
'rf_department_id' => 10,
|
||||
'rf_metrika_item_id' => 1,
|
||||
'value' => '30',
|
||||
]);
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
\Mockery::close();
|
||||
});
|
||||
|
||||
it('saves report snapshot idempotently through eloquent repository', function () {
|
||||
$adapter = \Mockery::mock(LegacyReportServiceAdapter::class);
|
||||
$adapter->shouldReceive('prepareMemoryForHeavySave')->twice();
|
||||
$adapter->shouldReceive('createPatientSnapshots')->twice();
|
||||
$adapter->shouldReceive('syncCalculatedMetrics')->twice();
|
||||
$adapter->shouldReceive('saveLethalMetricFromSnapshots')->twice();
|
||||
$adapter->shouldReceive('clearCacheAfterReportCreation')->twice();
|
||||
|
||||
$repository = new EloquentReportRepository($adapter);
|
||||
|
||||
$snapshot = new ReportSnapshot(
|
||||
departmentId: 10,
|
||||
userId: 5015,
|
||||
actorUserId: 15,
|
||||
periodStart: new DateTimeImmutable('2026-04-08 06:00:00'),
|
||||
periodEnd: new DateTimeImmutable('2026-04-09 06:00:00'),
|
||||
status: 'draft',
|
||||
metrics: [4 => 11],
|
||||
observationPatients: [['medical_history_id' => 100, 'comment' => 'watch']],
|
||||
unwantedEvents: [['title' => 'event', 'comment' => 'test', 'is_visible' => true]],
|
||||
);
|
||||
|
||||
$first = $repository->save($snapshot);
|
||||
$second = $repository->save(new ReportSnapshot(
|
||||
departmentId: 10,
|
||||
userId: 5015,
|
||||
actorUserId: 15,
|
||||
periodStart: new DateTimeImmutable('2026-04-08 06:00:00'),
|
||||
periodEnd: new DateTimeImmutable('2026-04-09 06:00:00'),
|
||||
status: 'draft',
|
||||
metrics: [4 => 12],
|
||||
observationPatients: [['medical_history_id' => 100, 'comment' => 'watch-2']],
|
||||
unwantedEvents: [['title' => 'event-2', 'comment' => 'test-2', 'is_visible' => true]],
|
||||
reportId: $first->reportId,
|
||||
));
|
||||
|
||||
expect($first->reportId)->toBe($second->reportId)
|
||||
->and(DB::table('reports')->count())->toBe(1)
|
||||
->and(DB::table('metrika_results')->where('rf_report_id', $first->reportId)->where('rf_metrika_item_id', 4)->value('value'))->toBe('12')
|
||||
->and(DB::table('observation_patients')->where('rf_report_id', $first->reportId)->value('comment'))->toBe('watch-2');
|
||||
});
|
||||
@@ -1,5 +0,0 @@
|
||||
<?php
|
||||
|
||||
test('that true is true', function () {
|
||||
expect(true)->toBeTrue();
|
||||
});
|
||||
@@ -1,38 +0,0 @@
|
||||
<?php
|
||||
|
||||
use App\Domain\Reports\Calculators\BedDaysCalculator;
|
||||
use App\Domain\Reports\Models\StayInterval;
|
||||
|
||||
it('calculates total and average bed days', function () {
|
||||
$calculator = new BedDaysCalculator;
|
||||
|
||||
$result = $calculator->calculate([
|
||||
new StayInterval(
|
||||
startAt: new DateTimeImmutable('2026-04-01 10:00:00'),
|
||||
endAt: new DateTimeImmutable('2026-04-04 09:00:00'),
|
||||
),
|
||||
new StayInterval(
|
||||
startAt: new DateTimeImmutable('2026-04-05 10:00:00'),
|
||||
endAt: new DateTimeImmutable('2026-04-07 09:00:00'),
|
||||
),
|
||||
]);
|
||||
|
||||
expect($result->total)->toBe(5)
|
||||
->and($result->count)->toBe(2)
|
||||
->and($result->average)->toBe(2.5);
|
||||
});
|
||||
|
||||
it('ignores invalid bed day intervals', function () {
|
||||
$calculator = new BedDaysCalculator;
|
||||
|
||||
$result = $calculator->calculate([
|
||||
new StayInterval(
|
||||
startAt: new DateTimeImmutable('2026-04-04 10:00:00'),
|
||||
endAt: new DateTimeImmutable('2026-04-01 09:00:00'),
|
||||
),
|
||||
]);
|
||||
|
||||
expect($result->total)->toBe(0)
|
||||
->and($result->count)->toBe(0)
|
||||
->and($result->average)->toBe(0.0);
|
||||
});
|
||||
@@ -1,47 +0,0 @@
|
||||
<?php
|
||||
|
||||
use App\Application\Reports\CompareLegacyAndNewReportUseCase;
|
||||
use App\Application\Reports\DTO\GenerateReportInput;
|
||||
use App\Domain\Reports\Contracts\ReportRepository;
|
||||
use App\Domain\Reports\Models\ReportSnapshot;
|
||||
use App\Infrastructure\Reports\Adapters\LegacyReportServiceAdapter;
|
||||
|
||||
it('returns matched when saved snapshot equals expected snapshot', function () {
|
||||
$input = new GenerateReportInput(
|
||||
departmentId: 10,
|
||||
userId: 5015,
|
||||
actorUserId: 15,
|
||||
periodStart: new DateTimeImmutable('2026-04-08 06:00:00'),
|
||||
periodEnd: new DateTimeImmutable('2026-04-09 06:00:00'),
|
||||
reportType: 'daily',
|
||||
rawPayload: [
|
||||
'departmentId' => 10,
|
||||
'userId' => 5015,
|
||||
'dates' => [1744063200, 1744149600],
|
||||
'metrics' => ['metrika_item_4' => 11],
|
||||
'observationPatients' => [],
|
||||
'unwantedEvents' => [],
|
||||
],
|
||||
persistedReportId: 55,
|
||||
);
|
||||
|
||||
$snapshot = new ReportSnapshot(
|
||||
departmentId: 10,
|
||||
userId: 5015,
|
||||
actorUserId: 15,
|
||||
periodStart: new DateTimeImmutable('2026-04-08 06:00:00'),
|
||||
periodEnd: new DateTimeImmutable('2026-04-09 06:00:00'),
|
||||
metrics: [4 => 11],
|
||||
);
|
||||
|
||||
$repository = \Mockery::mock(ReportRepository::class);
|
||||
$repository->shouldReceive('findSnapshot')->once()->with(55)->andReturn($snapshot);
|
||||
|
||||
$adapter = \Mockery::mock(LegacyReportServiceAdapter::class);
|
||||
$adapter->shouldReceive('buildSnapshotFromInput')->once()->with($input)->andReturn($snapshot);
|
||||
|
||||
$result = (new CompareLegacyAndNewReportUseCase($repository, $adapter))->handle($input);
|
||||
|
||||
expect($result->status)->toBe('matched')
|
||||
->and($result->diff)->toBe([]);
|
||||
});
|
||||
@@ -1,15 +0,0 @@
|
||||
<?php
|
||||
|
||||
use App\Domain\Reports\Calculators\DepartmentLoadCalculator;
|
||||
|
||||
it('calculates department load percentage', function () {
|
||||
$calculator = new DepartmentLoadCalculator;
|
||||
|
||||
expect($calculator->calculate(27, 30))->toBe(90);
|
||||
});
|
||||
|
||||
it('returns zero when beds count is zero', function () {
|
||||
$calculator = new DepartmentLoadCalculator;
|
||||
|
||||
expect($calculator->calculate(27, 0))->toBe(0);
|
||||
});
|
||||
@@ -1,92 +0,0 @@
|
||||
<?php
|
||||
|
||||
use App\Application\Reports\CompareLegacyAndNewReportUseCase;
|
||||
use App\Application\Reports\DTO\GenerateReportInput;
|
||||
use App\Application\Reports\DTO\ReportComparisonResult;
|
||||
use App\Application\Reports\GenerateReportUseCase;
|
||||
use App\Domain\Reports\Contracts\AuditLogger;
|
||||
use App\Domain\Reports\Contracts\PatientSource;
|
||||
use App\Domain\Reports\Contracts\ReportRepository;
|
||||
use App\Domain\Reports\Models\PatientCollection;
|
||||
use App\Domain\Reports\Models\ReportContext;
|
||||
use App\Domain\Reports\Models\ReportSnapshot;
|
||||
use App\Domain\Reports\Models\SavedReportResult;
|
||||
use App\Infrastructure\Reports\Adapters\LegacyReportServiceAdapter;
|
||||
|
||||
it('stores generated report and writes comparison audit', function () {
|
||||
$input = new GenerateReportInput(
|
||||
departmentId: 10,
|
||||
userId: 5015,
|
||||
actorUserId: 15,
|
||||
periodStart: new DateTimeImmutable('2026-04-08 06:00:00'),
|
||||
periodEnd: new DateTimeImmutable('2026-04-09 06:00:00'),
|
||||
reportType: 'daily',
|
||||
autoFill: true,
|
||||
);
|
||||
|
||||
$patientSource = new class implements PatientSource
|
||||
{
|
||||
public function load(ReportContext $context): PatientCollection
|
||||
{
|
||||
return new PatientCollection([], [
|
||||
'payload' => [
|
||||
'departmentId' => $context->departmentId,
|
||||
'userId' => $context->userId,
|
||||
'dates' => [$context->periodStart->getTimestamp(), $context->periodEnd->getTimestamp()],
|
||||
'status' => 'submitted',
|
||||
'metrics' => ['metrika_item_4' => 11],
|
||||
'observationPatients' => [],
|
||||
'unwantedEvents' => [],
|
||||
],
|
||||
]);
|
||||
}
|
||||
};
|
||||
|
||||
$snapshot = new ReportSnapshot(
|
||||
departmentId: 10,
|
||||
userId: 5015,
|
||||
actorUserId: 15,
|
||||
periodStart: new DateTimeImmutable('2026-04-08 06:00:00'),
|
||||
periodEnd: new DateTimeImmutable('2026-04-09 06:00:00'),
|
||||
status: 'submitted',
|
||||
autoFill: true,
|
||||
metrics: [4 => 11],
|
||||
);
|
||||
|
||||
$repository = \Mockery::mock(ReportRepository::class);
|
||||
$repository->shouldReceive('save')
|
||||
->once()
|
||||
->andReturn(new SavedReportResult(88, $snapshot));
|
||||
|
||||
$repository->shouldReceive('findSnapshot')
|
||||
->once()
|
||||
->with(88)
|
||||
->andReturn($snapshot);
|
||||
|
||||
$adapter = \Mockery::mock(LegacyReportServiceAdapter::class);
|
||||
$adapter->shouldReceive('buildSnapshotFromInput')
|
||||
->once()
|
||||
->andReturn($snapshot);
|
||||
|
||||
$comparator = new CompareLegacyAndNewReportUseCase($repository, $adapter);
|
||||
|
||||
$auditLogger = \Mockery::mock(AuditLogger::class);
|
||||
$auditLogger->shouldReceive('logComparison')->once();
|
||||
|
||||
$useCase = new GenerateReportUseCase(
|
||||
reportRepository: $repository,
|
||||
auditLogger: $auditLogger,
|
||||
comparator: $comparator,
|
||||
patientSource: $patientSource,
|
||||
calculators: [],
|
||||
compareBeforeCutover: true,
|
||||
);
|
||||
|
||||
$result = $useCase->handle($input);
|
||||
|
||||
expect($result->reportId)->toBe(88)
|
||||
->and($result->usedNewArchitecture)->toBeTrue()
|
||||
->and($result->comparison?->status)->toBe('matched')
|
||||
->and($result->comparison?->diff)->toBe([])
|
||||
->and($result->comparison?->reportId)->toBe(88);
|
||||
});
|
||||
@@ -1,28 +0,0 @@
|
||||
<?php
|
||||
|
||||
use App\Domain\Reports\ValueObjects\MetrikaConfig;
|
||||
|
||||
it('normalizes metrics from payload keys and numeric ids', function () {
|
||||
$normalized = MetrikaConfig::normalizeMetrics([
|
||||
'metrika_item_12' => 7,
|
||||
4 => 11,
|
||||
'invalid' => 100,
|
||||
'15' => 3,
|
||||
]);
|
||||
|
||||
expect($normalized)->toBe([
|
||||
4 => 11,
|
||||
12 => 7,
|
||||
15 => 3,
|
||||
]);
|
||||
});
|
||||
|
||||
it('converts normalized metrics back to payload keys', function () {
|
||||
expect(MetrikaConfig::toPayloadMetrics([
|
||||
4 => 11,
|
||||
12 => 7,
|
||||
]))->toBe([
|
||||
'metrika_item_4' => 11,
|
||||
'metrika_item_12' => 7,
|
||||
]);
|
||||
});
|
||||
@@ -1,38 +0,0 @@
|
||||
<?php
|
||||
|
||||
use App\Domain\Reports\Calculators\PreoperativeDaysCalculator;
|
||||
use App\Domain\Reports\Models\OperationInterval;
|
||||
|
||||
it('calculates total and average preoperative days', function () {
|
||||
$calculator = new PreoperativeDaysCalculator;
|
||||
|
||||
$result = $calculator->calculate([
|
||||
new OperationInterval(
|
||||
admittedAt: new DateTimeImmutable('2026-04-01 10:00:00'),
|
||||
operationAt: new DateTimeImmutable('2026-04-03 09:00:00'),
|
||||
),
|
||||
new OperationInterval(
|
||||
admittedAt: new DateTimeImmutable('2026-04-05 10:00:00'),
|
||||
operationAt: new DateTimeImmutable('2026-04-06 09:00:00'),
|
||||
),
|
||||
]);
|
||||
|
||||
expect($result->total)->toBe(3)
|
||||
->and($result->count)->toBe(2)
|
||||
->and($result->average)->toBe(1.5);
|
||||
});
|
||||
|
||||
it('ignores invalid preoperative intervals', function () {
|
||||
$calculator = new PreoperativeDaysCalculator;
|
||||
|
||||
$result = $calculator->calculate([
|
||||
new OperationInterval(
|
||||
admittedAt: new DateTimeImmutable('2026-04-03 10:00:00'),
|
||||
operationAt: new DateTimeImmutable('2026-04-01 09:00:00'),
|
||||
),
|
||||
]);
|
||||
|
||||
expect($result->total)->toBe(0)
|
||||
->and($result->count)->toBe(0)
|
||||
->and($result->average)->toBe(0.0);
|
||||
});
|
||||
@@ -1,58 +0,0 @@
|
||||
<?php
|
||||
|
||||
use App\Application\Reports\ReportInputFactory;
|
||||
use App\Models\Department;
|
||||
use App\Models\User;
|
||||
use App\Services\DateRange;
|
||||
use App\Services\DateRangeService;
|
||||
use Carbon\Carbon;
|
||||
|
||||
it('builds manual generate report input from validated payload', function () {
|
||||
$user = new User;
|
||||
$user->id = 15;
|
||||
|
||||
$factory = new ReportInputFactory(app(DateRangeService::class));
|
||||
|
||||
$input = $factory->forManualSave($user, [
|
||||
'departmentId' => 10,
|
||||
'userId' => 5015,
|
||||
'dates' => [1744063200, 1744149600],
|
||||
'metrics' => ['metrika_item_4' => 11],
|
||||
'observationPatients' => [['id' => 100]],
|
||||
'unwantedEvents' => [['title' => 'A']],
|
||||
'status' => 'draft',
|
||||
'reportId' => 55,
|
||||
]);
|
||||
|
||||
expect($input->departmentId)->toBe(10)
|
||||
->and($input->userId)->toBe(5015)
|
||||
->and($input->actorUserId)->toBe(15)
|
||||
->and($input->reportId)->toBe(55)
|
||||
->and($input->metrics)->toBe(['metrika_item_4' => 11])
|
||||
->and($input->rawPayload['actorUserId'])->toBe(15);
|
||||
});
|
||||
|
||||
it('builds auto fill generate report input from scoped user and date range', function () {
|
||||
$user = new User;
|
||||
$user->id = 15;
|
||||
$user->rf_lpudoctor_id = 5015;
|
||||
|
||||
$department = new Department;
|
||||
$department->department_id = 10;
|
||||
|
||||
$dateRange = new DateRange(
|
||||
startDate: Carbon::parse('2026-04-08 06:00:00', 'Asia/Yakutsk'),
|
||||
endDate: Carbon::parse('2026-04-09 06:00:00', 'Asia/Yakutsk'),
|
||||
startDateRaw: '2026-04-08 06:00:00',
|
||||
endDateRaw: '2026-04-09 06:00:00',
|
||||
isOneDay: true,
|
||||
);
|
||||
|
||||
$factory = new ReportInputFactory(app(DateRangeService::class));
|
||||
$input = $factory->forAutoFill($user, $department, $dateRange);
|
||||
|
||||
expect($input->autoFill)->toBeTrue()
|
||||
->and($input->status)->toBe('submitted')
|
||||
->and($input->departmentId)->toBe(10)
|
||||
->and($input->userId)->toBe(5015);
|
||||
});
|
||||
@@ -1,148 +0,0 @@
|
||||
<?php
|
||||
|
||||
use App\Infrastructure\Reports\Services\ReportPatientsReadService;
|
||||
use App\Infrastructure\Reports\Services\ReportReadContextResolver;
|
||||
use App\Models\Department;
|
||||
use App\Models\User;
|
||||
use App\Services\DateRange;
|
||||
use App\Services\SnapshotService;
|
||||
use App\Services\UnifiedPatientService;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
it('reads one-day plan patients from snapshots when submitted report exists', function () {
|
||||
$department = (new Department())->forceFill([
|
||||
'department_id' => 100,
|
||||
'rf_mis_department_id' => 200,
|
||||
]);
|
||||
$user = \Mockery::mock(User::class);
|
||||
$dateRange = new DateRange(
|
||||
Carbon::parse('2026-04-08 06:00:00'),
|
||||
Carbon::parse('2026-04-09 06:00:00'),
|
||||
'2026-04-08 06:00:00',
|
||||
'2026-04-09 06:00:00',
|
||||
true,
|
||||
);
|
||||
|
||||
$snapshotPatients = collect([
|
||||
(object) ['id' => 'mis:10', 'sourceType' => 'mis'],
|
||||
(object) ['id' => 'manual:501', 'sourceType' => 'manual'],
|
||||
]);
|
||||
|
||||
$unifiedPatientService = \Mockery::mock(UnifiedPatientService::class);
|
||||
$snapshotService = \Mockery::mock(SnapshotService::class);
|
||||
$contextResolver = \Mockery::mock(ReportReadContextResolver::class);
|
||||
|
||||
$contextResolver->shouldReceive('resolveBranchId')
|
||||
->once()
|
||||
->with($department)
|
||||
->andReturn(10);
|
||||
$contextResolver->shouldReceive('shouldUseReplicaForLiveStatus')
|
||||
->once()
|
||||
->with($user, 'plan', $dateRange)
|
||||
->andReturn(false);
|
||||
$contextResolver->shouldReceive('shouldUseSnapshots')
|
||||
->once()
|
||||
->with($department, $dateRange, false)
|
||||
->andReturn(true);
|
||||
$contextResolver->shouldReceive('getReportsForDateRange')
|
||||
->once()
|
||||
->with(100, $dateRange)
|
||||
->andReturn(collect([(object) ['report_id' => 91]]));
|
||||
$contextResolver->shouldReceive('getRecipientReportIds')
|
||||
->once()
|
||||
->with([91])
|
||||
->andReturn([91]);
|
||||
|
||||
$snapshotService->shouldReceive('getPatientsFromOneDayCurrentSnapshots')
|
||||
->once()
|
||||
->with('plan', [91], false, [91])
|
||||
->andReturn($snapshotPatients);
|
||||
|
||||
$service = new ReportPatientsReadService(
|
||||
unifiedPatientService: $unifiedPatientService,
|
||||
snapshotService: $snapshotService,
|
||||
contextResolver: $contextResolver,
|
||||
);
|
||||
|
||||
$patientIds = $service->getPatientsByStatus($department, $user, 'mis-plan', $dateRange, true);
|
||||
|
||||
expect($patientIds)->toBeInstanceOf(Collection::class)
|
||||
->and($patientIds->all())->toBe(['mis:10']);
|
||||
});
|
||||
|
||||
it('always reads reanimation patients from replica sources', function () {
|
||||
$department = (new Department())->forceFill([
|
||||
'department_id' => 100,
|
||||
'rf_mis_department_id' => 200,
|
||||
]);
|
||||
$user = \Mockery::mock(User::class);
|
||||
$dateRange = new DateRange(
|
||||
Carbon::parse('2026-04-08 06:00:00'),
|
||||
Carbon::parse('2026-04-09 06:00:00'),
|
||||
'2026-04-08 06:00:00',
|
||||
'2026-04-09 06:00:00',
|
||||
true,
|
||||
);
|
||||
|
||||
$expected = collect([(object) ['id' => 'mis:55']]);
|
||||
|
||||
$unifiedPatientService = \Mockery::mock(UnifiedPatientService::class);
|
||||
$snapshotService = \Mockery::mock(SnapshotService::class);
|
||||
$contextResolver = \Mockery::mock(ReportReadContextResolver::class);
|
||||
|
||||
$contextResolver->shouldReceive('resolveBranchId')
|
||||
->once()
|
||||
->with($department)
|
||||
->andReturn(10);
|
||||
|
||||
$unifiedPatientService->shouldReceive('getLivePatientsByStatus')
|
||||
->once()
|
||||
->with($department, $user, 'reanimation', $dateRange, 10, false, true)
|
||||
->andReturn($expected);
|
||||
|
||||
$service = new ReportPatientsReadService(
|
||||
unifiedPatientService: $unifiedPatientService,
|
||||
snapshotService: $snapshotService,
|
||||
contextResolver: $contextResolver,
|
||||
);
|
||||
|
||||
expect($service->getPatientsByStatus($department, $user, 'reanimation', $dateRange))->toBe($expected);
|
||||
});
|
||||
|
||||
it('counts scoped replica patients through unified patient service', function () {
|
||||
$department = (new Department())->forceFill([
|
||||
'department_id' => 100,
|
||||
'rf_mis_department_id' => 200,
|
||||
]);
|
||||
$user = \Mockery::mock(User::class);
|
||||
$dateRange = new DateRange(
|
||||
Carbon::parse('2026-04-08 06:00:00'),
|
||||
Carbon::parse('2026-04-09 06:00:00'),
|
||||
'2026-04-08 06:00:00',
|
||||
'2026-04-09 06:00:00',
|
||||
true,
|
||||
);
|
||||
|
||||
$unifiedPatientService = \Mockery::mock(UnifiedPatientService::class);
|
||||
$snapshotService = \Mockery::mock(SnapshotService::class);
|
||||
$contextResolver = \Mockery::mock(ReportReadContextResolver::class);
|
||||
|
||||
$contextResolver->shouldReceive('resolveBranchId')
|
||||
->once()
|
||||
->with($department)
|
||||
->andReturn(10);
|
||||
|
||||
$unifiedPatientService->shouldReceive('getLivePatientCountByStatus')
|
||||
->once()
|
||||
->with($department, $user, 'special-plan', $dateRange, 10, true)
|
||||
->andReturn(3);
|
||||
|
||||
$service = new ReportPatientsReadService(
|
||||
unifiedPatientService: $unifiedPatientService,
|
||||
snapshotService: $snapshotService,
|
||||
contextResolver: $contextResolver,
|
||||
);
|
||||
|
||||
expect($service->getPatientsCountByStatus($department, $user, 'special-plan', $dateRange))->toBe(3);
|
||||
});
|
||||
@@ -1,36 +0,0 @@
|
||||
<?php
|
||||
|
||||
use App\Domain\Reports\Models\ReportSnapshot;
|
||||
|
||||
it('normalizes metrics and exposes comparable payload', function () {
|
||||
$snapshot = new ReportSnapshot(
|
||||
departmentId: 10,
|
||||
userId: 5015,
|
||||
actorUserId: 15,
|
||||
periodStart: new DateTimeImmutable('2026-04-08 06:00:00'),
|
||||
periodEnd: new DateTimeImmutable('2026-04-09 06:00:00'),
|
||||
status: 'submitted',
|
||||
autoFill: true,
|
||||
metrics: [
|
||||
'metrika_item_12' => 7,
|
||||
4 => 11,
|
||||
],
|
||||
observationPatients: [['medical_history_id' => 100]],
|
||||
unwantedEvents: [['title' => 'Event']],
|
||||
);
|
||||
|
||||
expect($snapshot->normalizedMetrics())->toBe([
|
||||
4 => 11,
|
||||
12 => 7,
|
||||
])->and($snapshot->toComparableArray()['auto_fill'])->toBeTrue();
|
||||
});
|
||||
|
||||
it('rejects invalid period ranges', function () {
|
||||
new ReportSnapshot(
|
||||
departmentId: 10,
|
||||
userId: 5015,
|
||||
actorUserId: 15,
|
||||
periodStart: new DateTimeImmutable('2026-04-09 06:00:00'),
|
||||
periodEnd: new DateTimeImmutable('2026-04-08 06:00:00'),
|
||||
);
|
||||
})->throws(InvalidArgumentException::class);
|
||||
@@ -1,147 +0,0 @@
|
||||
<?php
|
||||
|
||||
use App\Infrastructure\Reports\Services\CalculatedMetricsSynchronizer;
|
||||
use App\Infrastructure\Reports\Services\ReportReadContextResolver;
|
||||
use App\Infrastructure\Reports\Services\ReportStatisticsReadService;
|
||||
use App\Models\Department;
|
||||
use App\Models\User;
|
||||
use App\Services\DateRange;
|
||||
use App\Services\PatientService;
|
||||
use App\Services\SnapshotService;
|
||||
use App\Services\UnifiedPatientService;
|
||||
use Carbon\Carbon;
|
||||
|
||||
it('returns empty statistics when department branch cannot be resolved', function () {
|
||||
$department = (new Department())->forceFill([
|
||||
'department_id' => 100,
|
||||
'rf_mis_department_id' => 200,
|
||||
]);
|
||||
$user = \Mockery::mock(User::class);
|
||||
$dateRange = reportStatisticsDateRange();
|
||||
|
||||
$contextResolver = \Mockery::mock(ReportReadContextResolver::class);
|
||||
$contextResolver->shouldReceive('resolveBranchId')
|
||||
->once()
|
||||
->with($department)
|
||||
->andReturn(null);
|
||||
|
||||
$service = new ReportStatisticsReadService(
|
||||
unifiedPatientService: \Mockery::mock(UnifiedPatientService::class),
|
||||
patientService: \Mockery::mock(PatientService::class),
|
||||
snapshotService: \Mockery::mock(SnapshotService::class),
|
||||
contextResolver: $contextResolver,
|
||||
calculatedMetricsSynchronizer: \Mockery::mock(CalculatedMetricsSynchronizer::class),
|
||||
);
|
||||
|
||||
expect($service->getReportStatistics($department, $user, $dateRange))->toMatchArray([
|
||||
'recipientCount' => 0,
|
||||
'extractCount' => 0,
|
||||
'currentCount' => 0,
|
||||
'deadCount' => 0,
|
||||
'surgicalCount' => [0, 0],
|
||||
'recipientIds' => [],
|
||||
'percentDead' => 0,
|
||||
'beds' => 0,
|
||||
]);
|
||||
});
|
||||
|
||||
it('reads live report statistics from replica when snapshots are not active', function () {
|
||||
$department = (new Department())->forceFill([
|
||||
'department_id' => 100,
|
||||
'rf_mis_department_id' => 200,
|
||||
]);
|
||||
$department->setRelation('metrikaDefault', collect([
|
||||
(object) ['rf_metrika_item_id' => 1, 'value' => 42],
|
||||
]));
|
||||
|
||||
$user = \Mockery::mock(User::class);
|
||||
$dateRange = reportStatisticsDateRange();
|
||||
|
||||
$contextResolver = \Mockery::mock(ReportReadContextResolver::class);
|
||||
$contextResolver->shouldReceive('resolveBranchId')
|
||||
->once()
|
||||
->with($department)
|
||||
->andReturn(10);
|
||||
$contextResolver->shouldReceive('shouldUseSnapshots')
|
||||
->once()
|
||||
->with($department, $dateRange)
|
||||
->andReturn(false);
|
||||
|
||||
$unifiedPatientService = \Mockery::mock(UnifiedPatientService::class);
|
||||
$unifiedPatientService->shouldReceive('getLivePatientCountByStatus')
|
||||
->once()
|
||||
->with($department, $user, 'plan', $dateRange, 10, true)
|
||||
->andReturn(7);
|
||||
$unifiedPatientService->shouldReceive('getLivePatientCountByStatus')
|
||||
->once()
|
||||
->with($department, $user, 'emergency', $dateRange, 10, true)
|
||||
->andReturn(5);
|
||||
$unifiedPatientService->shouldReceive('getLivePatientCountByStatus')
|
||||
->once()
|
||||
->with($department, $user, 'current', $dateRange, 10)
|
||||
->andReturn(30);
|
||||
$unifiedPatientService->shouldReceive('getLivePatientCountByStatus')
|
||||
->once()
|
||||
->with($department, $user, 'recipient', $dateRange, 10)
|
||||
->andReturn(4);
|
||||
$unifiedPatientService->shouldReceive('getLivePatientCountByStatus')
|
||||
->once()
|
||||
->with($department, $user, 'outcome', $dateRange, 10)
|
||||
->andReturn(8);
|
||||
$unifiedPatientService->shouldReceive('getLivePatientCountByStatus')
|
||||
->once()
|
||||
->with($department, $user, 'outcome-deceased', $dateRange, 10)
|
||||
->andReturn(2);
|
||||
$unifiedPatientService->shouldReceive('getRecipientIdsForReport')
|
||||
->once()
|
||||
->with($department, $user, $dateRange, 10)
|
||||
->andReturn([11, 12]);
|
||||
|
||||
$patientService = \Mockery::mock(PatientService::class);
|
||||
$patientService->shouldReceive('getSurgicalPatients')
|
||||
->once()
|
||||
->with('emergency', 10, $dateRange, true)
|
||||
->andReturn(3);
|
||||
$patientService->shouldReceive('getSurgicalPatients')
|
||||
->once()
|
||||
->with('plan', 10, $dateRange, true)
|
||||
->andReturn(6);
|
||||
|
||||
$calculatedMetricsSynchronizer = \Mockery::mock(CalculatedMetricsSynchronizer::class);
|
||||
$calculatedMetricsSynchronizer->shouldReceive('getManualSurgicalCounts')
|
||||
->once()
|
||||
->with($department, $dateRange)
|
||||
->andReturn([1, 2]);
|
||||
|
||||
$service = new ReportStatisticsReadService(
|
||||
unifiedPatientService: $unifiedPatientService,
|
||||
patientService: $patientService,
|
||||
snapshotService: \Mockery::mock(SnapshotService::class),
|
||||
contextResolver: $contextResolver,
|
||||
calculatedMetricsSynchronizer: $calculatedMetricsSynchronizer,
|
||||
);
|
||||
|
||||
expect($service->getReportStatistics($department, $user, $dateRange))->toMatchArray([
|
||||
'recipientCount' => 4,
|
||||
'extractCount' => 8,
|
||||
'currentCount' => 30,
|
||||
'deadCount' => 2,
|
||||
'surgicalCount' => [4, 8],
|
||||
'recipientIds' => [11, 12],
|
||||
'planCount' => 7,
|
||||
'emergencyCount' => 5,
|
||||
'percentDead' => 25.0,
|
||||
'beds' => 42,
|
||||
]);
|
||||
});
|
||||
|
||||
function reportStatisticsDateRange(): DateRange
|
||||
{
|
||||
return new DateRange(
|
||||
Carbon::parse('2026-04-08 06:00:00'),
|
||||
Carbon::parse('2026-04-09 06:00:00'),
|
||||
'2026-04-08 06:00:00',
|
||||
'2026-04-09 06:00:00',
|
||||
true,
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user