diff --git a/app/Http/Controllers/Api/ReportController.php b/app/Http/Controllers/Api/ReportController.php
index 487f0e6..e9a6acf 100644
--- a/app/Http/Controllers/Api/ReportController.php
+++ b/app/Http/Controllers/Api/ReportController.php
@@ -4,11 +4,13 @@ namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Resources\Mis\FormattedPatientResource;
+use App\Models\MedicalHistorySnapshot;
use App\Models\MetrikaGroup;
use App\Models\MetrikaResult;
use App\Models\MisMedicalHistory;
use App\Models\MisMigrationPatient;
use App\Models\MisStationarBranch;
+use App\Models\MisSurgicalOperation;
use App\Models\ObservationPatient;
use App\Models\Report;
use App\Models\UnwantedEvent;
@@ -28,20 +30,15 @@ class ReportController extends Controller
$department = $user->department;
$startDateCarbon = Carbon::now()->firstOfMonth();
- $startDate = $request->query('startAt', $startDateCarbon->format('Y-m-d'));
$endDateCarbon = Carbon::now();
- $endDate = $request->query('endAt', $endDateCarbon->format('Y-m-d'));
- $doctorStartDate = Carbon::now()->addDays(-1)->format('Y-m-d');
- $doctorEndDate = Carbon::now()->format('Y-m-d');
-
- if (is_numeric($startDate)) {
- $startDateCarbon = Carbon::createFromTimestampMs($startDate);
- $startDate = Carbon::createFromTimestampMs($startDate)->setTimezone('Asia/Yakutsk')->format('Y-m-d');
+ // Определяем даты в зависимости от роли
+ [$startDate, $endDate] = $this->getDateRangeForRole($user, $request->query('startAt'), $request->query('endAt'));
+ if (Carbon::parse($startDate)->isValid()) {
+ $startDateCarbon = Carbon::parse($startDate)->setTimeZone('Asia/Yakutsk');
}
- if (is_numeric($endDate)) {
- $endDateCarbon = Carbon::createFromTimestampMs($endDate);
- $endDate = Carbon::createFromTimestampMs($endDate)->setTimezone('Asia/Yakutsk')->format('Y-m-d');
+ if (Carbon::parse($endDate)->isValid()) {
+ $endDateCarbon = Carbon::parse($endDate)->setTimeZone('Asia/Yakutsk');
}
$beds = (int)$department->metrikaDefault()->where('rf_metrika_item_id', 1)->first()->value;
@@ -50,7 +47,7 @@ class ReportController extends Controller
->where('metrika_results.rf_metrika_item_id', 8)
->orderBy('sent_at', 'desc')->first())->value ?? 0;
- $percentLoadedBeds = intval($occupiedBeds) * 100 / $beds;
+ $percentLoadedBeds = round(intval($occupiedBeds) * 100 / $beds); //intval($occupiedBeds) * 100 / $beds;
$metrikaGroup = MetrikaGroup::whereMetrikaGroupId(2)->first();
$metrikaItems = $metrikaGroup->metrikaItems;
@@ -59,115 +56,110 @@ class ReportController extends Controller
$branchId = MisStationarBranch::where('rf_DepartmentID', $misDepartmentId)
->value('StationarBranchID');
- if ($user->isHeadOfDepartment())
- {
- $medicalHistoryIds = MisMigrationPatient::whereInDepartment($branchId)
- ->pluck('rf_MedicalHistoryID')
- ->unique()
- ->toArray();
- $extractedMedicalHistoryIds = MisMigrationPatient::extractedToday($branchId, $startDate, $endDate)
- ->pluck('rf_MedicalHistoryID')
- ->unique()
- ->toArray();
+ $unwantedEvents = UnwantedEvent::whereHas('report', function ($query) use ($user, $startDate, $endDate) {
+ $query->where('rf_department_id', $user->rf_department_id)
+ ->whereBetween('created_at', [$startDate, $endDate]);
+ })
+ ->get()->map(function ($item) {
+ return [
+ ...$item->toArray(),
+ 'created_at' => Carbon::parse($item->created_at)->format('Создано d.m.Y в H:i'),
+ ];
+ });
- $recipientCount = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
- ->when($user->isHeadOfDepartment(), function($query) use ($startDate, $endDate) {
- return $query->whereBetween('DateRecipient', [$startDate, $endDate]);
- })
- ->when(!$user->isHeadOfDepartment(), function($query) use ($doctorStartDate, $doctorEndDate) {
- return $query->whereBetween('DateRecipient', [$doctorStartDate, $doctorEndDate]);
- })
- ->orderBy('DateRecipient', 'DESC')
- ->count();
+ // Определяем, является ли пользователь заведующим/администратором
+ $isHeadOrAdmin = $user->isHeadOfDepartment() || $user->isAdmin();
- $extractedCount = MisMedicalHistory::whereIn('MedicalHistoryID', $extractedMedicalHistoryIds)
- ->orderBy('DateRecipient', 'DESC')
- ->count();
-
- $recipientIds = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
- ->when($doctorStartDate, function($query) use ($doctorStartDate, $doctorEndDate) {
- return $query->whereBetween('DateRecipient', [$doctorStartDate, $doctorEndDate]);
- })
- ->orderBy('DateRecipient', 'DESC')
- ->pluck('MedicalHistoryID')
- ->values();
-
- $currentCount = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
- ->currentlyHospitalized()
- ->orderBy('DateRecipient', 'DESC')
- ->count();
- } else {
- $medicalHistoryIds = MisMigrationPatient::currentlyInTreatment($branchId)
- ->pluck('rf_MedicalHistoryID')
- ->unique()
- ->toArray();
-
- $extractedMedicalHistoryIds = MisMigrationPatient::extractedToday($branchId)
- ->pluck('rf_MedicalHistoryID')
- ->unique()
- ->toArray();
-
- $recipientCount = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
- ->when($user->isHeadOfDepartment(), function($query) use ($startDate, $endDate) {
- return $query->whereBetween('DateRecipient', [$startDate, $endDate]);
- })
- ->when(!$user->isHeadOfDepartment(), function($query) use ($doctorStartDate, $doctorEndDate) {
- return $query->whereBetween('DateRecipient', [$doctorStartDate, $doctorEndDate]);
- })
- ->orderBy('DateRecipient', 'DESC')
- ->count();
-
- $extractedCount = MisMedicalHistory::whereIn('MedicalHistoryID', $extractedMedicalHistoryIds)
- ->orderBy('DateRecipient', 'DESC')
- ->count();
-
- $recipientIds = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
- ->when($user->isHeadOfDepartment(), function($query) use ($startDate, $endDate) {
- return $query->whereBetween('DateRecipient', [$startDate, $endDate]);
- })
- ->when(!$user->isHeadOfDepartment(), function($query) use ($doctorStartDate, $doctorEndDate) {
- return $query->whereBetween('DateRecipient', [$doctorStartDate, $doctorEndDate]);
- })
- ->orderBy('DateRecipient', 'DESC')
- ->pluck('MedicalHistoryID')
- ->values();
-
- $currentCount = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
- ->currentlyHospitalized()
- ->orderBy('DateRecipient', 'DESC')
- ->count();
- }
+ $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);
+ $recipientIds = $this->getPlanOrEmergencyPatients(
+ null,
+ $isHeadOrAdmin,
+ $branchId,
+ $startDate,
+ $endDate,
+ false,
+ true,
+ true,
+ today: true
+ );
return response()->json([
'department' => [
'beds' => $beds,
'percentLoadedBeds' => $percentLoadedBeds,
- 'recipientCount' => $recipientCount,
- 'extractCount' => $extractedCount,
+
+ 'recipientCount' => $plan + $emergency, //$recipientCount,
+ 'extractCount' => $outcomeCount, //$extractedCount,
'currentCount' => $currentCount,
- 'recipientIds' => $recipientIds
+ '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' => $recipientIds,
],
'dates' => [
'startAt' => $startDateCarbon->getTimestampMs(),
'endAt' => $endDateCarbon->getTimestampMs()
],
+ 'report' => [
+ 'unwantedEvents' => $unwantedEvents,
+ 'isActiveSendButton' => Carbon::createFromFormat('Y-m-d H:i:s', $endDate)->isToday() && (!$user->isHeadOfDepartment() && !$user->isAdmin()),
+ ],
'metrikaItems' => $metrikaItems
]);
}
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',
- 'unwantedEvent' => 'nullable'
+ 'unwantedEvents' => 'nullable|array',
+ 'dates' => 'required|array',
]);
-
$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) {
@@ -188,11 +180,29 @@ class ReportController extends Controller
'sent_at' => now()
]);
- if (in_array('unwantedEvent', $data)) {
- $unwantedEvent = UnwantedEvent::create([
- 'rf_report_id' => $report->id,
- 'comment' => $data['unwantedEvent']['comment'] ?? '',
- ]);
+ 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,
+ ]);
+ }
+ }
}
foreach ($metriks as $metrika) {
@@ -205,10 +215,73 @@ class ReportController extends Controller
'rf_department_id' => $data['departmentId'],
'rf_report_id' => $report->report_id,
'rf_medicalhistory_id' => $observationPatient['id'],
- 'rf_mkab_id' => null
+ 'rf_mkab_id' => null,
+ 'comment' => $observationPatient['comment'] ?? null
]);
}
+ // Сохраняем снимок для каждого типа пациентов
+
+ // 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'
+ ]);
+ }
+
+ // 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([
@@ -220,221 +293,61 @@ class ReportController extends Controller
{
$user = Auth::user();
$data = $request->validate([
- 'status' => 'required|string', // plan emergency
+ 'status' => 'required|string', // plan emergency observation deceased
'startAt' => 'nullable',
'endAt' => 'nullable',
]);
+ // Получаем базовые данные
$status = $data['status'];
-
- $startDateCarbon = Carbon::now()->firstOfMonth();
- $startDate = $data['startAt'] ?? $startDateCarbon->format('Y-m-d');
- $endDateCarbon = Carbon::now();
- $endDate = $data['endAt'] ?? $startDateCarbon->format('Y-m-d');
-
- $doctorStartDate = Carbon::now()->addDays(-1)->format('Y-m-d');
- $doctorEndDate = Carbon::now()->format('Y-m-d');
-
- if (is_numeric($startDate)) {
- $startDateCarbon = Carbon::createFromTimestampMs($startDate);
- $startDate = Carbon::createFromTimestampMs($startDate)->setTimezone('Asia/Yakutsk')->format('Y-m-d');
- }
- if (is_numeric($endDate)) {
- $endDateCarbon = Carbon::createFromTimestampMs($endDate);
- $endDate = Carbon::createFromTimestampMs($endDate)->setTimezone('Asia/Yakutsk')->format('Y-m-d');
- }
-
$model = new MisMedicalHistory();
$misDepartmentId = $request->user()->department->rf_mis_department_id;
$userDepartmentId = $request->user()->department->department_id;
- $misStationarBranchId = MisStationarBranch::where('rf_DepartmentID', $misDepartmentId)->first()->StationarBranchID;
+ $branchId = MisStationarBranch::where('rf_DepartmentID', $misDepartmentId)->value('StationarBranchID');
- if ($user->isHeadOfDepartment()) {
- if ($status === 'plan') {
- // Сначала получаем ID локально
- $branchId = MisStationarBranch::where('rf_DepartmentID', $misDepartmentId)
- ->value('StationarBranchID');
-
- if (!$branchId) {
- return collect();
- }
-
- $medicalHistoryIds = MisMigrationPatient::whereInDepartment($branchId)
- ->when($startDate && $endDate, function($query) use ($startDate, $endDate) {
- return $query->whereBetween('DateIngoing', [$startDate, $endDate]);
- })
- ->pluck('rf_MedicalHistoryID')
- ->unique()
- ->toArray();
-
- if (empty($medicalHistoryIds)) {
- return collect();
- }
-
- // Получаем истории
- $patients = MisMedicalHistory::select($model->getFillable())
- ->plan()
- ->whereIn('MedicalHistoryID', $medicalHistoryIds)
- ->orderBy('DateRecipient', 'DESC')
- ->get()
- ->map(function ($item, $index) use ($misStationarBranchId) {
- $item->num = $index + 1;
- $item->misStationarBranchId = $misStationarBranchId;
- return $item;
- });
- } else if ($status === 'emergency') {
- // Сначала получаем ID локально
- $branchId = MisStationarBranch::where('rf_DepartmentID', $misDepartmentId)
- ->value('StationarBranchID');
-
- if (!$branchId) {
- return collect();
- }
-
- $medicalHistoryIds = MisMigrationPatient::whereInDepartment($branchId)
- ->when($startDate && $endDate, function($query) use ($startDate, $endDate) {
- return $query->whereBetween('DateIngoing', [$startDate, $endDate]);
- })
- ->pluck('rf_MedicalHistoryID')
- ->unique()
- ->toArray();
-
- if (empty($medicalHistoryIds)) {
- return collect();
- }
-
- // Получаем истории
- $patients = MisMedicalHistory::select($model->getFillable())
- ->emergency()
- ->whereIn('MedicalHistoryID', $medicalHistoryIds)
- ->with(['surgicalOperations'])
- ->orderBy('DateRecipient', 'DESC')
- ->get()
- ->map(function ($item, $index) use ($misStationarBranchId) {
- $item->num = $index + 1;
- $item->misStationarBranchId = $misStationarBranchId;
- return $item;
- });
- } else if ($status === 'observation') {
- $medicalHistoryIds = ObservationPatient::where('rf_department_id', $userDepartmentId)
- ->pluck('rf_medicalhistory_id')
- ->toArray();
-
- $patients = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
- ->get()->map(function ($item, $index) use ($misStationarBranchId) {
- $item->num = $index + 1;
- $item->misStationarBranchId = $misStationarBranchId;
- return $item;
- });
-
- } else if ($status === 'deceased') {
- $patients = MisMedicalHistory::select(...$model->getFillable())
- ->deceased()
- ->inDepartment($misDepartmentId, $startDate, $endDate)
- ->get()
- ->map(function ($item, $index) {
- $item->num = $index + 1;
- return $item;
- });
- }
- } else {
- if ($status === 'plan') {
- // Сначала получаем ID локально
- $branchId = MisStationarBranch::where('rf_DepartmentID', $misDepartmentId)
- ->value('StationarBranchID');
-
- if (!$branchId) {
- return collect();
- }
-
- $medicalHistoryIds = MisMigrationPatient::currentlyInTreatment($branchId)
-// ->when($startDate && $endDate, function($query) use ($startDate, $endDate) {
-// return $query->whereBetween('DateIngoing', [$startDate, $endDate]);
-// })
- ->pluck('rf_MedicalHistoryID')
- ->unique()
- ->toArray();
-
- if (empty($medicalHistoryIds)) {
- return collect();
- }
-
- // Получаем истории
- $patients = MisMedicalHistory::select($model->getFillable())
- ->plan()
- ->whereIn('MedicalHistoryID', $medicalHistoryIds)
- ->currentlyHospitalized()
- ->orderBy('DateRecipient', 'DESC')
- ->get()
- ->map(function ($item, $index) use ($misStationarBranchId) {
- $item->num = $index + 1;
- $item->misStationarBranchId = $misStationarBranchId;
- return $item;
- });
- } else if ($status === 'emergency') {
- // Сначала получаем ID локально
- $branchId = MisStationarBranch::where('rf_DepartmentID', $misDepartmentId)
- ->value('StationarBranchID');
-
- if (!$branchId) {
- return collect();
- }
-
- $medicalHistoryIds = MisMigrationPatient::currentlyInTreatment($branchId)
-// ->when($startDate && $endDate, function($query) use ($startDate, $endDate) {
-// return $query->whereBetween('DateIngoing', [$startDate, $endDate]);
-// })
- ->pluck('rf_MedicalHistoryID')
- ->unique()
- ->toArray();
-
- if (empty($medicalHistoryIds)) {
- return collect();
- }
-
- // Получаем истории
- $patients = MisMedicalHistory::select($model->getFillable())
- ->emergency()
- ->whereIn('MedicalHistoryID', $medicalHistoryIds)
- ->currentlyHospitalized()
- ->with(['surgicalOperations'])
- ->orderBy('DateRecipient', 'DESC')
- ->get()
- ->map(function ($item, $index) use ($misStationarBranchId) {
- $item->num = $index + 1;
- $item->misStationarBranchId = $misStationarBranchId;
- return $item;
- });
- } else if ($status === 'observation') {
- $medicalHistoryIds = ObservationPatient::where('rf_department_id', $userDepartmentId)
- ->pluck('rf_medicalhistory_id')
- ->toArray();
-
- $patients = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
- ->get()->map(function ($item, $index) use ($misStationarBranchId) {
- $item->num = $index + 1;
- $item->misStationarBranchId = $misStationarBranchId;
- return $item;
- });
-
- } else if ($status === 'deceased') {
- $patients = MisMedicalHistory::select(...$model->getFillable())
- ->deceased()
- ->inDepartment($misDepartmentId, $startDate, $endDate)
- ->get()
- ->map(function ($item, $index) {
- $item->num = $index + 1;
- return $item;
- });
- }
+ if (!$branchId) {
+ return response()->json([]);
}
- $patients->load(['migrations' => function ($query) use ($startDate, $endDate, $misStationarBranchId) {
- $query->whereHas('diagnosis', function ($query) {
- $query->where('rf_DiagnosTypeID', 3);
- })->with('diagnosis.mkb')
- ->where('rf_StationarBranchID', $misStationarBranchId);
- }]);
+ // Определяем даты в зависимости от роли
+ [$startDate, $endDate] = $this->getDateRangeForRole($user, $data['startAt'] ?? null, $data['endAt'] ?? null);
+
+// dd($startDate, $endDate);
+
+ // Определяем, является ли пользователь заведующим/администратором
+ $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
+ };
+
+// dd($patients->pluck('MedicalHistoryID')->toArray());
+
+ // Если есть пациенты, добавляем дополнительные данные
+ 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));
}
@@ -443,97 +356,452 @@ class ReportController extends Controller
{
$user = Auth::user();
$data = $request->validate([
- 'status' => 'required|string', // plan emergency
+ 'status' => 'required|string', // plan emergency observation deceased
'startAt' => 'nullable',
'endAt' => 'nullable',
]);
+ // Получаем базовые данные
$status = $data['status'];
-
- $startDateCarbon = Carbon::now()->firstOfMonth();
- $startDate = $data['startAt'] ?? $startDateCarbon->format('Y-m-d');
- $endDateCarbon = Carbon::now();
- $endDate = $data['endAt'] ?? $endDateCarbon->format('Y-m-d');
-
- $doctorStartDate = Carbon::now()->addDays(-1)->format('Y-m-d');
- $doctorEndDate = Carbon::now()->format('Y-m-d');
-
- if (is_numeric($startDate)) {
- $startDateCarbon = Carbon::createFromTimestampMs($startDate);
- $startDate = Carbon::createFromTimestampMs($startDate)->setTimezone('Asia/Yakutsk')->format('Y-m-d');
- }
- if (is_numeric($endDate)) {
- $endDateCarbon = Carbon::createFromTimestampMs($endDate);
- $endDate = Carbon::createFromTimestampMs($endDate)->setTimezone('Asia/Yakutsk')->format('Y-m-d');
- }
-
+ $model = new MisMedicalHistory();
$misDepartmentId = $request->user()->department->rf_mis_department_id;
-
- // Получаем ID отделения
- $branchId = MisStationarBranch::where('rf_DepartmentID', $misDepartmentId)
- ->value('StationarBranchID');
+ $userDepartmentId = $request->user()->department->department_id;
+ $branchId = MisStationarBranch::where('rf_DepartmentID', $misDepartmentId)->value('StationarBranchID');
if (!$branchId) {
- return response()->json(0);
+ return response()->json([]);
}
- if ($user->isHeadOfDepartment()) {
- // Получаем ID медицинских историй по миграциям
- $medicalHistoryIds = MisMigrationPatient::whereInDepartment($branchId)
- ->when($startDate && $endDate, function($query) use ($startDate, $endDate) {
- return $query->whereBetween('DateIngoing', [$startDate, $endDate]);
- })
- ->pluck('rf_MedicalHistoryID')
- ->unique()
- ->toArray();
+ // Определяем даты в зависимости от роли
+ [$startDate, $endDate] = $this->getDateRangeForRole($user, $data['startAt'] ?? null, $data['endAt'] ?? null);
- if (empty($medicalHistoryIds)) {
- return response()->json(0);
- }
+ // Определяем, является ли пользователь заведующим/администратором
+ $isHeadOrAdmin = $user->isHeadOfDepartment() || $user->isAdmin();
- // Подсчет в зависимости от статуса
- if ($status === 'plan') {
- $count = MisMedicalHistory::plan()
- ->whereIn('MedicalHistoryID', $medicalHistoryIds)
- ->orderBy('DateRecipient', 'DESC')
- ->count();
- } else if ($status === 'emergency') {
- $count = MisMedicalHistory::emergency()
- ->whereIn('MedicalHistoryID', $medicalHistoryIds)
- ->orderBy('DateRecipient', 'DESC')
- ->count();
- } else {
- $count = 0;
+ $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 {
- // Получаем ID медицинских историй по миграциям
- $medicalHistoryIds = MisMigrationPatient::currentlyInTreatment($branchId)
- ->pluck('rf_MedicalHistoryID')
- ->unique()
- ->toArray();
-
- if (empty($medicalHistoryIds)) {
- return response()->json(0);
- }
-
- // Подсчет в зависимости от статуса
- if ($status === 'plan') {
- $count = MisMedicalHistory::plan()
- ->whereIn('MedicalHistoryID', $medicalHistoryIds)
- ->currentlyHospitalized()
- ->orderBy('DateRecipient', 'DESC')
- ->count();
- } else if ($status === 'emergency') {
- $count = MisMedicalHistory::emergency()
- ->whereIn('MedicalHistoryID', $medicalHistoryIds)
- ->currentlyHospitalized()
- ->orderBy('DateRecipient', 'DESC')
- ->count();
- } else {
- $count = 0;
- }
+ $count = $this->getPlanOrEmergencyPatients($status, $isHeadOrAdmin, $branchId, $startDate, $endDate, true);
}
return response()->json($count);
}
+
+ public function getRecipientIds(bool $isHeadOrAdmin, $branchId, $startDate, $endDate) {
+
+ }
+
+ /**
+ * Определить диапазон дат в зависимости от роли
+ */
+ 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);
+
+ // Если пользователь врач - всегда применяем правило "-1 день"
+// if (!$user->isHeadOfDepartment() && !$user->isAdmin()) {
+// // Для врача: endDate - выбранная дата, startDate - выбранная дата -1 день
+// $startDate = $endDate->copy()->subDay();
+// }
+
+ // Если даты одинаковые (выбран один день) или врач
+// dd($startDate->isSameDay($endDate) || (!$user->isHeadOfDepartment() && !$user->isAdmin()))0
+// dd($startDate->isCurrentDay());
+ 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');
+ }
+
+// dd($startDate);
+
+// dd($startDate, $endDate);
+ }
+ // Если даты не переданы - логика по умолчанию в зависимости от роли
+ else {
+ // Для заведующего или администратора - период месяца
+ if ($user->isHeadOfDepartment() || $user->isAdmin()) {
+ $startDate = Carbon::now('Asia/Yakutsk')
+ ->firstOfMonth()
+ ->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);
+ }
}
diff --git a/app/Http/Controllers/Web/StatisticController.php b/app/Http/Controllers/Web/StatisticController.php
index 5524471..08e7aa3 100644
--- a/app/Http/Controllers/Web/StatisticController.php
+++ b/app/Http/Controllers/Web/StatisticController.php
@@ -58,7 +58,7 @@ class StatisticController extends Controller
->orderBy('reports.sent_at', 'desc')
->first())->value ?? 0;
- $percentLoadedBeds = $beds > 0 ? $occupiedBeds * 100 / $beds : 0;
+ $percentLoadedBeds = $beds > 0 ? round($occupiedBeds * 100 / $beds) : 0;
$data[] = [
'department' => $department->name_short,
diff --git a/app/Http/Resources/Mis/FormattedPatientResource.php b/app/Http/Resources/Mis/FormattedPatientResource.php
index fe5bc02..303323e 100644
--- a/app/Http/Resources/Mis/FormattedPatientResource.php
+++ b/app/Http/Resources/Mis/FormattedPatientResource.php
@@ -27,7 +27,7 @@ class FormattedPatientResource extends JsonResource
];
}),
'operations' => $this->whenLoaded('surgicalOperations', function () {
- return $this->surgicalOperations()->where('rf_StationarBranchID', $this->misStationarBranchId)
+ return $this->operationOnBranch($this->misStationarBranchId, $this->startDate, $this->endDate)
->get()
->map(function (MisSurgicalOperation $operation) {
return [
@@ -38,6 +38,8 @@ class FormattedPatientResource extends JsonResource
'fullname' => Str::ucwords(Str::lower("$this->FAMILY $this->Name $this->OT")),
'age' => Carbon::parse($this->BD)->diff(Carbon::now())->format('%y'),
'birth_date' => Carbon::parse($this->BD)->format('d.m.Y'),
+ 'outcome_type' => $this->when($this->outcome_type, $this->outcome_type),
+ 'comment' => $this->when($this->comment, $this->comment)
];
}
}
diff --git a/app/Models/MedicalHistorySnapshot.php b/app/Models/MedicalHistorySnapshot.php
new file mode 100644
index 0000000..d7a6696
--- /dev/null
+++ b/app/Models/MedicalHistorySnapshot.php
@@ -0,0 +1,21 @@
+belongsTo(Report::class, 'rf_report_id');
+ }
+}
diff --git a/app/Models/MisMedicalHistory.php b/app/Models/MisMedicalHistory.php
index a9ebd39..7aa2c6e 100644
--- a/app/Models/MisMedicalHistory.php
+++ b/app/Models/MisMedicalHistory.php
@@ -26,7 +26,7 @@ class MisMedicalHistory extends Model
public function observationPatient()
{
- return $this->belongsTo(ObservationPatient::class, 'MedicalHistoryID', 'rf_medicalhistory_id');
+ return $this->hasMany(ObservationPatient::class, 'rf_medicalhistory_id', 'MedicalHistoryID');
}
public function surgicalOperations()
@@ -34,6 +34,12 @@ class MisMedicalHistory extends Model
return $this->hasMany(MisSurgicalOperation::class, 'rf_MedicalHistoryID', 'MedicalHistoryID');
}
+ public function scopeOperationOnBranch($query, $branchId, $startDate, $endDate)
+ {
+ return $this->surgicalOperations()->where('rf_StationarBranchID', $branchId)
+ ->whereBetween('Date', [$startDate, $endDate]);
+ }
+
public function scopeCurrentlyHospitalized($query)
{
return $query->whereDate('DateExtract', '1900-01-01')
@@ -43,25 +49,25 @@ class MisMedicalHistory extends Model
/*
* Истории со срочностью - Плановая
*/
- public function scopePlan()
+ public function scopePlan($query)
{
- return $this->where('rf_EmerSignID', 1);
+ return $query->where('rf_EmerSignID', 1);
}
/*
* Истории со срочностью - Экстренная
*/
- public function scopeEmergency()
+ public function scopeEmergency($query)
{
- return $this->where('rf_EmerSignID', 2);
+ return $query->where('rf_EmerSignID', 2);
}
/*
* Истории с результатом - Умер
*/
- public function scopeDeceased()
+ public function scopeDeceased($query)
{
- return $this->where('rf_kl_VisitResultID', 5);
+ return $query->where('rf_kl_VisitResultID', 5);
}
/*
diff --git a/app/Models/MisMigrationPatient.php b/app/Models/MisMigrationPatient.php
index 77f3406..f118fb8 100644
--- a/app/Models/MisMigrationPatient.php
+++ b/app/Models/MisMigrationPatient.php
@@ -25,7 +25,7 @@ class MisMigrationPatient extends Model
return $this->hasOne(MisMKB::class, 'MKBID', 'rf_MKBID');
}
- public function scopeCurrentlyInTreatment($query, $branchId = null)
+ public function scopeCurrentlyInTreatment($query, $branchId = null, $startDate = null, $endDate = null)
{
$query->where('rf_kl_VisitResultID', 0)
->where('rf_MedicalHistoryID', '<>', 0);
@@ -34,6 +34,10 @@ class MisMigrationPatient extends Model
$query->where('rf_StationarBranchID', $branchId);
}
+ if ($startDate && $endDate) {
+ $query->whereBetween('DateIngoing', [$startDate, $endDate]);
+ }
+
return $query;
}
@@ -48,6 +52,95 @@ class MisMigrationPatient extends Model
return $query;
}
+ /**
+ * Выбывшие пациенты (все исходы)
+ */
+ public function scopeOutcomePatients($query, $branchId = null, $startDate = null, $endDate = null)
+ {
+ $query->where('rf_kl_VisitResultID', '<>', 0) // не активное лечение
+ ->whereDate('DateOut', '<>', '1900-01-01') // есть дата выбытия
+ ->where('rf_MedicalHistoryID', '<>', 0);
+
+ if ($branchId) {
+ $query->where('rf_StationarBranchID', $branchId);
+ }
+
+ if ($startDate && $endDate) {
+ $query->whereBetween('DateOut', [$startDate, $endDate]);
+ }
+
+ return $query;
+ }
+
+ /**
+ * Выписанные пациенты
+ */
+ public function scopeDischarged($query, $branchId = null, $startDate = null, $endDate = 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 ($startDate && $endDate) {
+ $query->whereBetween('DateOut', [$startDate, $endDate]);
+ }
+
+ return $query;
+ }
+
+ /**
+ * Перевод в другое отделение
+ */
+ public function scopeTransferred($query, $branchId = null, $startDate = null, $endDate = 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 ($startDate && $endDate) {
+ $query->whereBetween('DateOut', [$startDate, $endDate]);
+ }
+
+ return $query;
+ }
+
+ /**
+ * Умершие пациенты
+ */
+ public function scopeDeceasedOutcome($query, $branchId = null, $startDate = null, $endDate = 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 ($startDate && $endDate) {
+ $query->whereBetween('DateOut', [$startDate, $endDate]);
+ }
+
+ return $query;
+ }
+
public function scopeExtractedToday($query, $branchId = null, $startDate = null, $endDate = null)
{
if (is_null($startDate)) $startDate = Carbon::now()->addDays(-1)->format('Y-m-d');
diff --git a/app/Models/Report.php b/app/Models/Report.php
index 94453ab..ef48ffc 100644
--- a/app/Models/Report.php
+++ b/app/Models/Report.php
@@ -25,4 +25,9 @@ class Report extends Model
{
return $this->hasMany(ObservationPatient::class, 'rf_report_id', 'report_id');
}
+
+ public function unwantedEvents()
+ {
+ return $this->hasMany(UnwantedEvent::class, 'rf_report_id', 'report_id');
+ }
}
diff --git a/app/Models/UnwantedEvent.php b/app/Models/UnwantedEvent.php
index 4b8708e..026c914 100644
--- a/app/Models/UnwantedEvent.php
+++ b/app/Models/UnwantedEvent.php
@@ -10,6 +10,13 @@ class UnwantedEvent extends Model
protected $fillable = [
'rf_report_id',
- 'comment'
+ 'comment',
+ 'title',
+ 'is_visible',
];
+
+ public function report()
+ {
+ return $this->belongsTo(Report::class, 'rf_report_id');
+ }
}
diff --git a/app/Services/DateRangeService.php b/app/Services/DateRangeService.php
new file mode 100644
index 0000000..089b034
--- /dev/null
+++ b/app/Services/DateRangeService.php
@@ -0,0 +1,70 @@
+getCustomDateRange($startAt, $endAt, $user);
+ }
+
+ return $this->getDefaultDateRange($user);
+ }
+
+ private function getCustomDateRange($startAt, $endAt, $user): array
+ {
+ $startDate = $this->parseDate($startAt);
+ $endDate = $this->parseDate($endAt);
+
+ if ($startDate->isSameDay($endDate)) {
+ $startDate = $startDate->subDay()->setTime(6, 0);
+ $endDate = $endDate->setTime(6, 0);
+ } else {
+ $startDate = $startDate->setTime(6, 0);
+ $endDate = $endDate->setTime(6, 0);
+ }
+
+ return [
+ $startDate->format('Y-m-d H:i:s'),
+ $endDate->format('Y-m-d H:i:s')
+ ];
+ }
+
+ private function getDefaultDateRange($user): array
+ {
+ if ($user->isHeadOfDepartment() || $user->isAdmin()) {
+ $startDate = Carbon::now('Asia/Yakutsk')
+ ->firstOfMonth()
+ ->setTime(6, 0);
+
+ $endDate = Carbon::now('Asia/Yakutsk')
+ ->setTime(6, 0);
+ } else {
+ $startDate = Carbon::now('Asia/Yakutsk')
+ ->subDay()
+ ->setTime(6, 0);
+
+ $endDate = Carbon::now('Asia/Yakutsk')
+ ->setTime(6, 0);
+ }
+
+ return [
+ $startDate->format('Y-m-d H:i:s'),
+ $endDate->format('Y-m-d H:i:s')
+ ];
+ }
+
+ private function parseDate($dateInput): Carbon
+ {
+ if (is_numeric($dateInput)) {
+ return Carbon::createFromTimestampMs($dateInput)
+ ->setTimezone('Asia/Yakutsk');
+ }
+
+ return Carbon::parse($dateInput, 'Asia/Yakutsk');
+ }
+}
diff --git a/app/Services/PatientService.php b/app/Services/PatientService.php
new file mode 100644
index 0000000..ca98593
--- /dev/null
+++ b/app/Services/PatientService.php
@@ -0,0 +1,449 @@
+getOutcomePatients($branchId, $startDate, $endDate, $outcomeType, $countOnly, $onlyIds);
+ }
+
+ // Обрабатываем обычные типы
+ $method = 'get' . ucfirst($type) . 'Patients';
+
+ if (method_exists($this, $method)) {
+ return $this->$method($isHeadOrAdmin, $branchId, $startDate, $endDate, $countOnly, $onlyIds);
+ }
+
+ throw new \InvalidArgumentException("Unknown patient type: {$type}");
+ }
+
+ public function getPlanPatients(
+ ?bool $isHeadOrAdmin,
+ ?int $branchId,
+ ?string $startDate,
+ ?string $endDate,
+ bool $countOnly,
+ bool $onlyIds
+ ): Collection|int|array {
+ return $this->getAdmissionPatients('plan', $isHeadOrAdmin, $branchId, $startDate, $endDate, $countOnly, $onlyIds);
+ }
+
+ public function getEmergencyPatients(
+ ?bool $isHeadOrAdmin,
+ ?int $branchId,
+ ?string $startDate,
+ ?string $endDate,
+ bool $countOnly,
+ bool $onlyIds
+ ): Collection|int|array {
+ return $this->getAdmissionPatients('emergency', $isHeadOrAdmin, $branchId, $startDate, $endDate, $countOnly, $onlyIds);
+ }
+
+ private function getAdmissionPatients(
+ string $admissionType,
+ ?bool $isHeadOrAdmin,
+ ?int $branchId,
+ ?string $startDate,
+ ?string $endDate,
+ bool $countOnly,
+ bool $onlyIds
+ ): Collection|int|array {
+ $query = $this->getBasePatientsQuery($isHeadOrAdmin, $branchId, $startDate, $endDate);
+
+ if ($admissionType === 'plan') {
+ $query->plan();
+ } else {
+ $query->emergency();
+ }
+
+ return $this->executeQuery($query, $countOnly, $onlyIds);
+ }
+
+ private function getBasePatientsQuery(
+ ?bool $isHeadOrAdmin,
+ ?int $branchId,
+ ?string $startDate,
+ ?string $endDate
+ ) {
+ $migrationPatient = new MisMigrationPatient();
+
+ if ($isHeadOrAdmin) {
+ $medicalHistoryIds = $migrationPatient->newQuery()
+ ->whereInDepartment($branchId)
+ ->whereBetween('DateIngoing', [$startDate, $endDate])
+ ->pluck('rf_MedicalHistoryID')
+ ->toArray();
+ } else {
+ $medicalHistoryIds = $migrationPatient->newQuery()
+ ->currentlyInTreatment($branchId)
+ ->whereBetween('DateIngoing', [$startDate, $endDate])
+ ->pluck('rf_MedicalHistoryID')
+ ->toArray();
+ }
+
+ if (empty($medicalHistoryIds)) {
+ return MisMedicalHistory::whereRaw('1=0'); // Пустой запрос
+ }
+
+ return MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
+ ->with(['surgicalOperations' => function ($query) use ($startDate, $endDate) {
+ $query->whereBetween('Date', [$startDate, $endDate]);
+ }])
+ ->orderBy('DateRecipient', 'DESC');
+ }
+
+ private function executeQuery($query, bool $countOnly, bool $onlyIds): Collection|int|array
+ {
+ if ($onlyIds) {
+ return $query->pluck('MedicalHistoryID')->toArray();
+ }
+
+ if ($countOnly) {
+ return $query->count();
+ }
+
+ return $query->get();
+ }
+
+ public function getObservationPatients(int $departmentId): Collection
+ {
+ return ObservationPatient::where('rf_department_id', $departmentId)
+ ->with(['medicalHistory'])
+ ->get()
+ ->map(function ($observation) {
+ $patient = $observation->medicalHistory;
+ $patient->observation_comment = $observation->comment;
+ return $patient;
+ });
+ }
+
+ public function getOutcomePatients(int $branchId, string $startDate, string $endDate, string $outcomeType, $countOnly = false, $onlyIds = false): Collection|int
+ {
+ return match($outcomeType) {
+ 'discharged' => $this->getDischargedOutcomePatients($branchId, $startDate, $endDate, $countOnly, $onlyIds),
+ 'transferred' => $this->getTransferredOutcomePatients($branchId, $startDate, $endDate, $countOnly, $onlyIds),
+ 'deceased' => $this->getDeceasedOutcomePatients($branchId, $startDate, $endDate, $countOnly, $onlyIds),
+ default => throw new \InvalidArgumentException("Неизвестный тип исхода: {$outcomeType}")
+ };
+ }
+
+ /**
+ * Получить всех выбывших пациентов
+ */
+ public function getAllOutcomePatients(
+ int $branchId,
+ string $startDate,
+ string $endDate,
+ bool $countOnly = false
+ ): Collection|int {
+ $migrationPatient = new MisMigrationPatient();
+
+ // Получаем миграции выбывших пациентов за период
+ $migrations = $migrationPatient->newQuery()
+ ->outcomePatients($branchId, $startDate, $endDate)
+ ->select('rf_MedicalHistoryID', 'rf_kl_VisitResultID', 'DateOut')
+ ->get()
+ ->groupBy('rf_MedicalHistoryID');
+
+ if ($migrations->isEmpty()) {
+ return $countOnly ? 0 : collect();
+ }
+
+ $medicalHistoryIds = $migrations->keys()->toArray();
+
+ $query = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
+ ->with(['surgicalOperations'])
+ ->orderBy('DateRecipient', 'DESC');
+
+ if ($countOnly) {
+ return $query->count();
+ }
+
+ $patients = $query->get();
+
+ return $this->addOutcomeInfoToPatients($patients, $migrations);
+ }
+
+ /**
+ * Получить миграции выбывших пациентов
+ */
+ private function getOutcomeMigrations(int $branchId, string $startDate, string $endDate): Collection
+ {
+ $migrationPatient = new MisMigrationPatient();
+
+ return $migrationPatient->newQuery()
+ ->outcomePatients($branchId, $startDate, $endDate)
+ ->select('rf_MedicalHistoryID', 'rf_kl_VisitResultID', 'DateOut')
+ ->get()
+ ->groupBy('rf_MedicalHistoryID');
+ }
+
+ /**
+ * Добавить информацию о выбытии к пациентам
+ */
+ private function addOutcomeInfoToPatients(Collection $patients, Collection $migrations): Collection
+ {
+ return $patients->map(function ($patient) use ($migrations) {
+ $patientMigrations = $migrations->get($patient->MedicalHistoryID, collect());
+
+ if ($patientMigrations->isNotEmpty()) {
+ $latestMigration = $patientMigrations->sortByDesc('DateOut')->first();
+ $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(int $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 . ')'
+ };
+ }
+
+ /**
+ * Получить выписанных пациентов
+ */
+ public function getDischargedOutcomePatients(
+ int $branchId,
+ string $startDate,
+ string $endDate,
+ bool $countOnly = false,
+ bool $onlyIds = false
+ ): Collection|array|int {
+ return $this->getSpecificOutcomePatients('discharged', $branchId, $startDate, $endDate, $onlyIds, $countOnly);
+ }
+
+ /**
+ * Получить переведенных пациентов
+ */
+ public function getTransferredOutcomePatients(
+ int $branchId,
+ string $startDate,
+ string $endDate,
+ bool $countOnly = false,
+ bool $onlyIds = false
+ ): Collection|array|int {
+ return $this->getSpecificOutcomePatients('transferred', $branchId, $startDate, $endDate, $onlyIds, $countOnly);
+ }
+
+ /**
+ * Получить умерших пациентов
+ */
+ public function getDeceasedOutcomePatients(
+ int $branchId,
+ string $startDate,
+ string $endDate,
+ bool $countOnly = false,
+ bool $onlyIds = false
+ ): Collection|int|array {
+ return $this->getSpecificOutcomePatients('deceased', $branchId, $startDate, $endDate, $onlyIds, $countOnly);
+ }
+
+ /**
+ * Общий метод для получения пациентов с определенным типом выбытия
+ */
+ private function getSpecificOutcomePatients(
+ string $type,
+ int $branchId,
+ string $startDate,
+ string $endDate,
+ bool $onlyIds = false,
+ bool $countOnly = false
+ ): Collection|int|array {
+ $migrationPatient = new MisMigrationPatient();
+
+ switch ($type) {
+ case 'discharged':
+ $query = $migrationPatient->newQuery()->discharged($branchId, $startDate, $endDate);
+ break;
+ case 'transferred':
+ $query = $migrationPatient->newQuery()->transferred($branchId, $startDate, $endDate);
+ break;
+ case 'deceased':
+ $query = $migrationPatient->newQuery()->deceasedOutcome($branchId, $startDate, $endDate);
+ break;
+ default:
+ throw new \InvalidArgumentException("Неизвестный тип выбытия: {$type}");
+ }
+
+ $medicalHistoryIds = $query->pluck('rf_MedicalHistoryID')->unique()->toArray();
+
+ if (empty($medicalHistoryIds)) {
+ if ($countOnly) return 0;
+ if ($onlyIds) return [];
+ return collect();
+ }
+
+ if ($onlyIds) {
+ return $medicalHistoryIds;
+ }
+
+ $patients = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
+ ->with(['surgicalOperations'])
+ ->orderBy('DateRecipient', 'DESC');
+
+ if ($countOnly) {
+ return $patients->count();
+ }
+
+ return $patients->get();
+ }
+
+ /**
+ * Получить пациентов, находящихся на лечении
+ */
+ public function getCurrentPatients(
+ int $branchId,
+ bool $countOnly = false,
+ bool $onlyIds = false
+ ): Collection|int|array {
+ $migrationPatient = new MisMigrationPatient();
+
+ $medicalHistoryIds = $migrationPatient->newQuery()
+ ->currentlyInTreatment($branchId)
+ ->pluck('rf_MedicalHistoryID')
+ ->unique()
+ ->toArray();
+
+ if (empty($medicalHistoryIds)) {
+ if ($countOnly) return 0;
+ if ($onlyIds) return [];
+ return collect();
+ }
+
+ if ($onlyIds) {
+ return $medicalHistoryIds;
+ }
+
+ $query = MisMedicalHistory::whereIn('MedicalHistoryID', $medicalHistoryIds)
+ ->currentlyHospitalized()
+ ->with(['surgicalOperations'])
+ ->orderBy('DateRecipient', 'DESC');
+
+ if ($countOnly) {
+ return $query->count();
+ }
+
+ return $query->get();
+ }
+
+ /**
+ * Получить пациентов с операциями
+ */
+ public function getSurgicalPatients(
+ string $status,
+ bool $isHeadOrAdmin,
+ int $branchId,
+ string $startDate,
+ string $endDate,
+ bool $countOnly = false
+ ): Collection|int {
+ $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 ($countOnly) {
+ return $query->count();
+ }
+
+ return $query->get();
+ }
+
+ /**
+ * Получить пациентов (плановых или экстренных), которые были в отделении в течение периода
+ */
+ public function getPatientsInDepartmentDuringPeriod(
+ ?string $patientType, // 'plan', 'emergency', null (все)
+ ?bool $isHeadOrAdmin,
+ ?int $branchId,
+ ?string $startDate,
+ ?string $endDate,
+ bool $countOnly = false,
+ bool $onlyIds = false,
+ bool $today = false
+ ): Collection|int|array {
+ // Используем скоуп inDepartment из модели MisMedicalHistory
+ $query = MisMedicalHistory::query()
+ ->whereHas('migrations', function ($q) use ($branchId, $startDate, $endDate) {
+ $q->where('rf_StationarBranchID', $branchId)
+ ->where('rf_MedicalHistoryID', '<>', 0)
+ ->where(function ($subQ) use ($startDate, $endDate) {
+ // Пациент находился в отделении в течение периода
+ // 1. Поступил в течение периода
+ $subQ->whereBetween('DateIngoing', [$startDate, $endDate])
+ // 2. Или выбыл в течение периода
+ ->orWhereBetween('DateOut', [$startDate, $endDate])
+ // 3. Или находился в отделении в течение всего периода
+ ->orWhere(function ($innerQ) use ($startDate, $endDate) {
+ $innerQ->where('DateIngoing', '<=', $startDate)
+ ->where(function ($deepQ) use ($endDate) {
+ $deepQ->where('DateOut', '>=', $endDate)
+ ->orWhereNull('DateOut')
+ ->orWhere('DateOut', '1900-01-01');
+ });
+ });
+ });
+ })
+ ->with(['surgicalOperations' => function ($query) use ($startDate, $endDate) {
+ $query->whereBetween('Date', [$startDate, $endDate]);
+ }])
+ ->orderBy('DateRecipient', 'DESC');
+
+ // Фильтруем по типу (план/экстренность)
+ if ($patientType === 'plan') {
+ $query->plan();
+ } elseif ($patientType === 'emergency') {
+ $query->emergency();
+ }
+
+ // Для врача добавляем условие "все еще в отделении"
+ if (!$isHeadOrAdmin && !$today) {
+ $query->currentlyHospitalized();
+ }
+
+ if ($onlyIds) {
+ return $query->select('MedicalHistoryID')
+ ->pluck('MedicalHistoryID')->values();
+ }
+
+ if ($countOnly) {
+ return $query->count();
+ }
+
+ return $query->get();
+ }
+}
diff --git a/app/Services/ReportService.php b/app/Services/ReportService.php
new file mode 100644
index 0000000..915e23a
--- /dev/null
+++ b/app/Services/ReportService.php
@@ -0,0 +1,125 @@
+patientService = $patientService;
+ }
+
+ public function createReport(array $data): Report
+ {
+ DB::beginTransaction();
+
+ try {
+ $report = Report::create([
+ 'rf_department_id' => $data['departmentId'],
+ 'rf_user_id' => Auth::id(),
+ 'sent_at' => now()
+ ]);
+
+ $this->saveMetrics($report, $data['metrics'] ?? []);
+ $this->saveUnwantedEvents($report, $data['unwantedEvents'] ?? []);
+ $this->saveObservationPatients($report, $data['observationPatients'] ?? []);
+ $this->savePatientSnapshots($report, $data);
+
+ DB::commit();
+
+ return $report;
+ } catch (\Exception $e) {
+ DB::rollBack();
+ throw $e;
+ }
+ }
+
+ private function saveMetrics(Report $report, array $metrics): void
+ {
+ foreach ($metrics as $key => $value) {
+ MetrikaResult::create([
+ 'rf_report_id' => $report->report_id,
+ 'rf_metrika_item_id' => (int) str_replace('metrika_item_', '', $key),
+ 'value' => $value
+ ]);
+ }
+ }
+
+ private function saveUnwantedEvents(Report $report, array $unwantedEvents): void
+ {
+ foreach ($unwantedEvents as $event) {
+ if (isset($event['unwanted_event_id'])) {
+ UnwantedEvent::updateOrCreate(
+ ['unwanted_event_id' => $event['unwanted_event_id']],
+ $this->formatUnwantedEventData($report, $event)
+ );
+ } else {
+ UnwantedEvent::create($this->formatUnwantedEventData($report, $event));
+ }
+ }
+ }
+
+ private function formatUnwantedEventData(Report $report, array $event): array
+ {
+ return [
+ 'rf_report_id' => $report->report_id,
+ 'comment' => $event['comment'] ?? '',
+ 'title' => $event['title'] ?? '',
+ 'is_visible' => $event['is_visible'] ?? true,
+ ];
+ }
+
+ private function saveObservationPatients(Report $report, array $observationPatients): void
+ {
+ foreach ($observationPatients as $patient) {
+ ObservationPatient::create([
+ 'rf_department_id' => $report->rf_department_id,
+ 'rf_report_id' => $report->report_id,
+ 'rf_medicalhistory_id' => $patient['id'],
+ 'comment' => $patient['comment'] ?? null
+ ]);
+ }
+ }
+
+ private function savePatientSnapshots(Report $report, array $data): void
+ {
+ $snapshotTypes = [
+ 'plan' => $this->patientService->getPlanPatients(
+ false,
+ $data['branchId'],
+ $data['startDate'],
+ $data['endDate'],
+ false,
+ true
+ ),
+ 'emergency' => $this->patientService->getEmergencyPatients(
+ false,
+ $data['branchId'],
+ $data['startDate'],
+ $data['endDate'],
+ false,
+ true
+ )
+ ];
+
+ foreach ($snapshotTypes as $type => $patientIds) {
+ foreach ($patientIds as $patientId) {
+ MedicalHistorySnapshot::create([
+ 'rf_report_id' => $report->report_id,
+ 'rf_medicalhistory_id' => $patientId,
+ 'patient_type' => $type
+ ]);
+ }
+ }
+ }
+}
diff --git a/database/migrations/2026_01_27_102348_add_title_and_visible_column_in_unwanted_events_table.php b/database/migrations/2026_01_27_102348_add_title_and_visible_column_in_unwanted_events_table.php
new file mode 100644
index 0000000..e4a3b9d
--- /dev/null
+++ b/database/migrations/2026_01_27_102348_add_title_and_visible_column_in_unwanted_events_table.php
@@ -0,0 +1,30 @@
+string('title')->nullable();
+ $table->string('is_visible')->default(true);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::table('unwanted_events', function (Blueprint $table) {
+ $table->dropColumn('title');
+ $table->dropColumn('is_visible');
+ });
+ }
+};
diff --git a/database/migrations/2026_01_28_151157_create_medical_history_snapshots_table.php b/database/migrations/2026_01_28_151157_create_medical_history_snapshots_table.php
new file mode 100644
index 0000000..827630b
--- /dev/null
+++ b/database/migrations/2026_01_28_151157_create_medical_history_snapshots_table.php
@@ -0,0 +1,30 @@
+id('medical_history_snapshot_id');
+ $table->foreignIdFor(\App\Models\Report::class, 'rf_report_id');
+ $table->foreignIdFor(\App\Models\MisMedicalHistory::class, 'rf_medicalhistory_id');
+ $table->string('patient_type');
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('medical_history_snapshots');
+ }
+};
diff --git a/resources/js/Components/ReportSelectDate.vue b/resources/js/Components/ReportSelectDate.vue
index 8ec4511..306451e 100644
--- a/resources/js/Components/ReportSelectDate.vue
+++ b/resources/js/Components/ReportSelectDate.vue
@@ -2,34 +2,158 @@
import {NDatePicker} from 'naive-ui'
import {storeToRefs} from "pinia";
import {useReportStore} from "../Stores/report.js";
-
-const themeOverride = {
- peers: {
- Input: {
- border: null,
- color: null,
- colorFocus: null,
- borderHover: null,
- borderFocus: null,
- boxShadowFocus: null,
- paddingMedium: ''
- }
- }
-}
+import {useAuthStore} from "../Stores/auth.js";
+import {computed, ref, onMounted, onUnmounted} from "vue";
const reportStore = useReportStore()
-const { timestampCurrentRange } = storeToRefs(reportStore)
+const authStore = useAuthStore()
+const {timestampCurrentRange} = storeToRefs(reportStore)
+
+// Текущее время для обновления
+const currentTime = ref(Date.now())
+
+// Обновляем время каждую секунду
+let intervalId = null
+onMounted(() => {
+ if (authStore.isDoctor) {
+ intervalId = setInterval(() => {
+ currentTime.value = Date.now()
+ }, 1000)
+ }
+})
+
+onUnmounted(() => {
+ if (intervalId) {
+ clearInterval(intervalId)
+ }
+})
+
+// Проверяем, является ли дата сегодняшней
+const isToday = (timestamp) => {
+ const date = new Date(timestamp)
+ const today = new Date()
+
+ return date.getDate() === today.getDate() &&
+ date.getMonth() === today.getMonth() &&
+ date.getFullYear() === today.getFullYear()
+}
+
+// Функция для форматирования с заглавной буквой
+const formatDateWithCapital = (timestamp) => {
+ const date = new Date(timestamp)
+ // Форматируем дату на русском
+ const formatted = date.toLocaleDateString('ru-RU', {
+ weekday: 'long',
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric'
+ })
+ // Делаем первую букву заглавной
+ return formatted.charAt(0).toUpperCase() + formatted.slice(1)
+}
+
+const themeOverride = computed(() => {
+ if (authStore.isDoctor) {
+ return {
+ peers: {
+ Input: {
+ border: '',
+ borderHover: '',
+ borderFocus: '',
+ boxShadowFocus: '',
+ color: '',
+ colorFocus: '',
+ fontSizeMedium: '22px',
+ fontWeight: '500'
+ }
+ }
+ }
+ }
+})
+
+// Используем кастомную функцию форматирования
+const formatComputed = computed(() => {
+ const baseFormat = "dd.MM.yyyy"
+
+ if (authStore.isDoctor) {
+ // Возвращаем функцию форматирования вместо строки
+ return formatDateWithCapital(modelComputed.value)
+ }
+
+ return baseFormat
+})
+
+const typeComputed = computed(() => authStore.isDoctor ? 'date' : 'daterange')
+
+const modelComputed = computed({
+ get: () => {
+ const value = reportStore.timestampCurrentRange
+
+ if (authStore.isDoctor) {
+ if (Array.isArray(value)) {
+ const doctorTimestamp = value[1]
+
+ // Если выбрана сегодняшняя дата - показываем текущее время
+ if (isToday(doctorTimestamp)) {
+ return currentTime.value
+ }
+ // Иначе показываем выбранную дату (без обновления)
+ return doctorTimestamp
+ }
+ return value
+ }
+
+ if (Array.isArray(value)) {
+ return value
+ } else {
+ return [value, value]
+ }
+ },
+ set: (value) => {
+ if (Array.isArray(value)) {
+ reportStore.timestampCurrentRange = value
+ } else {
+ reportStore.timestampCurrentRange = [value, value]
+ }
+
+ reportStore.getDataOnReportDate(reportStore.timestampCurrentRange)
+ }
+})
+
+const classComputed = computed(() => {
+ const baseClasses = []
+
+ if (authStore.isDoctor) {
+ baseClasses.push('w-[400px]')
+ baseClasses.push('text-end')
+ } else {
+ baseClasses.push('max-w-[280px]')
+ }
+
+ return baseClasses
+})
- reportStore.getDataOnReportDate(value)"
+
diff --git a/resources/js/Pages/Report/Components/MoveModalComment.vue b/resources/js/Pages/Report/Components/MoveModalComment.vue
new file mode 100644
index 0000000..d22952c
--- /dev/null
+++ b/resources/js/Pages/Report/Components/MoveModalComment.vue
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ Сохранить
+
+
+
+
+
+
+
diff --git a/resources/js/Pages/Report/Components/ReportForm.vue b/resources/js/Pages/Report/Components/ReportForm.vue
index f6c02e2..a406b1f 100644
--- a/resources/js/Pages/Report/Components/ReportForm.vue
+++ b/resources/js/Pages/Report/Components/ReportForm.vue
@@ -5,6 +5,7 @@ import ReportFormInput from "./ReportFormInput.vue";
import ReportSection from "./ReportSection.vue";
import {useReportStore} from "../../../Stores/report.js";
import {useAuthStore} from "../../../Stores/auth.js";
+import {computed} from "vue";
const props = defineProps({
mode: {
@@ -21,17 +22,19 @@ const onSubmit = () => {
departmentId: authStore.userDepartment.department_id
})
}
+
+
-
+
-
+
Сохранить отчет
diff --git a/resources/js/Pages/Report/Components/ReportFormInput.vue b/resources/js/Pages/Report/Components/ReportFormInput.vue
index fb2ebfc..5ded90e 100644
--- a/resources/js/Pages/Report/Components/ReportFormInput.vue
+++ b/resources/js/Pages/Report/Components/ReportFormInput.vue
@@ -1,30 +1,69 @@
-
-
-
+
+
+
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Операций
+ Э / П
+
+
+
+ / {{ reportStore.reportInfo?.department.surgicalCount[0] }}
+
+
+
+
+
+
diff --git a/resources/js/Pages/Report/Components/ReportHeader.vue b/resources/js/Pages/Report/Components/ReportHeader.vue
index 33c24f3..90b0aee 100644
--- a/resources/js/Pages/Report/Components/ReportHeader.vue
+++ b/resources/js/Pages/Report/Components/ReportHeader.vue
@@ -7,6 +7,7 @@ import {ru} from "date-fns/locale";
import {useAuthStore} from "../../../Stores/auth.js";
import {storeToRefs} from "pinia";
import {RiAddCircleLine} from 'vue-icons-plus/ri'
+import {TbAlertCircle} from 'vue-icons-plus/tb'
import {useReportStore} from "../../../Stores/report.js";
import ReportSelectDate from "../../../Components/ReportSelectDate.vue";
import DepartmentSelect from "../../../Components/DepartmentSelect.vue";
@@ -53,19 +54,16 @@ const currentDate = computed(() => {
-
+
{{ authStore.userDepartment.name_full }}
-
-
- {{ currentDate }}
-
-
-
-
+
+
+
+
@@ -113,9 +111,9 @@ const currentDate = computed(() => {
-
+
- Нежелательное событие
+ Нежелательные события ({{ reportStore.unwantedEvents.length }})
@@ -125,5 +123,11 @@ const currentDate = computed(() => {
diff --git a/resources/js/Pages/Report/Components/ReportSection.vue b/resources/js/Pages/Report/Components/ReportSection.vue
index c2c5d73..699983c 100644
--- a/resources/js/Pages/Report/Components/ReportSection.vue
+++ b/resources/js/Pages/Report/Components/ReportSection.vue
@@ -1,5 +1,5 @@
-
-
-
-
-
-
-
-
+
+
+
+