misPatientService = $misPatientService; $this->dateService = $dateRangeService; } public function index(Request $request) { $user = Auth::user(); $department = $user->department; $startDateCarbon = Carbon::now()->firstOfMonth(); $endDateCarbon = Carbon::now(); // Определяем даты в зависимости от роли [$startDate, $endDate] = $this->dateService->getDateRangeForUser($user, $request->query('startAt'), $request->query('endAt')); if (Carbon::parse($startDate)->isValid()) { $startDateCarbon = Carbon::parse($startDate)->setTimeZone('Asia/Yakutsk'); } if (Carbon::parse($endDate)->isValid()) { $endDateCarbon = Carbon::parse($endDate)->setTimeZone('Asia/Yakutsk'); } $reportIds = []; $reports = $this->getReportsForDateRange($user->rf_department_id, $startDate, $endDate); $reportIds = $reports->pluck('report_id')->toArray(); // Определяем, используем ли мы снапшоты $reportToday = Report::whereDate('sent_at', $endDate) ->whereDate('created_at', $endDate) ->first(); $useSnapshots = ($user->isAdmin() || $user->isHeadOfDepartment()) || (Carbon::parse($endDate)->isToday() === false || $reportToday); if ($useSnapshots && ($user->isHeadOfDepartment() || $user->isAdmin())) { $report = Report::whereDate('sent_at', $endDate) ->where('rf_department_id', $department->department_id) ->first(); $fillableUserId = $report->rf_lpudoctor_id ?? null; } else { $fillableUserId = $request->query('userId', $user->rf_lpudoctor_id); } $beds = (int)$department->metrikaDefault()->where('rf_metrika_item_id', 1)->first()->value; $occupiedBeds = optional(Report::where('rf_department_id', $user->rf_department_id) ->join('metrika_results', 'reports.report_id', '=', 'metrika_results.rf_report_id') ->where('metrika_results.rf_metrika_item_id', 8) ->orderBy('sent_at', 'desc')->first())->value ?? 0; $percentLoadedBeds = round(intval($occupiedBeds) * 100 / $beds); //intval($occupiedBeds) * 100 / $beds; $metrikaGroup = MetrikaGroup::whereMetrikaGroupId(2)->first(); $metrikaItems = $metrikaGroup->metrikaItems; $misDepartmentId = $request->user()->department->rf_mis_department_id; $branchId = MisStationarBranch::where('rf_DepartmentID', $misDepartmentId) ->value('StationarBranchID'); $unwantedEvents = UnwantedEvent::whereHas('report', function ($query) use ($user, $startDate, $endDate) { $query->where('rf_department_id', $user->rf_department_id) ->whereDate('created_at', $endDate); }) ->get()->map(function ($item) { return [ ...$item->toArray(), 'created_at' => Carbon::parse($item->created_at)->format('Создано d.m.Y в H:i'), ]; }); // Определяем, является ли пользователь заведующим/администратором $isHeadOrAdmin = $user->isHeadOfDepartment() || $user->isAdmin(); // Получаем статистику в зависимости от источника данных if ($useSnapshots || $reportToday) { // Используем снапшоты для статистики // $plan = $this->getCountFromSnapshots('plan', $reportIds); // $emergency = $this->getCountFromSnapshots('emergency', $reportIds); $recipientCount = $this->getPatientsCountFromSnapshot('recipient', $reportIds); $outcomeCount = $this->getPatientsCountFromSnapshot('outcome', $reportIds); $currentCount = $this->getPatientsCountFromSnapshot('current', $reportIds); $deadCount = $this->getPatientsCountFromSnapshot('deceased', $reportIds); // Для операций все равно используем реплику с фильтрацией по датам $surgicalCount = [ $this->getSurgicalPatientsFromSnapshot('plan', $reportIds), $this->getSurgicalPatientsFromSnapshot('emergency', $reportIds) ]; $recipientIds = $this->getRecipientIdsFromSnapshots($reportIds); } else { // Используем реплику для статистики $plan = $this->getPlanOrEmergencyPatients( 'plan', $isHeadOrAdmin, $branchId, $startDate, $endDate, true, today: true ); $emergency = $this->getPlanOrEmergencyPatients( 'emergency', $isHeadOrAdmin, $branchId, $startDate, $endDate, true, today: true ); $outcomeCount = $this->getAllOutcomePatients( $branchId, $startDate, $endDate, true ); $currentCount = $this->getCurrentPatients($branchId, true); $deadCount = $this->getDeceasedOutcomePatients($branchId, $startDate, $endDate, true); $surgicalCount = [ $this->getSurgicalPatients('plan', $isHeadOrAdmin, $branchId, $startDate, $endDate, true), $this->getSurgicalPatients('emergency', $isHeadOrAdmin, $branchId, $startDate, $endDate, true) ]; $recipientIds = $this->getPlanOrEmergencyPatients( null, $isHeadOrAdmin, $branchId, $startDate, $endDate, false, true, true, today: true ); } $isActiveSendButton = Carbon::createFromFormat('Y-m-d H:i:s', $endDate)->isToday() && (!$user->isHeadOfDepartment() && !$user->isAdmin()) && $reportToday == null; $reportDoctor = $reportToday?->lpuDoctor; $message = null; if ($reportToday) { if ($reportDoctor && $reportDoctor->LPUDoctorID === intval($fillableUserId)) { $isActiveSendButton = true; } else { $message = "Отчет уже создан пользователем: $reportDoctor->FAM_V $reportDoctor->IM_V $reportDoctor->OT_V"; } $lpuDoctor = $reportDoctor; } else { if (Carbon::parse($startDate)->diffInDays(Carbon::parse($endDate)) > 1.0) { $lpuDoctor = null; } else { $lpuDoctor = MisLpuDoctor::where('LPUDoctorID', $fillableUserId)->first(); } } $isRangeOneDay = $this->dateService->isRangeOneDay($startDate, $endDate); $date = $isHeadOrAdmin ? [ $this->dateService->parseDate($isRangeOneDay ? $endDate : $startDate)->getTimestampMs(), $this->dateService->parseDate($endDate)->getTimestampMs() ] : $this->dateService->parseDate($endDate)->getTimestampMs(); return response()->json([ 'department' => [ 'beds' => $beds, 'percentLoadedBeds' => $percentLoadedBeds, 'recipientCount' => $useSnapshots ? $recipientCount : $plan + $emergency, //$recipientCount, 'extractCount' => $outcomeCount, //$extractedCount, 'currentCount' => $currentCount, 'deadCount' => $deadCount, 'surgicalCount' => $surgicalCount, 'recipientIds' => $recipientIds, ], 'dates' => [ 'startAt' => $startDateCarbon->getTimestampMs(), 'endAt' => $endDateCarbon->getTimestampMs() ], 'report' => [ 'report_id' => $reportToday?->report_id, 'unwantedEvents' => $unwantedEvents, 'isActiveSendButton' => $isActiveSendButton, 'message' => $message, 'isOneDay' => $isRangeOneDay, 'isHeadOrAdmin' => $isHeadOrAdmin, 'dates' => $date ], 'metrikaItems' => $metrikaItems, 'userId' => $fillableUserId, 'userName' => $lpuDoctor ? "$lpuDoctor->FAM_V $lpuDoctor->IM_V $lpuDoctor->OT_V" : null ]); } private function getSurgicalPatientsFromSnapshot(string $type, array $reportIds) { $count = 0; switch ($type) { case 'emergency': $count = $this->getMetrikaResult(10, $reportIds); break; case 'plan': $count = $this->getMetrikaResult(11, $reportIds); break; case 'recipient': $count = $this->getMetrikaResult(3, $reportIds); break; } return $count; } private function getPatientsCountFromSnapshot(string $type, array $reportIds) { $count = 0; switch ($type) { case 'emergency': $count = $this->getMetrikaResult(10, $reportIds); break; case 'plan': $count = $this->getMetrikaResult(11, $reportIds); break; case 'recipient': $count = $this->getMetrikaResult(3, $reportIds); break; case 'outcome': $count = $this->getMetrikaResult(7, $reportIds); break; case 'deceased': $count = $this->getMetrikaResult(9, $reportIds); break; case 'current': $count = $this->getMetrikaResult(8, $reportIds); break; } return $count; } private function getMetrikaResult(int $metrikaItemId, array $reportIds) { $reports = Report::whereIn('report_id', $reportIds) ->with('metrikaResults') ->get(); $count = 0; foreach ($reports as $report) { foreach ($report->metrikaResults as $metrikaResult) { if ($metrikaResult->rf_metrika_item_id === $metrikaItemId) $count += intval($metrikaResult->value) ?? 0; } } return $count; } /** * Получить количество пациентов из снапшотов */ private function getCountFromSnapshots(string $type, array $reportIds) { return match($type) { 'plan' => MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds) ->where('patient_type', 'plan') ->distinct('rf_medicalhistory_id') ->count('rf_medicalhistory_id'), 'emergency' => MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds) ->where('patient_type', 'emergency') ->distinct('rf_medicalhistory_id') ->count('rf_medicalhistory_id'), 'outcome' => MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds) ->whereIn('patient_type', ['discharged', 'transferred', 'deceased']) ->distinct('rf_medicalhistory_id') ->count('rf_medicalhistory_id'), 'deceased' => MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds) ->where('patient_type', 'deceased') ->distinct('rf_medicalhistory_id') ->count('rf_medicalhistory_id'), 'discharged' => MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds) ->where('patient_type', 'discharged') ->distinct('rf_medicalhistory_id') ->count('rf_medicalhistory_id'), 'transferred' => MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds) ->where('patient_type', 'transferred') ->distinct('rf_medicalhistory_id') ->count('rf_medicalhistory_id'), 'recipient' => MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds) ->where('patient_type', 'recipient') ->distinct('rf_medicalhistory_id') ->count('rf_medicalhistory_id'), default => 0 }; } /** * Получить ID пациентов на лечении из снапшотов */ private function getCurrentPatientsFromSnapshots(array $reportIds, $branchId) { // Получаем ID всех пациентов из снапшотов за период $allSnapshotPatientIds = MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds) ->pluck('rf_medicalhistory_id') ->unique() ->toArray(); if (empty($allSnapshotPatientIds)) { return 0; } // Получаем ID выбывших пациентов $outcomePatientIds = MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds) ->whereIn('patient_type', ['discharged', 'transferred', 'deceased']) ->pluck('rf_medicalhistory_id') ->unique() ->toArray(); // Текущие пациенты = все пациенты - выбывшие $currentPatientIds = array_diff($allSnapshotPatientIds, $outcomePatientIds); return count($currentPatientIds); } /** * Получить ID поступивших пациентов из снапшотов */ private function getRecipientIdsFromSnapshots(array $reportIds) { $recipientIds = MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds) ->where('patient_type', 'recipient') ->pluck('rf_medicalhistory_id') ->unique() ->toArray(); return $recipientIds; } // /** // * Получить отчеты по промежутку дат (оптимизированная версия) // */ // private function getReportsForDateRange($departmentId, $startDate, $endDate) // { // $start = Carbon::parse($startDate); // $end = Carbon::parse($endDate); // // // Если промежуток большой, ограничиваем количество отчетов // $daysDiff = $start->diffInDays($end); // // if ($daysDiff > 30) { // // Для больших промежутков берем только последние отчеты // return Report::where('rf_department_id', $departmentId) // ->whereBetween('created_at', [$start, $end]) // ->orderBy('created_at', 'DESC') // ->take(30) // Ограничиваем количеством // ->get() // ->reverse(); // Возвращаем в правильном порядке // } // // return Report::where('rf_department_id', $departmentId) // ->whereBetween('created_at', [$start, $end]) // ->orderBy('created_at', 'ASC') // ->get(); // } public function store(Request $request) { $user = Auth::user(); $misDepartmentId = $user->department->rf_mis_department_id; $branchId = MisStationarBranch::where('rf_DepartmentID', $misDepartmentId) ->value('StationarBranchID'); // Определяем, является ли пользователь заведующим/администратором $isHeadOrAdmin = $user->isHeadOfDepartment() || $user->isAdmin(); $data = $request->validate([ 'metrics' => 'required', 'observationPatients' => 'nullable', 'departmentId' => 'required|integer', 'unwantedEvents' => 'nullable|array', 'dates' => 'required|array', 'userId' => 'required|integer', 'reportId' => 'nullable' ]); $metrics = $data['metrics']; $observationPatients = $data['observationPatients']; $unwantedEvents = $data['unwantedEvents']; // Определяем даты в зависимости от роли [$startDate, $endDate] = $this->getDateRangeForRole($user, $data['dates'][0], $data['dates'][1]); $metriks = []; foreach ($metrics as $key => $value) { $metrika = new MetrikaResult; $metrikaId = (int)Str::replace('metrika_item_', '', $key); $metrika->rf_metrika_item_id = $metrikaId; $metrika->value = $value; $metriks[] = $metrika; } \DB::beginTransaction(); if (isset($data['reportId']) && $data['reportId']) { $report = Report::updateOrCreate( [ 'report_id' => $data['reportId'] ], [ 'rf_department_id' => $data['departmentId'], 'rf_user_id' => Auth::user()->id, 'rf_lpudoctor_id' => $data['userId'], 'created_at' => now(), 'sent_at' => now() ] ); } else { $report = Report::create([ 'rf_department_id' => $data['departmentId'], 'rf_user_id' => Auth::user()->id, 'rf_lpudoctor_id' => $data['userId'], 'created_at' => now(), 'sent_at' => now() ]); } if (count($unwantedEvents)) { foreach ($unwantedEvents as $unwantedEvent) { // Если есть ID - ищем по нему if (isset($unwantedEvent['unwanted_event_id']) && $unwantedEvent['unwanted_event_id']) { UnwantedEvent::updateOrCreate( ['unwanted_event_id' => $unwantedEvent['unwanted_event_id']], [ 'rf_report_id' => $report->report_id, 'comment' => $unwantedEvent['comment'] ?? '', 'title' => $unwantedEvent['title'] ?? '', 'is_visible' => $unwantedEvent['is_visible'] ?? true, ] ); } else { // Если нет ID - создаем новую запись UnwantedEvent::create([ 'rf_report_id' => $report->report_id, 'comment' => $unwantedEvent['comment'] ?? '', 'title' => $unwantedEvent['title'] ?? '', 'is_visible' => $unwantedEvent['is_visible'] ?? true, ]); } } } else { $unwantedEvents = $report->unwantedEvents; foreach ($unwantedEvents as $unwantedEvent) { $unwantedEvent->delete(); } } foreach ($metriks as $metrika) { MetrikaResult::updateOrCreate( [ 'rf_report_id' => $report->report_id, 'rf_metrika_item_id' => $metrika->rf_metrika_item_id ], [ 'rf_report_id' => $report->report_id, 'rf_metrika_item_id' => $metrika->rf_metrika_item_id, 'value' => $metrika->value, ] ); } if (count($observationPatients)) { foreach ($observationPatients as $observationPatient) { ObservationPatient::updateOrCreate( [ 'rf_medicalhistory_id' => $observationPatient['id'], 'rf_department_id' => $data['departmentId'] ], [ 'rf_department_id' => $data['departmentId'], 'rf_report_id' => $report->report_id, 'rf_medicalhistory_id' => $observationPatient['id'], 'rf_mkab_id' => null, 'comment' => $observationPatient['comment'] ?? null ] ); } } else { foreach ($report->observationPatients as $observationPatient) { $observationPatient->delete(); } } // Сохраняем снимок для каждого типа пациентов // 1. Плановые $planIds = $this->getPlanOrEmergencyPatients('plan', false, $branchId, $startDate, $endDate, false, false, true); foreach ($planIds as $id) { MedicalHistorySnapshot::create([ 'rf_report_id' => $report->report_id, 'rf_medicalhistory_id' => $id, 'patient_type' => 'plan' ]); } // 2. Экстренные $emergencyIds = $this->getPlanOrEmergencyPatients('emergency', false, $branchId, $startDate, $endDate, false, false, true); foreach ($emergencyIds as $id) { MedicalHistorySnapshot::create([ 'rf_report_id' => $report->report_id, 'rf_medicalhistory_id' => $id, 'patient_type' => 'emergency' ]); } // 3. Выписанные $dischargedIds = $this->getDischargedPatients($branchId, $startDate, $endDate, true); foreach ($dischargedIds as $id) { MedicalHistorySnapshot::create([ 'rf_report_id' => $report->report_id, 'rf_medicalhistory_id' => $id, 'patient_type' => 'discharged' ]); } // 4. Переведенные $transferredIds = $this->getTransferredPatients($branchId, $startDate, $endDate, true); foreach ($transferredIds as $id) { MedicalHistorySnapshot::create([ 'rf_report_id' => $report->report_id, 'rf_medicalhistory_id' => $id, 'patient_type' => 'transferred' ]); } // 5. Умершие $deceasedIds = $this->getDeceasedOutcomePatients($branchId, $startDate, $endDate, false, true); foreach ($deceasedIds as $id) { MedicalHistorySnapshot::create([ 'rf_report_id' => $report->report_id, 'rf_medicalhistory_id' => $id, 'patient_type' => 'deceased' ]); } // 5. Поступившие $recipientIds = $this->getPlanOrEmergencyPatients( null, $isHeadOrAdmin, $branchId, $startDate, $endDate, false, true, true, today: true ); foreach ($recipientIds as $id) { MedicalHistorySnapshot::create([ 'rf_report_id' => $report->report_id, 'rf_medicalhistory_id' => $id, 'patient_type' => 'recipient' ]); } // 6. Находящиеся на лечении // $currentIds = $this->getCurrentPatients($branchId, false, true); // foreach ($currentIds as $id) { // MedicalHistorySnapshot::create([ // 'rf_report_id' => $report->report_id, // 'rf_medicalhistory_id' => $id, // 'patient_type' => 'current' // ]); // } \DB::commit(); return response()->json([ 'message' => 'success' ]); } public function getPatients(Request $request) { $user = Auth::user(); $data = $request->validate([ 'status' => 'required|string', // plan emergency observation deceased 'startAt' => 'nullable', 'endAt' => 'nullable', ]); // Получаем базовые данные $status = $data['status']; $model = new MisMedicalHistory(); $misDepartmentId = $request->user()->department->rf_mis_department_id; $userDepartmentId = $request->user()->department->department_id; $branchId = MisStationarBranch::where('rf_DepartmentID', $misDepartmentId)->value('StationarBranchID'); if (!$branchId) { return response()->json([]); } // Определяем даты в зависимости от роли [$startDate, $endDate] = $this->getDateRangeForRole($user, $data['startAt'] ?? null, $data['endAt'] ?? null); // Для заведующего/админа ищем отчет по endDate (дате просмотра) $reportIds = []; $reports = $this->getReportsForDateRange($user->rf_department_id, $startDate, $endDate); $reportIds = $reports->pluck('report_id')->toArray(); // Определяем, используем ли мы снапшоты $useSnapshots = ($user->isAdmin() || $user->isHeadOfDepartment()) || Carbon::parse($endDate)->isToday() === false; // Обработка каждого статуса if ($useSnapshots) { // Используем снапшоты: получаем ID пациентов, затем данные из реплики $patients = match($status) { 'plan', 'emergency' => $this->getPatientsFromSnapshotsUsingReplica($status, $reportIds, $branchId, $startDate, $endDate), 'observation' => $this->getObservationPatientsFromSnapshotsUsingReplica($userDepartmentId, $reportIds), 'outcome-discharged' => $this->getOutcomePatientsFromSnapshotsUsingReplica('discharged', $reportIds, $branchId, $startDate, $endDate), 'outcome-transferred' => $this->getOutcomePatientsFromSnapshotsUsingReplica('transferred', $reportIds, $branchId, $startDate, $endDate), 'outcome-deceased' => $this->getOutcomePatientsFromSnapshotsUsingReplica('deceased', $reportIds, $branchId, $startDate, $endDate), default => collect() }; } else { // // Используем реплику для врачей или когда нет отчетов $isHeadOrAdmin = $user->isHeadOfDepartment() || $user->isAdmin(); $patients = match($status) { 'plan', 'emergency' => $this->getPlanOrEmergencyPatients($status, $isHeadOrAdmin, $branchId, $startDate, $endDate), 'observation' => $this->getObservationPatients($userDepartmentId), 'outcome-discharged' => $this->getDischargedPatients($branchId, $startDate, $endDate), 'outcome-transferred' => $this->getTransferredPatients($branchId, $startDate, $endDate), 'outcome-deceased' => $this->getDeceasedOutcomePatients($branchId, $startDate, $endDate), default => 0 }; } // Если есть пациенты, добавляем дополнительные данные if ($patients->isNotEmpty()) { $patients = $patients->map(function ($item, $index) use ($branchId, $startDate, $endDate) { $item->num = $index + 1; $item->misStationarBranchId = $branchId; $item->startDate = $startDate; $item->endDate = $endDate; return $item; }); // Загружаем связи $patients->load(['migrations' => function ($query) use ($startDate, $endDate, $branchId) { $query->whereHas('diagnosis', function ($q) { $q->where('rf_DiagnosTypeID', 3); }) ->with('diagnosis.mkb') ->where('rf_StationarBranchID', $branchId); }]); } return response()->json(FormattedPatientResource::collection($patients)); } public function getPatientsCount(Request $request) { $user = Auth::user(); $data = $request->validate([ 'status' => 'required|string', // plan emergency observation deceased 'startAt' => 'nullable', 'endAt' => 'nullable', ]); // Получаем базовые данные $status = $data['status']; $model = new MisMedicalHistory(); $misDepartmentId = $request->user()->department->rf_mis_department_id; $userDepartmentId = $request->user()->department->department_id; $branchId = MisStationarBranch::where('rf_DepartmentID', $misDepartmentId)->value('StationarBranchID'); if (!$branchId) { return response()->json([]); } // Определяем даты в зависимости от роли [$startDate, $endDate] = $this->getDateRangeForRole($user, $data['startAt'] ?? null, $data['endAt'] ?? null); // Для заведующего/админа ищем отчеты по промежутку дат $reportIds = []; $reports = $this->getReportsForDateRange($user->rf_department_id, $startDate, $endDate); $reportIds = $reports->pluck('report_id')->toArray(); // Определяем, используем ли мы снапшоты $useSnapshots = ($user->isAdmin() || $user->isHeadOfDepartment()) || Carbon::parse($endDate)->isToday() === false; if ($useSnapshots) { // Считаем из снапшотов $patientTypeMap = [ 'plan' => 'plan', 'emergency' => 'emergency', 'observation' => 'observation', 'outcome' => null, 'outcome-discharged' => 'discharged', 'outcome-transferred' => 'transferred', 'outcome-deceased' => 'deceased' ]; $patientType = $patientTypeMap[$status] ?? null; if ($status === 'outcome') { // Считаем уникальных пациентов по всем типам исходов $count = MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds) ->whereIn('patient_type', ['discharged', 'transferred', 'deceased']) ->distinct('rf_medicalhistory_id') ->count('rf_medicalhistory_id'); } elseif ($patientType) { $count = MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds) ->where('patient_type', $patientType) ->distinct('rf_medicalhistory_id') ->count('rf_medicalhistory_id'); } else { $count = 0; } } else { // Определяем, является ли пользователь заведующим/администратором $isHeadOrAdmin = $user->isHeadOfDepartment() || $user->isAdmin(); $isOutcomeOrObservation = in_array($status, ['outcome', 'observation']); if ($isOutcomeOrObservation) { switch ($status) { case 'observation': $count = ObservationPatient::where('rf_department_id', $userDepartmentId)->count(); break; case 'outcome': $count = $this->getAllOutcomePatients($branchId, $startDate, $endDate, true); break; } } else { $count = $this->getPlanOrEmergencyPatients($status, $isHeadOrAdmin, $branchId, $startDate, $endDate, true); } } return response()->json($count); } /** * Определить диапазон дат в зависимости от роли */ private function getDateRangeForRole($user, $startAt = null, $endAt = null): array { // Функция для парсинга даты $parseDate = function($dateInput) { if (is_numeric($dateInput)) { return Carbon::createFromTimestampMs($dateInput) ->setTimezone('Asia/Yakutsk'); } return Carbon::parse($dateInput, 'Asia/Yakutsk'); }; // Если переданы обе даты (и заведующий, и врач могут выбрать даты) if ($startAt && $endAt) { $startDate = $parseDate($startAt); $endDate = $parseDate($endAt); if ($startDate->isSameDay($endDate)) { // Сдвигаем начало на день назад для всех ролей при выборе одного дня // И всегда для врача $startDate = $startDate->copy()->addDays(-1)->setTime(6, 0)->format('Y-m-d H:i:s'); $endDate = $endDate->setTime(6, 0)->format('Y-m-d H:i:s'); } else { // Для диапазона оставляем как есть (только для заведующих) $startDate = $startDate->setTime(6, 0)->format('Y-m-d H:i:s'); $endDate = $endDate->setTime(6, 0)->format('Y-m-d H:i:s'); } } // Если даты не переданы - логика по умолчанию в зависимости от роли else { // Для заведующего или администратора - сутки if ($user->isHeadOfDepartment() || $user->isAdmin()) { $startDate = Carbon::now('Asia/Yakutsk') ->subDay() ->setTime(6, 0) ->format('Y-m-d H:i:s'); $endDate = Carbon::now('Asia/Yakutsk') ->setTime(6, 0) ->format('Y-m-d H:i:s'); } // Для врача - только сутки (вчера 06:00 - сегодня 06:00) else { $startDate = Carbon::now('Asia/Yakutsk')->subDay()->setTime(6, 0)->format('Y-m-d H:i:s'); $endDate = Carbon::now('Asia/Yakutsk')->setTime(6, 0)->format('Y-m-d H:i:s'); } } return [$startDate, $endDate]; } /** * Получить пациентов (плановых или экстренных) */ private function getPlanOrEmergencyPatients( ?string $status, bool $isHeadOrAdmin, $branchId, $startDate, $endDate, bool $returnedCount = false, bool $all = false, bool $onlyIds = false, bool $today = false ) { // Определяем, является ли статус outcome $isOutcomeStatus = in_array($status, ['outcome-transferred', 'outcome-discharged', 'outcome-deceased']); if ($isOutcomeStatus) { switch ($status) { case 'outcome-transferred': $query = MisMigrationPatient::transferred($branchId, $startDate, $endDate); break; case 'outcome-discharged': $query = MisMigrationPatient::discharged($branchId, $startDate, $endDate); break; case 'outcome-deceased': $query = MisMigrationPatient::deceasedOutcome($branchId, $startDate, $endDate); break; } } else { // Разная логика для заведующего и врача if ($isHeadOrAdmin) { // Заведующий: используем whereInDepartment $query = MisMigrationPatient::whereInDepartment($branchId) ->whereBetween('DateIngoing', [$startDate, $endDate]); } else { // Врач: используем currentlyInTreatment + фильтр по дате $query = MisMigrationPatient::currentlyInTreatment($branchId) ->when($today, function ($query) use ($startDate, $endDate) { return $query->whereBetween('DateIngoing', [$startDate, $endDate]); }); } } $medicalHistoryIds = $query->pluck('rf_MedicalHistoryID')->toArray(); if (empty($medicalHistoryIds)) { if ($returnedCount) return 0; return collect(); } // Получаем истории $query = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds) ->with(['surgicalOperations' => function ($query) use ($startDate, $endDate) { $query->whereBetween('Date', [$startDate, $endDate]); }]) ->orderBy('DateRecipient', 'DESC'); // Выбираем план или экстренность if (!$all && !$isOutcomeStatus) { if ($status === 'plan') { $query->plan(); } else if ($status === 'emergency') { $query->emergency(); } } // Для врача добавляем условие "в отделении" if (!$isHeadOrAdmin && !$isOutcomeStatus) { $query->currentlyHospitalized(); } if ($onlyIds) { return $query->select('MedicalHistoryID') ->pluck('MedicalHistoryID')->values(); } else { if ($returnedCount) return $query->count(); else return $query->get(); } } /** * Получить пациентов под наблюдением */ private function getObservationPatients( $userDepartmentId ) { $observationPatients = ObservationPatient::where('rf_department_id', $userDepartmentId) ->select(['rf_medicalhistory_id', 'observation_patient_id']) ->get() ->groupBy('rf_medicalhistory_id'); $medicalHistoryIds = $observationPatients->keys()->toArray(); if (empty($medicalHistoryIds)) { return collect(); } $patients = MisMedicalHistory::whereHas('observationPatient', function ($q) use ($userDepartmentId) { $q->where('rf_department_id', $userDepartmentId); }) ->with(['observationPatient' => function($query) use ($userDepartmentId) { $query->where('rf_department_id', $userDepartmentId) ->select(['rf_medicalhistory_id', 'observation_patient_id', 'comment']); }]) ->orderBy('DateRecipient', 'DESC') ->get(); // Добавляем комментарии $patients = $patients->map(function ($patient) { // Объединяем все комментарии $patient->comment = $patient->observationPatient ->pluck('comment') ->filter() ->implode('; '); return $patient; }); return $patients; } /** * Получить выбывших пациентов */ private function getOutcomePatients( $misDepartmentId, $startDate, $endDate ) { // Здесь оставляем оригинальный запрос, т.к. он специфичен для умерших return MisMedicalHistory::deceased() ->inDepartment($misDepartmentId, $startDate, $endDate) ->get(); } /** * Получить всех выбывших пациентов */ private function getAllOutcomePatients($branchId, $startDate, $endDate, bool $returnedCount = false) { // Сначала получаем миграции с типами выбытия $migrations = MisMigrationPatient::outcomePatients($branchId, $startDate, $endDate) ->select('rf_MedicalHistoryID', 'rf_kl_VisitResultID', 'DateOut') ->get() ->groupBy('rf_MedicalHistoryID'); if ($migrations->isEmpty()) { if ($returnedCount) return 0; return collect(); } $medicalHistoryIds = $migrations->keys()->toArray(); // Получаем истории $patients = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds) ->with(['surgicalOperations']) ->orderBy('DateRecipient', 'DESC'); if ($returnedCount) return $patients->count(); else $patients = $patients->get(); // Добавляем информацию о типе выбытия return $patients->map(function ($patient) use ($migrations) { $patientMigrations = $migrations->get($patient->MedicalHistoryID, collect()); // Определяем основной тип выбытия (берем последнюю миграцию) $latestMigration = $patientMigrations->sortByDesc('DateOut')->first(); if ($latestMigration) { $patient->outcome_type = $this->getOutcomeTypeName($latestMigration->rf_kl_VisitResultID); $patient->outcome_date = $latestMigration->DateOut; $patient->visit_result_id = $latestMigration->rf_kl_VisitResultID; } return $patient; }); } /** * Получить понятное название типа выбытия */ private function getOutcomeTypeName($visitResultId): string { return match($visitResultId) { 1, 7, 8, 9, 10, 11, 48, 49, 124 => 'Выписка', 2, 3, 4, 12, 13, 14 => 'Перевод', 5, 6, 15, 16 => 'Умер', // Добавьте другие коды по мере необходимости default => 'Другое (' . $visitResultId . ')' }; } /** * Получить выписанных пациентов */ private function getDischargedPatients($branchId, $startDate, $endDate, bool $onlyIds = false) { $medicalHistoryIds = MisMigrationPatient::discharged($branchId, $startDate, $endDate) ->pluck('rf_MedicalHistoryID') ->unique() ->toArray(); if (empty($medicalHistoryIds)) { return collect(); } if ($onlyIds) { return $medicalHistoryIds; } return MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds) ->with(['surgicalOperations']) ->orderBy('DateRecipient', 'DESC') ->get(); } /** * Получить переведенных пациентов */ private function getTransferredPatients($branchId, $startDate, $endDate, bool $onlyIds = false) { $medicalHistoryIds = MisMigrationPatient::transferred($branchId, $startDate, $endDate) ->pluck('rf_MedicalHistoryID') ->unique() ->toArray(); if (empty($medicalHistoryIds)) { return collect(); } if ($onlyIds) { return $medicalHistoryIds; } return MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds) ->with(['surgicalOperations']) ->orderBy('DateRecipient', 'DESC') ->get(); } /** * Получить умерших пациентов (исход) */ private function getDeceasedOutcomePatients($branchId, $startDate, $endDate, bool $returnedCount = false, bool $onlyIds = false) { $medicalHistoryIds = MisMigrationPatient::deceasedOutcome($branchId, $startDate, $endDate) ->pluck('rf_MedicalHistoryID') ->unique() ->toArray(); if (empty($medicalHistoryIds)) { if ($returnedCount) return 0; return collect(); } if ($onlyIds) { return $medicalHistoryIds; } $query = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds) ->with(['surgicalOperations']) ->orderBy('DateRecipient', 'DESC'); if ($returnedCount) return $query->count(); else return $query->get(); } /** * Получить пациентов с операциями */ private function getSurgicalPatients(string $status, bool $isHeadOrAdmin, $branchId, $startDate, $endDate, bool $returnedCount = false) { $query = MisSurgicalOperation::where('rf_StationarBranchID', $branchId) ->whereBetween('Date', [$startDate, $endDate]) ->orderBy('Date', 'DESC'); if ($status === 'plan') { $query->where('rf_TypeSurgOperationInTimeID', 6); } else { $query->whereIn('rf_TypeSurgOperationInTimeID', [4, 5]); } if ($returnedCount) return $query->count(); else return $query->get(); } /** * Находятся на лечении */ private function getCurrentPatients($branchId, bool $returnedCount = false, bool $onlyIds = false) { // $currentCount = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds) // ->currentlyHospitalized() // ->orderBy('DateRecipient', 'DESC') // ->count(); $medicalHistoryIds = MisMigrationPatient::currentlyInTreatment($branchId) ->pluck('rf_MedicalHistoryID') ->unique() ->toArray(); if (empty($medicalHistoryIds)) { if ($returnedCount) return 0; return collect(); } if ($onlyIds) return $medicalHistoryIds; $patients = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds) ->currentlyHospitalized() ->with(['surgicalOperations']) ->orderBy('DateRecipient', 'DESC'); if ($returnedCount) return $patients->count(); else return $patients->get(); } public function removeObservation( Request $request, ) { $data = $request->validate([ 'id' => 'required' ]); ObservationPatient::where('rf_medicalhistory_id', $data['id'])->delete(); return response()->json()->setStatusCode(200); } // api/report/unwanted-event public function removeUnwantedEvent(UnwantedEvent $unwantedEvent, Request $request) { $unwantedEvent->delete(); return response()->json()->setStatusCode(200); } public function getDepartmentUsers(Request $request) { $user = Auth::user(); $departmentId = $user->department->rf_mis_department_id; $users = MisLpuDoctor::select(['LPUDoctorID', 'FAM_V', 'IM_V', 'OT_V']) ->active() ->inMyDepartment() ->get(); return response()->json([ ...$users ])->setStatusCode(200); } /** * Получить все отчеты в промежутке дат (для агрегации данных) */ private function getReportsForDateRange($departmentId, $startDate, $endDate) { if (Carbon::parse($startDate)->diffInDays(Carbon::parse($endDate)) > 1.0) return Report::where('rf_department_id', $departmentId) ->whereBetween('created_at', [$startDate, $endDate]) ->orderBy('created_at', 'ASC') ->get(); else return Report::where('rf_department_id', $departmentId) ->whereDate('created_at', $endDate) ->orderBy('created_at', 'ASC') ->get(); } /** * Получить плановых/экстренных пациентов из снапшотов через реплику */ private function getPatientsFromSnapshotsUsingReplica(string $status, array $reportIds, $branchId, $startDate, $endDate) { // Получаем ID пациентов из снапшотов $patientType = $status === 'plan' ? 'plan' : 'emergency'; $medicalHistoryIds = MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds) ->where('patient_type', $patientType) ->pluck('rf_medicalhistory_id') ->unique() ->toArray(); if (empty($medicalHistoryIds)) { return collect(); } // Используем существующие скоупы для получения данных из реплики $query = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds) ->with(['surgicalOperations' => function ($query) use ($startDate, $endDate) { $query->whereBetween('Date', [$startDate, $endDate]); }]) ->orderBy('DateRecipient', 'DESC'); // Применяем те же фильтры if ($status === 'plan') { $query->plan(); } else { $query->emergency(); } return $query->get(); } /** * Получить пациентов под наблюдением из снапшотов через реплику */ private function getObservationPatientsFromSnapshotsUsingReplica($userDepartmentId, array $reportIds) { // Получаем ID пациентов из снапшотов наблюдения $medicalHistoryIds = ObservationPatient::whereIn('rf_report_id', $reportIds) ->pluck('rf_medicalhistory_id') ->unique() ->toArray(); if (empty($medicalHistoryIds)) { return collect(); } // Используем существующий метод с фильтрацией по ID из снапшотов $patients = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds) ->whereHas('observationPatient', function ($q) use ($userDepartmentId) { $q->where('rf_department_id', $userDepartmentId); }) ->with(['observationPatient' => function($query) use ($userDepartmentId) { $query->where('rf_department_id', $userDepartmentId) ->select(['rf_medicalhistory_id', 'observation_patient_id', 'comment']); }]) ->orderBy('DateRecipient', 'DESC') ->get(); // Добавляем комментарии $patients = $patients->map(function ($patient) { $patient->comment = $patient->observationPatient ->pluck('comment') ->filter() ->implode('; '); return $patient; }); return $patients; } /** * Получить пациентов с исходом из снапшотов через реплику */ private function getOutcomePatientsFromSnapshotsUsingReplica(string $outcomeType, array $reportIds, $branchId, $startDate, $endDate) { // Получаем ID пациентов из снапшотов исходов $medicalHistoryIds = MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds) ->where('patient_type', $outcomeType) ->pluck('rf_medicalhistory_id') ->unique() ->toArray(); if (empty($medicalHistoryIds)) { return collect(); } // Используем соответствующий метод для получения данных из реплики return match($outcomeType) { 'discharged' => $this->getDischargedPatientsByIds($medicalHistoryIds, $branchId), 'transferred' => $this->getTransferredPatientsByIds($medicalHistoryIds, $branchId), 'deceased' => $this->getDeceasedPatientsByIds($medicalHistoryIds, $branchId), default => collect() }; } /** * Получить выписанных пациентов по ID из снапшотов */ private function getDischargedPatientsByIds(array $medicalHistoryIds, $branchId) { return MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds) ->with(['surgicalOperations']) ->orderBy('DateRecipient', 'DESC') ->get() ->map(function ($patient) use ($branchId) { // Получаем информацию о выписке $migration = $patient->migrations ->where('rf_kl_VisitResultID', 'in', [1, 7, 8, 9, 10, 11, 48, 49, 124]) ->whereNotNull('DateOut') ->sortByDesc('DateOut') ->first(); if ($migration) { $patient->outcome_type = $this->getOutcomeTypeName($migration->rf_kl_VisitResultID); $patient->outcome_date = $migration->DateOut; $patient->visit_result_id = $migration->rf_kl_VisitResultID; } return $patient; }); } /** * Получить переведенных пациентов по ID из снапшотов */ private function getTransferredPatientsByIds(array $medicalHistoryIds, $branchId) { return MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds) ->with(['surgicalOperations']) ->orderBy('DateRecipient', 'DESC') ->get() ->map(function ($patient) use ($branchId) { // Получаем информацию о переводе $migration = $patient->migrations ->where('rf_kl_VisitResultID', 'in', [2, 3, 4, 12, 13, 14]) ->whereNotNull('DateOut') ->sortByDesc('DateOut') ->first(); if ($migration) { $patient->outcome_type = $this->getOutcomeTypeName($migration->rf_kl_VisitResultID); $patient->outcome_date = $migration->DateOut; $patient->visit_result_id = $migration->rf_kl_VisitResultID; } return $patient; }); } /** * Получить умерших пациентов по ID из снапшотов */ private function getDeceasedPatientsByIds(array $medicalHistoryIds, $branchId) { return MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds) ->with(['surgicalOperations']) ->orderBy('DateRecipient', 'DESC') ->get() ->map(function ($patient) use ($branchId) { // Получаем информацию о смерти $migration = $patient->migrations ->where('rf_kl_VisitResultID', 'in', [5, 6, 15, 16]) ->whereNotNull('DateOut') ->sortByDesc('DateOut') ->first(); if ($migration) { $patient->outcome_type = $this->getOutcomeTypeName($migration->rf_kl_VisitResultID); $patient->outcome_date = $migration->DateOut; $patient->visit_result_id = $migration->rf_kl_VisitResultID; } return $patient; }); } }