diff --git a/app/Console/Commands/FillReportsFromDate.php b/app/Console/Commands/FillReportsFromDate.php new file mode 100644 index 0000000..65f43ae --- /dev/null +++ b/app/Console/Commands/FillReportsFromDate.php @@ -0,0 +1,98 @@ +option('date') ?? Carbon::now()->subDays(7)->format('Y-m-d'); + $endDate = $this->option('end-date') ?? Carbon::now()->format('Y-m-d'); + $departmentId = $this->option('department'); + $userId = $this->option('user'); + $force = $this->option('force'); + + $this->info("Заполнение отчетов с {$startDate} по {$endDate}"); + + // Получаем отделения + $departments = $departmentId + ? Department::where('department_id', $departmentId)->get() + : Department::all(); + + if ($departments->isEmpty()) { + $this->error('Отделения не найдены'); + return 1; + } + + $totalReports = 0; + $totalErrors = 0; + + foreach ($departments as $department) { + $this->info("Обработка отделения: {$department->name}"); + + // Получаем пользователей отделения + $users = $userId + ? User::where('id', $userId)->where('rf_department_id', $department->department_id)->get() + : $this->getDepartmentUsers($department); + + if ($users->isEmpty()) { + $this->warn("В отделении {$department->name} нет пользователей"); + continue; + } + + foreach ($users as $user) { + try { + $created = $this->autoReportService->fillReportsForUser( + $user, + $startDate, + $endDate, + $force + ); + + $totalReports += $created; + $this->info("Для пользователя {$user->name} создано {$created} отчетов"); + } catch (\Exception $e) { + $totalErrors++; + $this->error("Ошибка для пользователя {$user->name}: {$e->getMessage()}"); + } + } + } + + $this->info("Готово! Создано отчетов: {$totalReports}, ошибок: {$totalErrors}"); + + return 0; + } + + private function getDepartmentUsers(Department $department) + { + // Получаем пользователей, которые могут создавать отчеты + return User::where('rf_department_id', $department->department_id) + ->where(function ($query) { + $query->where('role', 'doctor') + ->orWhere('role', 'head_of_department'); + }) + ->where('is_active', true) + ->get(); + } +} diff --git a/app/Http/Controllers/Api/ReportController.php b/app/Http/Controllers/Api/ReportController.php index 880ab69..3e4993b 100644 --- a/app/Http/Controllers/Api/ReportController.php +++ b/app/Http/Controllers/Api/ReportController.php @@ -185,12 +185,12 @@ class ReportController extends Controller } } - $isRangeOneDay = $this->dateService->isRangeOneDay($startDate, $endDate); + $isRangeOneDay = $this->dateRangeService->isRangeOneDay($startDate, $endDate); $date = $isHeadOrAdmin ? [ - $this->dateService->parseDate($isRangeOneDay ? $endDate : $startDate)->getTimestampMs(), - $this->dateService->parseDate($endDate)->getTimestampMs() - ] : $this->dateService->parseDate($endDate)->getTimestampMs(); + $this->dateRangeService->parseDate($isRangeOneDay ? $endDate : $startDate)->getTimestampMs(), + $this->dateRangeService->parseDate($endDate)->getTimestampMs() + ] : $this->dateRangeService->parseDate($endDate)->getTimestampMs(); return response()->json([ 'department' => [ @@ -285,72 +285,6 @@ class ReportController extends Controller 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 поступивших пациентов из снапшотов */ @@ -365,33 +299,6 @@ class ReportController extends Controller 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(); @@ -408,7 +315,8 @@ class ReportController extends Controller 'observationPatients' => 'nullable', 'departmentId' => 'required|integer', 'unwantedEvents' => 'nullable|array', - 'dates' => 'required|array', + 'startAt' => 'required|integer', + 'endAt' => 'required|integer', 'userId' => 'required|integer', 'reportId' => 'nullable' ]); @@ -417,7 +325,7 @@ class ReportController extends Controller $unwantedEvents = $data['unwantedEvents']; // Определяем даты в зависимости от роли - [$startDate, $endDate] = $this->getDateRangeForRole($user, $data['dates'][0], $data['dates'][1]); + $dateRange = $this->dateRangeService->getDateRangeFromRequest($request, $user); $metriks = []; foreach ($metrics as $key => $value) { @@ -429,6 +337,57 @@ class ReportController extends Controller $metriks[] = $metrika; } + // 1. Плановые + $planIds = $this->reportService->getPatientsByStatus( + $user, + 'plan', + $dateRange, + true, + true, + ); + $planCount = $this->reportService->getPatientsCountByStatus($user, 'plan', $dateRange); + // 2. Экстренные + $emergencyIds = $this->reportService->getPatientsByStatus( + $user, + 'emergency', + $dateRange, + true, + true, + ); + $emergencyCount = $this->reportService->getPatientsCountByStatus($user, 'emergency', $dateRange); + // 3. Выписанные + $dischargedIds = $this->reportService->getPatientsByStatus( + $user, + 'outcome', + $dateRange, + true, + true + ); + // 4. Переведенные + $transferredIds = $this->reportService->getPatientsByStatus( + $user, + 'outcome-transferred', + $dateRange, + true, + true + ); + // 5. Умершие + $deceasedIds = $this->reportService->getPatientsByStatus( + $user, + 'outcome-deceased', + $dateRange, + true, + true + ); + // 6. Поступившие + $recipientIds = $this->reportService->getPatientsByStatus( + $user, + 'recipient', + $dateRange, + true, + true + ); + \DB::beginTransaction(); if (isset($data['reportId']) && $data['reportId']) { @@ -483,6 +442,17 @@ class ReportController extends Controller $unwantedEvent->delete(); } } + MetrikaResult::updateOrCreate( + [ + 'rf_report_id' => $report->report_id, + 'rf_metrika_item_id' => 16 + ], + [ + 'rf_report_id' => $report->report_id, + 'rf_metrika_item_id' => 16, + 'value' => count($unwantedEvents), + ] + ); foreach ($metriks as $metrika) { MetrikaResult::updateOrCreate( @@ -519,11 +489,21 @@ class ReportController extends Controller $observationPatient->delete(); } } + MetrikaResult::updateOrCreate( + [ + 'rf_report_id' => $report->report_id, + 'rf_metrika_item_id' => 14 + ], + [ + 'rf_report_id' => $report->report_id, + 'rf_metrika_item_id' => 14, + 'value' => count($observationPatients), + ] + ); // Сохраняем снимок для каждого типа пациентов - - // 1. Плановые - $planIds = $this->getPlanOrEmergencyPatients('plan', false, $branchId, $startDate, $endDate, false, false, true); + // Планово + //$this->getPlanOrEmergencyPatients('plan', false, $branchId, $dateRange->startSql(), $dateRange->endSql(), false, false, true); foreach ($planIds as $id) { MedicalHistorySnapshot::create([ 'rf_report_id' => $report->report_id, @@ -531,9 +511,20 @@ class ReportController extends Controller 'patient_type' => 'plan' ]); } + MetrikaResult::updateOrCreate( + [ + 'rf_report_id' => $report->report_id, + 'rf_metrika_item_id' => 4 + ], + [ + 'rf_report_id' => $report->report_id, + 'rf_metrika_item_id' => 4, + 'value' => $planCount, + ] + ); - // 2. Экстренные - $emergencyIds = $this->getPlanOrEmergencyPatients('emergency', false, $branchId, $startDate, $endDate, false, false, true); + //$this->getPlanOrEmergencyPatients('emergency', false, $branchId, $startDate, $endDate, false, false, true); + // Экстренно foreach ($emergencyIds as $id) { MedicalHistorySnapshot::create([ 'rf_report_id' => $report->report_id, @@ -541,9 +532,19 @@ class ReportController extends Controller 'patient_type' => 'emergency' ]); } + MetrikaResult::updateOrCreate( + [ + 'rf_report_id' => $report->report_id, + 'rf_metrika_item_id' => 12 + ], + [ + 'rf_report_id' => $report->report_id, + 'rf_metrika_item_id' => 12, + 'value' => $emergencyCount, + ] + ); - // 3. Выписанные - $dischargedIds = $this->getDischargedPatients($branchId, $startDate, $endDate, true); + //$this->getDischargedPatients($branchId, $startDate, $endDate, true); foreach ($dischargedIds as $id) { MedicalHistorySnapshot::create([ 'rf_report_id' => $report->report_id, @@ -551,9 +552,19 @@ class ReportController extends Controller 'patient_type' => 'discharged' ]); } + MetrikaResult::updateOrCreate( + [ + 'rf_report_id' => $report->report_id, + 'rf_metrika_item_id' => 15, + ], + [ + 'rf_report_id' => $report->report_id, + 'rf_metrika_item_id' => 15, + 'value' => count($dischargedIds), + ] + ); - // 4. Переведенные - $transferredIds = $this->getTransferredPatients($branchId, $startDate, $endDate, true); + //$this->getTransferredPatients($branchId, $startDate, $endDate, true); foreach ($transferredIds as $id) { MedicalHistorySnapshot::create([ 'rf_report_id' => $report->report_id, @@ -561,9 +572,19 @@ class ReportController extends Controller 'patient_type' => 'transferred' ]); } + MetrikaResult::updateOrCreate( + [ + 'rf_report_id' => $report->report_id, + 'rf_metrika_item_id' => 13, + ], + [ + 'rf_report_id' => $report->report_id, + 'rf_metrika_item_id' => 13, + 'value' => count($transferredIds), + ] + ); - // 5. Умершие - $deceasedIds = $this->getDeceasedOutcomePatients($branchId, $startDate, $endDate, false, true); + //$this->getDeceasedOutcomePatients($branchId, $startDate, $endDate, false, true); foreach ($deceasedIds as $id) { MedicalHistorySnapshot::create([ 'rf_report_id' => $report->report_id, @@ -571,19 +592,29 @@ class ReportController extends Controller 'patient_type' => 'deceased' ]); } - - // 5. Поступившие - $recipientIds = $this->getPlanOrEmergencyPatients( - null, - $isHeadOrAdmin, - $branchId, - $startDate, - $endDate, - false, - true, - true, - today: true + MetrikaResult::updateOrCreate( + [ + 'rf_report_id' => $report->report_id, + 'rf_metrika_item_id' => 9, + ], + [ + 'rf_report_id' => $report->report_id, + 'rf_metrika_item_id' => 9, + 'value' => count($deceasedIds), + ] ); + +// $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, @@ -591,8 +622,19 @@ class ReportController extends Controller 'patient_type' => 'recipient' ]); } + MetrikaResult::updateOrCreate( + [ + 'rf_report_id' => $report->report_id, + 'rf_metrika_item_id' => 3, + ], + [ + 'rf_report_id' => $report->report_id, + 'rf_metrika_item_id' => 3, + 'value' => count($recipientIds), + ] + ); - // 6. Находящиеся на лечении + // 7. Находящиеся на лечении // $currentIds = $this->getCurrentPatients($branchId, false, true); // foreach ($currentIds as $id) { // MedicalHistorySnapshot::create([ @@ -651,59 +693,6 @@ class ReportController extends Controller 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]; - } - /** * Получить пациентов (плановых или экстренных) */ @@ -784,61 +773,6 @@ class ReportController extends Controller } } - /** - * Получить пациентов под наблюдением - */ - 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(); - } - /** * Получить всех выбывших пациентов */ @@ -896,54 +830,6 @@ class ReportController extends Controller }; } - /** - * Получить выписанных пациентов - */ - 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(); - } - /** * Получить умерших пациентов (исход) */ @@ -1050,8 +936,12 @@ class ReportController extends Controller $departmentId = $user->department->rf_mis_department_id; $users = MisLpuDoctor::select(['LPUDoctorID', 'FAM_V', 'IM_V', 'OT_V']) + ->whereHas('prvds', function ($query) use ($departmentId) { + $query->where('rf_DepartmentID', $departmentId) + ->whereDate('D_END', '2222-01-01 00:00:00.000000'); + }) ->active() - ->inMyDepartment() + ->whereNotIn('LPUDoctorID', [0, 1]) ->get(); return response()->json([ @@ -1075,184 +965,4 @@ class ReportController extends Controller ->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; - }); - } } diff --git a/app/Http/Controllers/Web/ReportController.php b/app/Http/Controllers/Web/ReportController.php index 5fda37a..f5d8607 100644 --- a/app/Http/Controllers/Web/ReportController.php +++ b/app/Http/Controllers/Web/ReportController.php @@ -68,12 +68,9 @@ class ReportController extends Controller 'reportId' => 'nullable|integer' ]); - $report = $this->reportService->storeReport($validated, Auth::user()); + $report = $this->reportService->storeReport($validated, Auth::user(), false); - return response()->json([ - 'message' => 'success', - 'report_id' => $report->report_id - ]); + return redirect()->route('start'); } public function getPatients(Request $request) diff --git a/app/Http/Controllers/Web/StatisticController.php b/app/Http/Controllers/Web/StatisticController.php index 783dd77..7c37153 100644 --- a/app/Http/Controllers/Web/StatisticController.php +++ b/app/Http/Controllers/Web/StatisticController.php @@ -71,16 +71,16 @@ class StatisticController extends Controller $emergencyCount = 0; $planSurgical = 0; $emergencySurgical = 0; $transferredCount = 0; $deceasedCount = 0; foreach ($reports as $report) { - $allCount += $this->getMetrikaResultFromReport($report, 3, $isRangeOneDay); - $currentCount += $this->getMetrikaResultFromReport($report, 8, false); - $occupiedBeds += $this->getMetrikaResultFromReport($report, 8, $isRangeOneDay); - $planCount += $this->getMetrikaResultFromReport($report, 4, $isRangeOneDay); - $emergencyCount += $this->getMetrikaResultFromReport($report, 12, $isRangeOneDay); - $planSurgical += $this->getMetrikaResultFromReport($report, 11, $isRangeOneDay); - $emergencySurgical += $this->getMetrikaResultFromReport($report, 10, $isRangeOneDay); - $transferredCount += $this->getMetrikaResultFromReport($report, 13, $isRangeOneDay); - $outcomeCount += $this->getMetrikaResultFromReport($report, 7, $isRangeOneDay); - $deceasedCount += $this->getMetrikaResultFromReport($report, 9, $isRangeOneDay); + $allCount += $this->getMetrikaResultFromReport($report, 3, $isRangeOneDay); // Поступило + $currentCount += $this->getMetrikaResultFromReport($report, 8, false); // Состоит + $occupiedBeds += $this->getMetrikaResultFromReport($report, 8, $isRangeOneDay); // Состоит + $planCount += $this->getMetrikaResultFromReport($report, 4, $isRangeOneDay); // Поступление - Планово + $emergencyCount += $this->getMetrikaResultFromReport($report, 12, $isRangeOneDay); // Поступление - Экстренно + $planSurgical += $this->getMetrikaResultFromReport($report, 11, $isRangeOneDay); // Операции - Планово + $emergencySurgical += $this->getMetrikaResultFromReport($report, 10, $isRangeOneDay); // Операции - Экстренно + $transferredCount += $this->getMetrikaResultFromReport($report, 13, $isRangeOneDay); // Поступление - Перевод + $outcomeCount += $this->getMetrikaResultFromReport($report, 7, $isRangeOneDay); // Выбыло + $deceasedCount += $this->getMetrikaResultFromReport($report, 9, $isRangeOneDay); // Умерло } // Независимые метрики (установки по умолчанию и т.п.) diff --git a/app/Models/LifeMisMigrationPatient.php b/app/Models/LifeMisMigrationPatient.php new file mode 100644 index 0000000..8c6f718 --- /dev/null +++ b/app/Models/LifeMisMigrationPatient.php @@ -0,0 +1,194 @@ +hasOne(MisStationarBranch::class, 'StationarBranchID', 'rf_StationarBranchID'); + } + + public function diagnosis() + { + return $this->hasMany(MisDiagnos::class, 'rf_MigrationPatientID', 'MigrationPatientID'); + } + + public function mkb() + { + return $this->hasOne(MisMKB::class, 'MKBID', 'rf_MKBID'); + } + + public function medicalHistory() + { + return $this->belongsTo(MisMedicalHistory::class, 'rf_MedicalHistoryID', 'MedicalHistoryID'); + } + + /** + * Находятся на лечении + */ + public function scopeCurrentlyInTreatment($query, $branchId = null, DateRange $dateRange = null) + { + $query->where('rf_kl_VisitResultID', 0) + ->where('rf_kl_StatCureResultID', 0) + ->whereBetween('DateIngoing', [$dateRange->startSql(), $dateRange->endSql()]) + ->whereHas('medicalHistory', function ($query) use ($branchId, $dateRange) { + $query->whereDate('DateExtract', '1900-01-01'); + }) + ->where('rf_MedicalHistoryID', '<>', 0); + + if ($branchId) { + $query->where('rf_StationarBranchID', $branchId); + } + + if ($dateRange) { + $query->whereBetween('DateIngoing', [$dateRange->startSql(), $dateRange->endSql()]); + } + + return $query; + } + + public function scopeWhereInDepartment($query, $branchId = null) + { + $query->where('rf_MedicalHistoryID', '<>', 0); + + if ($branchId) { + $query->where('rf_StationarBranchID', $branchId); + } + + return $query; + } + + /** + * Выбывшие пациенты (все исходы) + */ + public function scopeOutcomePatients($query, $branchId = null, DateRange $dateRange = null) + { + $query->where('rf_kl_VisitResultID', '<>', 0) // не активное лечение + ->whereDate('DateOut', '<>', '1900-01-01') // есть дата выбытия + ->where('rf_MedicalHistoryID', '<>', 0); + + if ($branchId) { + $query->where('rf_StationarBranchID', $branchId); + } + + if ($dateRange) { + $query->whereBetween('DateOut', [$dateRange->startSql(), $dateRange->endSql()]); + } + + return $query; + } + + /** + * Выписанные пациенты + */ + public function scopeOutcomeDischarged($query, $branchId = null, DateRange $dateRange = null) + { + // ID выписки + $dischargeCodes = [1, 7, 8, 9, 10, 11, 48, 49, 124]; + + $query->whereIn('rf_kl_VisitResultID', $dischargeCodes) + ->whereDate('DateOut', '<>', '1900-01-01') + ->where('rf_MedicalHistoryID', '<>', 0); + + if ($branchId) { + $query->where('rf_StationarBranchID', $branchId); + } + + if ($dateRange) { + $query->whereBetween('DateOut', [$dateRange->startSql(), $dateRange->endSql()]); + } + + return $query; + } + + /** + * Перевод в другое отделение + */ + public function scopeOutcomeTransferred($query, $branchId = null, DateRange $dateRange = null) + { + // ID перевода + $transferCodes = [2, 3, 4, 12, 13, 14]; + + $query->whereIn('rf_kl_VisitResultID', $transferCodes) + ->whereDate('DateOut', '<>', '1900-01-01') + ->where('rf_MedicalHistoryID', '<>', 0); + + if ($branchId) { + $query->where('rf_StationarBranchID', $branchId); + } + + if ($dateRange) { + $query->whereBetween('DateOut', [$dateRange->startSql(), $dateRange->endSql()]); + } + + return $query; + } + + /** + * Умершие пациенты + */ + public function scopeDeceasedOutcome($query, $branchId = null, DateRange $dateRange = null) + { + // ID умершего + $deceasedCodes = [5, 6, 15, 16]; + + $query->whereIn('rf_kl_VisitResultID', $deceasedCodes) + ->whereDate('DateOut', '<>', '1900-01-01') + ->where('rf_MedicalHistoryID', '<>', 0); + + if ($branchId) { + $query->where('rf_StationarBranchID', $branchId); + } + + if ($dateRange) { + $query->whereBetween('DateOut', [$dateRange->startSql(), $dateRange->endSql()]); + } + + return $query; + } + + public function scopeOutcomeWithoutTransferred($query, $branchId = null, DateRange $dateRange = null) + { + // ID выписанных, без переводных + $outcomeWithoutTransferredIds = [5, 6, 15, 16, 1, 7, 8, 9, 10, 11, 48, 49, 124]; + + $query->whereIn('rf_kl_VisitResultID', $outcomeWithoutTransferredIds) + ->whereDate('DateOut', '<>', '1900-01-01') + ->where('rf_MedicalHistoryID', '<>', 0); + + if ($branchId) { + $query->where('rf_StationarBranchID', $branchId); + } + + if ($dateRange) { + $query->whereBetween('DateOut', [$dateRange->startSql(), $dateRange->endSql()]); + } + + return $query; + } + + public function scopeExtractedToday($query, $branchId = null, DateRange $dateRange = null) + { +// if (is_null($startDate)) $startDate = Carbon::now()->addDays(-1)->format('Y-m-d'); +// if (is_null($endDate)) $endDate = Carbon::now()->format('Y-m-d'); + + $query->where('rf_kl_VisitResultID', '<>', 0) + ->where('rf_MedicalHistoryID', '<>', 0) + ->when($dateRange, function($query) use ($dateRange) { + return $query->whereBetween('DateOut', [$dateRange->startSql(), $dateRange->endSql()]); + }); + + if ($branchId) { + $query->where('rf_StationarBranchID', $branchId); + } + + return $query; + } +} diff --git a/app/Models/MisDocPrvd.php b/app/Models/MisDocPrvd.php new file mode 100644 index 0000000..5c90ff7 --- /dev/null +++ b/app/Models/MisDocPrvd.php @@ -0,0 +1,16 @@ +belongsTo(MisLpuDoctor::class, 'rf_LPUDoctorID'); + } +} diff --git a/app/Models/MisLpuDoctor.php b/app/Models/MisLpuDoctor.php index 4527c53..d4e9b09 100644 --- a/app/Models/MisLpuDoctor.php +++ b/app/Models/MisLpuDoctor.php @@ -22,4 +22,9 @@ class MisLpuDoctor extends Model return $query->where('rf_DepartmentID', $department->rf_mis_department_id); } + + public function prvds() + { + return $this->hasMany(MisDocPrvd::class, 'rf_LPUDoctorID', 'LPUDoctorID'); + } } diff --git a/app/Models/MisMigrationPatient.php b/app/Models/MisMigrationPatient.php index 481717f..0a999dd 100644 --- a/app/Models/MisMigrationPatient.php +++ b/app/Models/MisMigrationPatient.php @@ -88,7 +88,7 @@ class MisMigrationPatient extends Model /** * Выписанные пациенты */ - public function scopeDischarged($query, $branchId = null, DateRange $dateRange = null) + public function scopeOutcomeDischarged($query, $branchId = null, DateRange $dateRange = null) { // ID выписки $dischargeCodes = [1, 7, 8, 9, 10, 11, 48, 49, 124]; @@ -111,7 +111,7 @@ class MisMigrationPatient extends Model /** * Перевод в другое отделение */ - public function scopeTransferred($query, $branchId = null, DateRange $dateRange = null) + public function scopeOutcomeTransferred($query, $branchId = null, DateRange $dateRange = null) { // ID перевода $transferCodes = [2, 3, 4, 12, 13, 14]; @@ -154,6 +154,26 @@ class MisMigrationPatient extends Model return $query; } + public function scopeOutcomeWithoutTransferred($query, $branchId = null, DateRange $dateRange = null) + { + // ID выписанных, без переводных + $outcomeWithoutTransferredIds = [5, 6, 15, 16, 1, 7, 8, 9, 10, 11, 48, 49, 124]; + + $query->whereIn('rf_kl_VisitResultID', $outcomeWithoutTransferredIds) + ->whereDate('DateOut', '<>', '1900-01-01') + ->where('rf_MedicalHistoryID', '<>', 0); + + if ($branchId) { + $query->where('rf_StationarBranchID', $branchId); + } + + if ($dateRange) { + $query->whereBetween('DateOut', [$dateRange->startSql(), $dateRange->endSql()]); + } + + return $query; + } + public function scopeExtractedToday($query, $branchId = null, DateRange $dateRange = null) { // if (is_null($startDate)) $startDate = Carbon::now()->addDays(-1)->format('Y-m-d'); diff --git a/app/Services/AutoReportService.php b/app/Services/AutoReportService.php new file mode 100644 index 0000000..f27242d --- /dev/null +++ b/app/Services/AutoReportService.php @@ -0,0 +1,279 @@ +createReportForDate($user, $date, $force); + + if ($reportCreated) { + $createdCount++; + } + } catch (\Exception $e) { + Log::error("Ошибка создания отчета для {$user->id} на {$date->format('Y-m-d')}: {$e->getMessage()}"); + throw $e; // или continue в зависимости от требований + } + } + + return $createdCount; + } + + /** + * Создать отчет для конкретной даты + */ + public function createReportForDate(User $user, Carbon $date, bool $force = false): bool + { + // Проверяем, существует ли уже отчет на эту дату + $existingReport = Report::where('rf_department_id', $user->rf_department_id) + ->whereDate('created_at', $date) + ->whereDate('sent_at', $date) + ->first(); + + if ($existingReport && !$force) { + return false; // Отчет уже существует + } + + // Если есть существующий отчет и force=true - удаляем его + if ($existingReport && $force) { + $existingReport->delete(); + } + + // Создаем DateRange для этой даты + // Приводим к Illuminate\Carbon если нужно + if (!$date instanceof \Illuminate\Support\Carbon) { + $date = \Illuminate\Support\Carbon::instance($date); + } + $dateRange = $this->dateRangeService->createDateRangeForDate($date, $user); + + // Получаем данные для отчета + $reportData = $this->prepareReportData($user, $dateRange, $date); + + // Создаем отчет + DB::transaction(function () use ($user, $reportData, $date) { + $this->reportService->storeReport($reportData, $user); + }); + + return true; + } + + /** + * Подготовить данные для отчета + */ + private function prepareReportData(User $user, DateRange $dateRange, Carbon $date): array + { + $department = $user->department; + $branchId = $this->getBranchId($department->rf_mis_department_id); + $isHeadOrAdmin = $user->isHeadOfDepartment() || $user->isAdmin(); + + // Получаем метрики + $metrics = $this->calculateMetrics( + $user, + $isHeadOrAdmin, + $branchId, + $dateRange + ); + + // Получаем количество коек + $beds = $this->getBedCount($department); + + // Формируем данные отчета + return [ + 'departmentId' => $department->department_id, + 'userId' => $user->rf_lpudoctor_id ?? $user->id, + 'dates' => [ + $dateRange->startTimestamp(), + $dateRange->endTimestamp() + ], + 'sent_at' => $this->dateRangeService->toSqlFormat($date), + 'created_at' => $this->dateRangeService->toSqlFormat($date), + 'metrics' => $this->formatMetrics($metrics), + 'observationPatients' => $this->getObservationPatients($user->rf_department_id, $date), + 'unwantedEvents' => [], + ]; + } + + /** + * Рассчитать метрики для отчета + */ + private function calculateMetrics( + User $user, + bool $isHeadOrAdmin, + int $branchId, + DateRange $dateRange + ): array { + $metrics = []; + + // 1. Плановые пациенты + $metrics['plan'] = $this->patientQueryService->getPlanOrEmergencyPatients( + 'plan', + $isHeadOrAdmin, + $branchId, + $dateRange, + true, + false, + true, + true + ); + + Log::info($this->patientQueryService->getPlanOrEmergencyPatients( + 'plan', + $isHeadOrAdmin, + $branchId, + $dateRange, + false, + false, + true, + true + )); + + // 2. Экстренные пациенты + $metrics['emergency'] = $this->patientQueryService->getPlanOrEmergencyPatients( + 'emergency', + $isHeadOrAdmin, + $branchId, + $dateRange, + true, + false, + true, + true + ); + + // 3. Поступившие сегодня + $metrics['recipient'] = $this->patientQueryService->getPlanOrEmergencyPatients( + null, + $isHeadOrAdmin, + $branchId, + $dateRange, + true, + false, + false, + true + ); + + // 4. Выписанные + $metrics['discharged'] = $this->patientQueryService->getOutcomePatients( + $branchId, + $dateRange, + 'discharged' + )->count(); + + // 5. Переведенные + $metrics['transferred'] = $this->patientQueryService->getOutcomePatients( + $branchId, + $dateRange, + 'transferred' + )->count(); + + // 6. Умершие + $metrics['deceased'] = $this->patientQueryService->getOutcomePatients( + $branchId, + $dateRange, + 'deceased' + )->count(); + + // 7. Текущие пациенты + $metrics['current'] = $this->patientQueryService->getAllPatientsInDepartment( + $isHeadOrAdmin, + $branchId, + $dateRange, + true + ); + + // 8. Плановые операции + $metrics['plan_surgery'] = $this->patientQueryService->getSurgicalPatients( + 'plan', + $branchId, + $dateRange, + true + ); + + // 9. Экстренные операции + $metrics['emergency_surgery'] = $this->patientQueryService->getSurgicalPatients( + 'emergency', + $branchId, + $dateRange, + true + ); + + return $metrics; + } + + /** + * Форматировать метрики для сохранения + */ + private function formatMetrics(array $metrics): array + { + return [ + 'metrika_item_3' => $metrics['plan'] ?? 0, // плановые + 'metrika_item_4' => $metrics['emergency'] ?? 0, // экстренные + 'metrika_item_5' => $metrics['recipient'] ?? 0, // поступившие + 'metrika_item_6' => ($metrics['plan_surgery'] ?? 0) + ($metrics['emergency_surgery'] ?? 0), // всего операций + 'metrika_item_7' => $metrics['discharged'] ?? 0, // выписанные + 'metrika_item_8' => $metrics['current'] ?? 0, // текущие + 'metrika_item_9' => $metrics['deceased'] ?? 0, // умершие + 'metrika_item_10' => $metrics['plan_surgery'] ?? 0, // плановые операции + 'metrika_item_11' => $metrics['emergency_surgery'] ?? 0, // экстренные операции + 'metrika_item_12' => $metrics['transferred'] ?? 0, // переведенные + 'metrika_item_13' => 0, // под наблюдением (будет заполнено отдельно) + ]; + } + + /** + * Получить пациентов под наблюдением на дату + */ + private function getObservationPatients(int $departmentId, Carbon $date): array + { + // Здесь нужно реализовать логику получения пациентов под наблюдением + // на конкретную дату. Возможно, из снапшотов или истории. + return []; // временно возвращаем пустой массив + } + + /** + * Получить ID отделения + */ + private function getBranchId(int $misDepartmentId): ?int + { + return MisStationarBranch::where('rf_DepartmentID', $misDepartmentId) + ->value('StationarBranchID'); + } + + /** + * Получить количество коек + */ + private function getBedCount(Department $department): int + { + $default = $department->metrikaDefault()->where('rf_metrika_item_id', 1)->first(); + return (int)($default->value ?? 0); + } +} diff --git a/app/Services/DateRangeService.php b/app/Services/DateRangeService.php index a85dcdf..49583ce 100644 --- a/app/Services/DateRangeService.php +++ b/app/Services/DateRangeService.php @@ -125,4 +125,20 @@ class DateRangeService { return $this->toCarbon($date)->format('Y-m-d H:i:s'); } + + public function createDateRangeForDate(Carbon $date, User $user): DateRange + { + // Для автоматического заполнения используем логику как для врача + // (вчера 06:00 - сегодня 06:00) + $startDate = $date->copy()->subDay()->setTime(6, 0); + $endDate = $date->copy()->setTime(6, 0); + + return new DateRange( + startDate: $startDate, + endDate: $endDate, + startDateRaw: $startDate->format('Y-m-d H:i:s'), + endDateRaw: $endDate->format('Y-m-d H:i:s'), + isOneDay: true + ); + } } diff --git a/app/Services/PatientService.php b/app/Services/PatientService.php index f84a91b..57e56ea 100644 --- a/app/Services/PatientService.php +++ b/app/Services/PatientService.php @@ -2,6 +2,7 @@ namespace App\Services; +use App\Models\LifeMisMigrationPatient; use App\Models\MisMedicalHistory; use App\Models\MisMigrationPatient; use App\Models\ObservationPatient; @@ -19,17 +20,28 @@ class PatientService DateRange $dateRange, bool $countOnly = false, bool $onlyIds = false, - bool $includeCurrent = false + bool $includeCurrent = false, + bool $fillableAuto = false ) { // Получаем поступивших сегодня - $recipientQuery = $this->buildRecipientQuery($type, $isHeadOrAdmin, $branchId, $dateRange); - $recipientIds = $recipientQuery->pluck('rf_MedicalHistoryID')->toArray(); + $recipientQuery = $this->buildRecipientQuery($type, $isHeadOrAdmin, $branchId, $dateRange, $fillableAuto); + if ($fillableAuto) + $recipientIds = $recipientQuery->distinct()->pluck('rf_MedicalHistoryID')->toArray(); + else + $recipientIds = $recipientQuery->pluck('rf_MedicalHistoryID')->toArray(); // Если нужно добавить уже находящихся в отделении if ($includeCurrent) { - $currentIds = MisMigrationPatient::currentlyInTreatment($branchId) - ->pluck('rf_MedicalHistoryID') - ->toArray(); + if ($fillableAuto) { + $currentIds = LifeMisMigrationPatient::currentlyInTreatment($branchId, $dateRange) + ->distinct() + ->pluck('rf_MedicalHistoryID') + ->toArray(); + } else { + $currentIds = MisMigrationPatient::currentlyInTreatment($branchId) + ->pluck('rf_MedicalHistoryID') + ->toArray(); + } $medicalHistoryIds = array_unique(array_merge($recipientIds, $currentIds)); } else { @@ -38,14 +50,10 @@ class PatientService if (empty($medicalHistoryIds)) { if ($countOnly) return 0; - if ($onlyIds) return []; + if ($onlyIds) return collect(); return collect(); } - if ($onlyIds) { - return $medicalHistoryIds; - } - // Получаем истории $query = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds) ->with([ @@ -53,7 +61,7 @@ class PatientService $q->whereBetween('Date', [$dateRange->startSql(), $dateRange->endSql()]); }, 'migrations' => function ($q) use ($branchId) { - $q->where('rf_StationarBranchID', $branchId) // укажите поле сортировки + $q->where('rf_StationarBranchID', $branchId) ->take(1) // берем только одну последнюю ->with(['diagnosis' => function ($q) { $q->where('rf_DiagnosTypeID', 3) @@ -75,6 +83,10 @@ class PatientService return $query->count(); } + if ($onlyIds) { + return $query->pluck('MedicalHistoryID'); + } + return $query->get()->map(function ($patient) use ($recipientIds, $branchId) { // Добавляем флаг "поступил сегодня" $patient->is_recipient_today = in_array($patient->MedicalHistoryID, $recipientIds); @@ -89,7 +101,8 @@ class PatientService bool $isHeadOrAdmin, int $branchId, DateRange $dateRange, - bool $countOnly = false + bool $countOnly = false, + bool $onlyIds = false ) { // Поступившие сегодня $recipientIds = $this->buildRecipientQuery(null, $isHeadOrAdmin, $branchId, $dateRange) @@ -113,9 +126,13 @@ class PatientService return count($allIds); } + if ($onlyIds) { + return collect($allIds); + } + return MisMedicalHistory::whereIn('MedicalHistoryID', $allIds) - ->with(['surgicalOperations' => function ($q) use ($startDate, $endDate) { - $q->whereBetween('Date', [$startDate, $endDate]); + ->with(['surgicalOperations' => function ($q) use ($dateRange) { + $q->whereBetween('Date', [$dateRange->startSql(), $dateRange->endSql()]); }]) ->orderBy('DateRecipient', 'DESC') ->get() @@ -128,17 +145,22 @@ class PatientService /** * Получить пациентов под наблюдением */ - public function getObservationPatients(int $departmentId) + public function getObservationPatients(int $departmentId, bool $onlyIds = false) { - $patients = MisMedicalHistory::whereHas('observationPatient', function ($q) use ($departmentId) { + $query = MisMedicalHistory::whereHas('observationPatient', function ($q) use ($departmentId) { $q->where('rf_department_id', $departmentId); - }) - ->with(['observationPatient' => function($query) use ($departmentId) { + }); + + if (!$onlyIds) { + $query->with(['observationPatient' => function ($query) use ($departmentId) { $query->where('rf_department_id', $departmentId) ->select(['rf_medicalhistory_id', 'observation_patient_id', 'comment']); }]) - ->orderBy('DateRecipient', 'DESC') - ->get(); + ->orderBy('DateRecipient', 'DESC'); + } + + if ($onlyIds) $patients = $query->pluck('MedicalHistoryID'); + else $patients = $query->get(); return $patients->map(function ($patient) { $patient->comment = $patient->observationPatient @@ -155,11 +177,13 @@ class PatientService public function getOutcomePatients( int $branchId, DateRange $dateRange, - string $outcomeType = 'all' + string $outcomeType = 'all', + bool $onlyIds = false ) { $methodMap = [ - 'discharged' => 'discharged', - 'transferred' => 'transferred', + 'discharged' => 'outcomeDischarged', + 'transferred' => 'outcomeTransferred', + 'without-transferred' => 'outcomeWithoutTransferred', 'deceased' => 'deceasedOutcome', 'all' => 'outcomePatients', ]; @@ -175,6 +199,8 @@ class PatientService return collect(); } + if ($onlyIds) return collect($medicalHistoryIds); + return MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds) ->with(['surgicalOperations']) ->orderBy('DateRecipient', 'DESC') @@ -282,17 +308,28 @@ class PatientService ?string $type, bool $isHeadOrAdmin, int $branchId, - DateRange $dateRange + DateRange $dateRange, + bool $fillableAuto = false ) { // Разная логика для заведующего и врача if ($isHeadOrAdmin) { // Заведующий: все поступившие за период - $query = MisMigrationPatient::whereInDepartment($branchId) - ->whereBetween('DateIngoing', [$dateRange->startSql(), $dateRange->endSql()]); + if ($fillableAuto) { + $query = LifeMisMigrationPatient::whereInDepartment($branchId) + ->whereBetween('DateIngoing', [$dateRange->startSql(), $dateRange->endSql()]); + } else { + $query = MisMigrationPatient::whereInDepartment($branchId) + ->whereBetween('DateIngoing', [$dateRange->startSql(), $dateRange->endSql()]); + } } else { // Врач: только поступившие за сутки - $query = MisMigrationPatient::whereInDepartment($branchId) - ->whereBetween('DateIngoing', [$dateRange->startSql(), $dateRange->endSql()]); + if ($fillableAuto) { + $query = LifeMisMigrationPatient::whereInDepartment($branchId) + ->whereBetween('DateIngoing', [$dateRange->startSql(), $dateRange->endSql()]); + } else { + $query = MisMigrationPatient::whereInDepartment($branchId) + ->whereBetween('DateIngoing', [$dateRange->startSql(), $dateRange->endSql()]); + }; } return $query; diff --git a/app/Services/ReportService.php b/app/Services/ReportService.php index d08d7ef..4f5f202 100644 --- a/app/Services/ReportService.php +++ b/app/Services/ReportService.php @@ -2,6 +2,7 @@ namespace App\Services; +use App\Models\Department; use App\Models\MedicalHistorySnapshot; use App\Models\MetrikaResult; use App\Models\MisLpuDoctor; @@ -46,7 +47,7 @@ class ReportService /** * Создать или обновить отчет */ - public function storeReport(array $data, User $user): Report + public function storeReport(array $data, User $user, $fillableAuto = false): Report { DB::beginTransaction(); @@ -58,7 +59,7 @@ class ReportService $this->saveObservationPatients($report, $data['observationPatients'] ?? [], $user->rf_department_id); // Сохраняем снапшоты пациентов - $this->snapshotService->createPatientSnapshots($report, $user, $data['dates']); + $this->snapshotService->createPatientSnapshots($report, $user, $data['dates'], $fillableAuto); DB::commit(); return $report; @@ -74,17 +75,20 @@ class ReportService public function getPatientsByStatus( User $user, string $status, - DateRange $dateRange + DateRange $dateRange, + bool $onlyIds = false, + bool $beforeCreate = false, + ?bool $includeCurrentPatients = null ) { $branchId = $this->getBranchId($user->department->rf_mis_department_id); - $useSnapshots = $this->shouldUseSnapshots($user, $dateRange); + $useSnapshots = $this->shouldUseSnapshots($user, $dateRange, $beforeCreate); if ($useSnapshots) { return $this->getPatientsFromSnapshots($user, $status, $dateRange, $branchId); } - return $this->getPatientsFromReplica($user, $status, $dateRange, $branchId); + return $this->getPatientsFromReplica($user, $status, $dateRange, $branchId, $onlyIds, $includeCurrentPatients); } /** @@ -118,9 +122,9 @@ class ReportService /** * Определить, нужно ли использовать снапшоты */ - private function shouldUseSnapshots(User $user, DateRange $dateRange): bool + private function shouldUseSnapshots(User $user, DateRange $dateRange, bool $beforeCreate = false): bool { - if ($user->isAdmin() || $user->isHeadOfDepartment()) { + if (($user->isAdmin() || $user->isHeadOfDepartment()) && !$beforeCreate) { return true; } @@ -141,7 +145,8 @@ class ReportService 'rf_department_id' => $data['departmentId'], 'rf_user_id' => $user->id, 'rf_lpudoctor_id' => $data['userId'], - 'sent_at' => now(), + 'sent_at' => $data['sent_at'] ?? $this->dateRangeService->toSqlFormat(\Illuminate\Support\Carbon::now()), + 'created_at' => $data['created_at'] ?? $this->dateRangeService->toSqlFormat(\Illuminate\Support\Carbon::now()), ]; if (isset($data['reportId']) && $data['reportId']) { @@ -151,6 +156,13 @@ class ReportService ); } else { $report = Report::create($reportData); + $department = Department::where('department_id', $reportData['rf_department_id'])->first(); + $beds = $department->metrikaDefault->where('rf_metrika_item_id', 1)->first(); + MetrikaResult::create([ + 'rf_report_id' => $report->report_id, + 'rf_metrika_item_id' => 1, + 'value' => $beds->value + ]); } return $report; @@ -265,7 +277,7 @@ class ReportService $isActiveSendButton = $this->isSendButtonActive($user, $dateRange, $reportToday, $fillableUserId); $message = null; - if ($reportToday && $reportToday->rf_lpudoctor_id !== intval($fillableUserId)) { + if ($reportToday) { $reportDoctor = $reportToday->lpuDoctor; $message = "Отчет уже создан пользователем: $reportDoctor->FAM_V $reportDoctor->IM_V $reportDoctor->OT_V"; } @@ -329,9 +341,11 @@ class ReportService 'emergency' => $this->getMetrikaResultCount(12, $reportIds), 'outcome' => $this->getMetrikaResultCount(7, $reportIds), 'deceased' => $this->getMetrikaResultCount(9, $reportIds), + 'current' => $this->getMetrikaResultCount(8, $reportIds), // 'discharged' => $this->getMetrikaResultCount('discharged', $reportIds), 'transferred' => $this->getMetrikaResultCount(13, $reportIds), 'recipient' => $this->getMetrikaResultCount(3, $reportIds), + 'beds' => $this->getMetrikaResultCount(1, $reportIds) ]; // Получаем ID поступивших пациентов @@ -350,10 +364,11 @@ class ReportService return [ 'recipientCount' => $snapshotStats['recipient'] ?? 0, 'extractCount' => $snapshotStats['outcome'] ?? 0, - 'currentCount' => $this->calculateCurrentPatientsFromSnapshots($reportIds, $branchId), + 'currentCount' => $snapshotStats['current'] ?? 0,//$this->calculateCurrentPatientsFromSnapshots($reportIds, $branchId), 'deadCount' => $snapshotStats['deceased'] ?? 0, 'surgicalCount' => $surgicalCount, 'recipientIds' => $recipientIds, + 'beds' => $snapshotStats['beds'] ?? 0 ]; } @@ -440,6 +455,10 @@ class ReportService false // только поступившие сегодня ); + $misBranch = MisStationarBranch::where('StationarBranchID', $branchId)->first(); + $beds = Department::where('rf_mis_department_id', $misBranch->rf_DepartmentID) + ->first()->metrikaDefault->where('rf_metrika_item_id', 1)->first(); + return [ 'recipientCount' => $recipientCount, // только поступившие сегодня 'extractCount' => $outcomeCount, @@ -449,14 +468,20 @@ class ReportService 'recipientIds' => $recipientIds, // ID поступивших сегодня 'planCount' => $planCount, // плановые (поступившие + уже лечащиеся) 'emergencyCount' => $emergencyCount, // экстренные (поступившие + уже лечащиеся) + 'beds' => $beds->value ]; } /** * Получить пациентов из снапшотов */ - private function getPatientsFromSnapshots(User $user, string $status, DateRange $dateRange, int $branchId) - { + private function getPatientsFromSnapshots( + User $user, + string $status, + DateRange $dateRange, + int $branchId, + bool $onlyIds = false + ) { $reports = $this->getReportsForDateRange( $user->rf_department_id, $dateRange @@ -476,21 +501,27 @@ class ReportService $patientType = $patientTypeMap[$status] ?? null; if ($patientType === 'observation') { - return $this->getObservationPatientsFromSnapshots($user->rf_department_id, $reportIds); + return $this->patientQueryService->getObservationPatients($user->rf_department_id, $onlyIds); //$this->getObservationPatientsFromSnapshots($user->rf_department_id, $reportIds, $onlyIds); } - return $this->snapshotService->getPatientsFromSnapshots($patientType, $reportIds, $branchId); + return $this->snapshotService->getPatientsFromSnapshots($patientType, $reportIds, $branchId, $onlyIds); } /** * Получить пациентов из реплики БД */ - private function getPatientsFromReplica(User $user, string $status, DateRange $dateRange, int $branchId) - { + private function getPatientsFromReplica( + User $user, + string $status, + DateRange $dateRange, + int $branchId, + bool $onlyIds = false, + ?bool $isIncludeCurrent = null + ) { $isHeadOrAdmin = $user->isHeadOfDepartment() || $user->isAdmin(); // Для плановых и экстренных включаем уже лечащихся - $includeCurrent = in_array($status, ['plan', 'emergency']); + $includeCurrent = $isIncludeCurrent ?? in_array($status, ['plan', 'emergency']); return match($status) { 'plan', 'emergency' => $this->patientQueryService->getPlanOrEmergencyPatients( @@ -499,29 +530,40 @@ class ReportService $branchId, $dateRange, false, - false, + $onlyIds, $includeCurrent ), - 'observation' => $this->patientQueryService->getObservationPatients($user->rf_department_id), + 'observation' => $this->patientQueryService->getObservationPatients($user->rf_department_id, $onlyIds), + 'outcome' => $this->patientQueryService->getOutcomePatients( + $branchId, + $dateRange, + 'without-transferred', + $onlyIds + ), // Выписанные без перевода 'outcome-discharged' => $this->patientQueryService->getOutcomePatients( $branchId, $dateRange, - 'discharged' + 'discharged', + $onlyIds ), 'outcome-transferred' => $this->patientQueryService->getOutcomePatients( $branchId, $dateRange, - 'transferred' + 'transferred', + $onlyIds ), 'outcome-deceased' => $this->patientQueryService->getOutcomePatients( $branchId, $dateRange, - 'deceased' + 'deceased', + $onlyIds ), 'current' => $this->patientQueryService->getAllPatientsInDepartment( $isHeadOrAdmin, $branchId, - $dateRange + $dateRange, + false, + $onlyIds ), 'recipient' => $this->patientQueryService->getPlanOrEmergencyPatients( null, @@ -529,7 +571,7 @@ class ReportService $branchId, $dateRange, false, - false, + $onlyIds, false // только поступившие ), default => collect() @@ -666,8 +708,8 @@ class ReportService return null; } - // Если дата не сегодня, не показываем врача - if (!$dateRange->isEndDateToday()) { + // Если дата это период, не показываем врача + if (!$dateRange->isOneDay) { return null; } @@ -744,7 +786,7 @@ class ReportService /** * Получить пациентов под наблюдением из снапшотов */ - private function getObservationPatientsFromSnapshots(int $departmentId, array $reportIds) + private function getObservationPatientsFromSnapshots(int $departmentId, array $reportIds, bool $onlyIds = false) { $medicalHistoryIds = ObservationPatient::whereIn('rf_report_id', $reportIds) ->where('rf_department_id', $departmentId) @@ -756,6 +798,10 @@ class ReportService return collect(); } + if ($onlyIds) { + return collect($medicalHistoryIds); + } + return MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds) ->with(['observationPatient' => function($query) use ($departmentId) { $query->where('rf_department_id', $departmentId) diff --git a/app/Services/SnapshotService.php b/app/Services/SnapshotService.php index 36db8c5..16867df 100644 --- a/app/Services/SnapshotService.php +++ b/app/Services/SnapshotService.php @@ -3,8 +3,10 @@ namespace App\Services; use App\Models\MedicalHistorySnapshot; +use App\Models\MetrikaResult; use App\Models\MisMedicalHistory; use App\Models\MisStationarBranch; +use App\Models\ObservationPatient; use App\Models\Report; use App\Models\User; use Carbon\Carbon; @@ -13,95 +15,146 @@ use Illuminate\Support\Collection; class SnapshotService { public function __construct( - protected PatientService $patientService + protected PatientService $patientService, + protected DateRangeService $dateRangeService, ) {} /** * Создать снапшоты пациентов для отчета */ - public function createPatientSnapshots(Report $report, User $user, array $dates): void + public function createPatientSnapshots(Report $report, User $user, array $dates, $fillableAuto = false): void { $branchId = $this->getBranchId($user->department->rf_mis_department_id); [$startDate, $endDate] = $this->parseDates($dates); + $dateRange = $this->dateRangeService->getNormalizedDateRange($user, $startDate, $endDate); + $isHeadOrAdmin = $user->isHeadOfDepartment() || $user->isAdmin(); - // Плановые пациенты - $this->createSnapshotsForType( - $report, + // Массив для хранения подсчитанных метрик + $metrics = []; + + // 1. Плановые пациенты + $planIds = $this->patientService->getPlanOrEmergencyPatients( 'plan', - $this->patientService->getPlanOrEmergencyPatients( - 'plan', - $user->isHeadOfDepartment() || $user->isAdmin(), - $branchId, - $startDate, - $endDate, - false, - true - ) - ); - - // Экстренные пациенты - $this->createSnapshotsForType( - $report, - 'emergency', - $this->patientService->getPlanOrEmergencyPatients( - 'emergency', - $user->isHeadOfDepartment() || $user->isAdmin(), - $branchId, - $startDate, - $endDate, - false, - true - ) - ); - - // Выписанные - $this->createSnapshotsForType( - $report, - 'discharged', - $this->patientService->getOutcomePatients( - $branchId, - $startDate, - $endDate, - 'discharged' - )->pluck('MedicalHistoryID')->toArray() - ); - - // Переведенные - $this->createSnapshotsForType( - $report, - 'transferred', - $this->patientService->getOutcomePatients( - $branchId, - $startDate, - $endDate, - 'transferred' - )->pluck('MedicalHistoryID')->toArray() - ); - - // Умершие - $this->createSnapshotsForType( - $report, - 'deceased', - $this->patientService->getOutcomePatients( - $branchId, - $startDate, - $endDate, - 'deceased' - )->pluck('MedicalHistoryID')->toArray() - ); - - // Поступившие - $recipientIds = $this->patientService->getPlanOrEmergencyPatients( - null, - $user->isHeadOfDepartment() || $user->isAdmin(), + $isHeadOrAdmin, $branchId, - $startDate, - $endDate, + $dateRange, false, + true, + true, + $fillableAuto + ); + $this->createSnapshotsForType($report, 'plan', $planIds); + $metrics[4] = $this->patientService->getPlanOrEmergencyPatients( + 'plan', + $isHeadOrAdmin, + $branchId, + $dateRange, + true, + $fillableAuto + ); // metrika_item_3 - плановые + + // 2. Экстренные пациенты + $emergencyIds = $this->patientService->getPlanOrEmergencyPatients( + 'emergency', + $isHeadOrAdmin, + $branchId, + $dateRange, + false, + true, + true, + $fillableAuto + ); + $this->createSnapshotsForType($report, 'emergency', $emergencyIds); + $metrics[12] = $this->patientService->getPlanOrEmergencyPatients( + 'emergency', + $isHeadOrAdmin, + $branchId, + $dateRange, + true, + $fillableAuto + );; // metrika_item_12 - экстренные + + // 3. Выписанные + $dischargedIds = $this->patientService->getOutcomePatients( + $branchId, + $dateRange, + 'discharged', true ); + $this->createSnapshotsForType($report, 'discharged', $dischargedIds); + $metrics[15] = count($dischargedIds); // metrika_item_15 - выписанные + // 4. Переведенные + $transferredIds = $this->patientService->getOutcomePatients( + $branchId, + $dateRange, + 'transferred', + true + ); + $this->createSnapshotsForType($report, 'transferred', $transferredIds); + $metrics[13] = count($transferredIds); // metrika_item_13 - переведенные + + // 5. Умершие + $deceasedIds = $this->patientService->getOutcomePatients( + $branchId, + $dateRange, + 'deceased', + true + ); + $this->createSnapshotsForType($report, 'deceased', $deceasedIds); +// $metrics[9] = count($deceasedIds); // metrika_item_9 - умершие + + // 6. Поступившие (все новые поступления - плановые + экстренные) + $recipientIds = $this->patientService->getPlanOrEmergencyPatients( + null, + $isHeadOrAdmin, + $branchId, + $dateRange, + false, + true, + false // только поступившие сегодня + ); $this->createSnapshotsForType($report, 'recipient', $recipientIds); +// $metrics[3] = count($recipientIds); // metrika_item_3 - поступившие + + // 8. Плановые операции + $planSurgeryCount = $this->patientService->getSurgicalPatients( + 'plan', + $branchId, + $dateRange, + true + ); +// $metrics[11] = $planSurgeryCount; // metrika_item_11 - плановые операции + + // 9. Экстренные операции + $emergencySurgeryCount = $this->patientService->getSurgicalPatients( + 'emergency', + $branchId, + $dateRange, + true + ); +// $metrics[10] = $emergencySurgeryCount; // metrika_item_10 - экстренные операции + + // Сохраняем все метрики + $this->saveMetrics($report, $metrics); + } + + /** + * Сохранить метрики в базу + */ + private function saveMetrics(Report $report, array $metrics): void + { + foreach ($metrics as $metrikaItemId => $value) { + MetrikaResult::updateOrCreate( + [ + 'rf_report_id' => $report->report_id, + 'rf_metrika_item_id' => $metrikaItemId, + ], + [ + 'value' => $value, + ] + ); + } } /** @@ -126,8 +179,12 @@ class SnapshotService public function getPatientsFromSnapshots( string $type, array $reportIds, - ?int $branchId = null + ?int $branchId = null, + bool $onlyIds = false ): Collection { + // Для плановых и экстренных включаем уже лечащихся +// $includeCurrent = in_array($type, ['plan', 'emergency']); + $medicalHistoryIds = MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds) ->where('patient_type', $type) ->pluck('rf_medicalhistory_id') @@ -138,6 +195,10 @@ class SnapshotService return collect(); } + if ($onlyIds) { + return collect($medicalHistoryIds); + } + return MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds) ->when($type === 'plan', fn($q) => $q->plan()) ->when($type === 'emergency', fn($q) => $q->emergency()) @@ -165,14 +226,21 @@ class SnapshotService /** * Создать снапшоты для определенного типа пациентов */ - private function createSnapshotsForType(Report $report, string $type, array $medicalHistoryIds): void + private function createSnapshotsForType(Report $report, string $type, Collection $medicalHistoryIds): void { foreach ($medicalHistoryIds as $id) { - MedicalHistorySnapshot::create([ - 'rf_report_id' => $report->report_id, - 'rf_medicalhistory_id' => $id, - 'patient_type' => $type, - ]); + MedicalHistorySnapshot::updateOrCreate( + [ + 'rf_report_id' => $report->report_id, + 'rf_medicalhistory_id' => $id, + 'patient_type' => $type + ], + [ + 'rf_report_id' => $report->report_id, + 'rf_medicalhistory_id' => $id, + 'patient_type' => $type, + ] + ); } } diff --git a/resources/js/Components/DatePickerQuery.vue b/resources/js/Components/DatePickerQuery.vue index 7d83815..7d42311 100644 --- a/resources/js/Components/DatePickerQuery.vue +++ b/resources/js/Components/DatePickerQuery.vue @@ -75,22 +75,32 @@ const formattedValue = computed(() => { } }) -watch(() => modelValue.value, (newVal, oldVal) => { - if (!newVal) return +const handleDateUpdate = (value) => { + console.log('Получено значение:', value) - if (Array.isArray(newVal)) { - if (newVal.length === 2 && - (!oldVal || newVal[0] !== oldVal[0] || newVal[1] !== oldVal[1])) { - queryDate.value = newVal - setQueryDate() + // Устанавливаем новое значение + modelValue.value = value + + // Для диапазона: отправляем только если обе даты заполнены + if (isUseDateRange.value) { + if (Array.isArray(value) && value[0] && value[1]) { + // Дебаунс для предотвращения двойной отправки + setTimeout(() => { + // Проверяем что значение не изменилось за время таймаута + if (JSON.stringify(modelValue.value) === JSON.stringify(value)) { + queryDate.value = value + setQueryDate() + } + }, 100) } } else { - if (newVal !== oldVal) { - queryDate.value = newVal + // Для одиночной даты отправляем сразу + if (value) { + queryDate.value = [value, value] setQueryDate() } } -}) +} - - - -