self::URGENCY_PLANNED, 2 => self::URGENCY_URGENT, default => self::URGENCY_UNKNOWN, }; } /** * Определяет поступил ли пациент в диапазоне. */ public static function classifyAdmitted(Carbon|string|null $ingoingDate, DateRange $dateRange): bool { if (is_null($ingoingDate)) { return false; } $ingoingLocal = Carbon::parse($ingoingDate); // Начало диапазона (с 09:00 первого дня) $rangeStart = $dateRange->start()->copy()->setTime(9, 0, 0); // Конец диапазона (до 09:00 последнего дня) $rangeEnd = $dateRange->end()->copy()->setTime(9, 0, 0); return $ingoingLocal->between($rangeStart, $rangeEnd); } /** * @param Collection|null $reanimations * @param DateRange $dateRange * @return bool */ public static function classifyReanimation(Collection|null $reanimations, DateRange $dateRange): bool { if ($reanimations === null || $reanimations->isEmpty()) return false; if ($reanimations ->where('out_date', '<', $dateRange->endSql()) ->where('out_date', '>=', $dateRange->startSql()) ->isNotEmpty()) { return true; } return false; } public static function classifyObservable(?ObservableMedicalHistory $observable, DateRange $dateRange): bool { if (empty($observable)) { return false; } $start = $dateRange->start(); $end = $dateRange->end(); // Наблюдение началось после окончания диапазона if ($observable->observable_in > $end) { return false; } // Наблюдение закончилось до начала диапазона if ($observable->observable_out !== null && $observable->observable_out <= $start) { return false; } return true; } public static function classifyPeriodFlags( UnifiedMedicalHistory|MedicalHistory|ReportDutyPatient|ReportNursePatient $history, DateRange $dateRange ): array { // Получаем все миграции $migrations = self::historyMigrations($history); if ($migrations->isEmpty()) { return self::emptyFlags($history); } // Приводим даты к Carbon $deathDate = self::toCarbon($history->death_date ?? null); // Получаем границы периода $periodStart = $dateRange->start(); $periodEnd = $dateRange->end(); // ========== 1. СМЕРТЬ ========== // if ($deathDate && $deathDate->lte($periodEnd)) { // return [ // 'recipient' => false, // 'discharged' => false, // 'deceased' => true, // 'transferred' => false, // 'outcome' => true, // 'current_at_end' => false, // 'planned' => (int) ($history->urgency_id ?? 0) === 2, // 'urgent' => (int) ($history->urgency_id ?? 0) === 1, // ]; // } // ========== 2. АНАЛИЗ МИГРАЦИЙ ========== $hasRecipientInPeriod = false; $hasDeathInPeriod = false; $hasTransferInPeriod = false; $hasDischargeInPeriod = false; $hasActiveMigrationAtEnd = false; $transferred = []; foreach ($migrations as $migration) { $ingoingDate = self::toCarbon($migration->ingoing_date ?? null); $outDate = self::toCarbon($migration->out_date ?? null); $visitResultId = (int) ($migration->visit_result_id ?? 0); $statCureResultId = (int) ($migration->stat_cure_result_id ?? 0); // Поступление в периоде if ($ingoingDate && self::dateInPeriod($ingoingDate, $dateRange)) { $hasRecipientInPeriod = true; } // Проверка на активную миграцию в конце периода if ($ingoingDate && $ingoingDate <= $periodEnd) { if (!$outDate || $outDate > $periodEnd) { $hasActiveMigrationAtEnd = true; } } // Выбытие в периоде (есть out_date в периоде) if ($outDate && self::dateInPeriod($outDate, $dateRange)) { // Смерть по исходу лечения (5, 15) if (in_array($visitResultId, [5, 15], true)) { $hasDeathInPeriod = true; } // Перевод (коды 4, 14) elseif (in_array($visitResultId, [4, 14], true)) { $hasTransferInPeriod = true; } // Выписка else { $hasDischargeInPeriod = true; } } } // ========== 3. ЕСЛИ НЕТ ИСХОДА ПО МИГРАЦИЯМ, ПРОВЕРЯЕМ EXTRACT_DATE ========== // if (!$hasDeathInPeriod && !$hasTransferInPeriod && !$hasDischargeInPeriod) { // $extractDate = self::toCarbon($history->extract_date ?? null); // $visitResultId = (int) ($history->visit_result_id ?? 0); // // if ($extractDate && $extractDate->lte($periodEnd)) { // // Смерть // if ($deathDate && $deathDate->lte($periodEnd)) { // $hasDeathInPeriod = true; // } // // Перевод // elseif (in_array($visitResultId, [4, 14], true)) { // $hasTransferInPeriod = true; // } // // Выписка // else { // $hasDischargeInPeriod = true; // } // } // } // ========== 4. ФОРМИРОВАНИЕ РЕЗУЛЬТАТА ========== // Смерть if ($hasDeathInPeriod) { return [ 'recipient' => $hasRecipientInPeriod, 'discharged' => false, 'deceased' => true, 'transferred' => false, 'outcome' => false, 'current_at_end' => false, 'planned' => (int) ($history->urgency_id ?? 0) === 2, 'urgent' => (int) ($history->urgency_id ?? 0) === 1, ]; } // Перевод if ($hasTransferInPeriod) { return [ 'recipient' => $hasRecipientInPeriod, 'discharged' => false, 'deceased' => false, 'transferred' => true, 'outcome' => false, 'current_at_end' => false, 'planned' => (int) ($history->urgency_id ?? 0) === 2, 'urgent' => (int) ($history->urgency_id ?? 0) === 1, ]; } // Выписка if ($hasDischargeInPeriod) { return [ 'recipient' => $hasRecipientInPeriod, 'discharged' => true, 'deceased' => false, 'transferred' => false, 'outcome' => true, 'current_at_end' => false, 'planned' => (int) ($history->urgency_id ?? 0) === 2, 'urgent' => (int) ($history->urgency_id ?? 0) === 1, ]; } // В отделении на конец периода if ($hasActiveMigrationAtEnd) { return [ 'recipient' => $hasRecipientInPeriod, 'discharged' => false, 'deceased' => false, 'transferred' => false, 'outcome' => false, 'current_at_end' => true, 'planned' => (int) ($history->urgency_id ?? 0) === 2, 'urgent' => (int) ($history->urgency_id ?? 0) === 1, ]; } // По умолчанию - поступивший, но не в отделении (например, выписан до периода) return [ 'recipient' => $hasRecipientInPeriod, 'discharged' => false, 'deceased' => false, 'transferred' => false, 'outcome' => false, 'current_at_end' => false, 'planned' => (int) ($history->urgency_id ?? 0) === 2, 'urgent' => (int) ($history->urgency_id ?? 0) === 1, ]; } /** * Пустые флаги для пациента без миграций */ private static function emptyFlags($history): array { return [ 'recipient' => false, 'discharged' => false, 'deceased' => false, 'transferred' => false, 'outcome' => false, 'current_at_end' => false, 'planned' => (int) ($history->urgency_id ?? 0) === 2, 'urgent' => (int) ($history->urgency_id ?? 0) === 1, ]; } /** * Определяет статус пациента на основе "сырых" полей из БД. */ public static function classify(UnifiedMedicalHistory|MedicalHistory|ReportDutyPatient|ReportNursePatient $history, DateRange $dateRange): string { $flags = self::classifyPeriodFlags($history, $dateRange); if ($flags['deceased']) { return self::STATUS_DECEASED; } if ($flags['transferred']) { return self::STATUS_TRANSFERRED; } if ($flags['discharged']) { return self::STATUS_DISCHARGED; } if ($flags['recipient']) { return self::STATUS_RECIPIENT; } if ($flags['current_at_end']) { return self::STATUS_IN_DEPARTMENT; } return 'unknown'; } private static function historyMigrations( UnifiedMedicalHistory|MedicalHistory|ReportDutyPatient|ReportNursePatient $history ): Collection { if (method_exists($history, 'relationLoaded') && $history->relationLoaded('migrations')) { return $history->migrations ?? collect(); } return collect([$history->latestMigration])->filter(); } private static function isCurrentAtPeriodEnd( UnifiedMedicalHistory|MedicalHistory|ReportDutyPatient|ReportNursePatient $history, Collection $migrations, DateRange $dateRange ): bool { if (self::toCarbon($history->death_date ?? null)?->lte($dateRange->endDate)) { return false; } if (self::toCarbon($history->extract_date ?? null)?->lte($dateRange->endDate)) { return false; } return $migrations->contains(function ($migration) use ($dateRange) { $ingoingDate = self::toCarbon($migration->ingoing_date ?? null); $outDate = self::toCarbon($migration->out_date ?? null); return $ingoingDate && $ingoingDate->lte($dateRange->endDate) && (! $outDate || $outDate->gt($dateRange->endDate)); }); } public static function dateInPeriod(Carbon|string|null $date, DateRange $dateRange): bool { $date = self::toCarbon($date); return $date && $date->gt($dateRange->startDate) && $date->lte($dateRange->endDate); } private static function toCarbon(Carbon|string|null $date): ?Carbon { return $date ? Carbon::parse($date) : null; } }