|null $sourceTypes */ public function getDtos( Department $department, string $status, DateRange $dateRange, ?array $sourceTypes = ['manual', 'special'], bool $forSnapshots = false ): Collection { return $this->mapManualPatients( $this->getPatients($department, $status, $dateRange, $sourceTypes, ! $forSnapshots), $dateRange ); } /** * Загрузить сырые manual/special записи пациентов для запрошенного статуса отчёта. * * @param array|null $sourceTypes */ public function getPatients( Department $department, string $status, DateRange $dateRange, ?array $sourceTypes = ['manual', 'special'], bool $withOperations = true ): Collection { $query = $this->buildManualPatientsQuery($department, $dateRange, $sourceTypes, $withOperations); return match ($status) { 'plan', 'emergency' => $query->current()->where('patient_kind', $status)->get(), 'current' => $query->current()->get(), 'recipient' => $query->whereBetween('admitted_at', [$dateRange->startSql(), $dateRange->endSql()])->get(), 'outcome' => $query ->whereNotNull('outcome_type') ->whereIn('outcome_type', ['discharged', 'deceased']) ->whereBetween('outcome_at', [$dateRange->startSql(), $dateRange->endSql()]) ->get(), 'outcome-discharged', 'outcome-transferred', 'outcome-deceased' => $query ->where('outcome_type', str_replace('outcome-', '', $status)) ->whereBetween('outcome_at', [$dateRange->startSql(), $dateRange->endSql()]) ->get(), 'reanimation' => collect(), default => collect(), }; } /** * Посчитать manual/special пациентов для статуса отчёта. * * @param array|null $sourceTypes */ public function getCount( Department $department, string $status, DateRange $dateRange, ?array $sourceTypes = ['manual', 'special'] ): int { $query = $this->buildManualPatientsQuery($department, $dateRange, $sourceTypes, false); return match ($status) { 'plan', 'emergency' => $query->current()->where('patient_kind', $status)->count(), 'current' => $query->current()->count(), 'recipient' => $query->whereBetween('admitted_at', [$dateRange->startSql(), $dateRange->endSql()])->count(), 'outcome' => $query ->whereNotNull('outcome_type') ->whereIn('outcome_type', ['discharged', 'deceased']) ->whereBetween('outcome_at', [$dateRange->startSql(), $dateRange->endSql()]) ->count(), 'outcome-discharged', 'outcome-transferred', 'outcome-deceased' => $query ->where('outcome_type', str_replace('outcome-', '', $status)) ->whereBetween('outcome_at', [$dateRange->startSql(), $dateRange->endSql()]) ->count(), default => 0, }; } /** * Загрузить manual-пациентов, уже связанных с MIS-картами в пределах отчётного окна. */ public function getLinkedManualPatientsForPeriod(Department $department, DateRange $dateRange): Collection { $reportIds = $this->getReportIdsForDepartmentPeriod($department, $dateRange); return DepartmentPatient::query() ->where(function ($builder) use ($department, $reportIds) { if (! empty($reportIds)) { $builder->whereIn('rf_report_id', $reportIds); } $builder->orWhere(function ($legacyQuery) use ($department) { $legacyQuery->whereNull('rf_report_id') ->where('rf_department_id', $department->department_id); }); }) ->whereIn('source_type', ['manual', 'special']) ->whereNotNull('rf_medicalhistory_id') ->get() ->keyBy('rf_medicalhistory_id'); } /** * @param array|null $sourceTypes */ private function buildManualPatientsQuery( Department $department, DateRange $dateRange, ?array $sourceTypes, bool $withOperations ) { $reportIds = $this->getReportIdsForDepartmentPeriod($department, $dateRange); $query = DepartmentPatient::query() ->where(function ($builder) use ($department, $reportIds) { if (! empty($reportIds)) { $builder->whereIn('rf_report_id', $reportIds); } $builder->orWhere(function ($legacyQuery) use ($department) { $legacyQuery->whereNull('rf_report_id') ->where('rf_department_id', $department->department_id); }); }); if ($withOperations) { $query->with(['operations.serviceMedical']); } if ($sourceTypes !== null) { $query->whereIn('source_type', $sourceTypes); } return $query; } private function getReportIdsForDepartmentPeriod(Department $department, DateRange $dateRange): array { return Report::query() ->where('rf_department_id', $department->department_id) ->when( $dateRange->isOneDay, fn ($query) => $query->exactPeriod($dateRange->startSql(), $dateRange->endSql()), fn ($query) => $query->withinPeriod($dateRange->startSql(), $dateRange->endSql()), ) ->pluck('report_id') ->all(); } private function mapManualPatients(Collection $manualPatients, DateRange $dateRange): Collection { return $manualPatients ->map(function (DepartmentPatient $patient) use ($dateRange) { $operationsRelation = $patient->relationLoaded('operations') ? $patient->operations : collect(); $operations = $operationsRelation->map(fn ($operation) => [ 'id' => $operation->department_patient_operation_id, 'code' => $operation->serviceMedical?->ServiceMedicalCode ?? $operation->service_code, 'name' => $operation->serviceMedical?->ServiceMedicalName ?? $operation->service_name, 'startAt' => $operation->started_at?->toIso8601String(), 'endAt' => $operation->ended_at?->toIso8601String(), ])->filter(fn ($operation) => $operation['code'] || $operation['name'])->values()->all(); return UnifiedPatientData::fromDepartmentPatient( $patient, $patient->admitted_at?->betweenIncluded($dateRange->startDate, $dateRange->endDate) ?? false, $operations, $this->resolveObservationComment(null, $patient->department_patient_id) ); }) ->sortByDesc(fn (UnifiedPatientData $patient) => $patient->admittedAt ?? '') ->values(); } private function resolveObservationComment(?int $medicalHistoryId, ?int $departmentPatientId): ?string { $query = ObservationPatient::query(); if ($departmentPatientId) { $query->where('rf_department_patient_id', $departmentPatientId); } elseif ($medicalHistoryId) { $query->where('rf_medicalhistory_id', $medicalHistoryId); } else { return null; } return $query->pluck('comment')->filter()->implode('; ') ?: null; } }