diff --git a/app/Http/Controllers/Api/ReportController.php b/app/Http/Controllers/Api/ReportController.php
index 96ffadf..c720cc3 100644
--- a/app/Http/Controllers/Api/ReportController.php
+++ b/app/Http/Controllers/Api/ReportController.php
@@ -16,6 +16,8 @@ use App\Models\ObservationPatient;
use App\Models\Report;
use App\Models\UnwantedEvent;
use App\Models\User;
+use App\Services\DateRangeService;
+use App\Services\MisPatientService;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Auth;
@@ -26,6 +28,15 @@ use Inertia\Inertia;
class ReportController extends Controller
{
+ protected MisPatientService $misPatientService;
+ protected DateRangeService $dateService;
+
+ public function __construct(MisPatientService $misPatientService, DateRangeService $dateRangeService)
+ {
+ $this->misPatientService = $misPatientService;
+ $this->dateService = $dateRangeService;
+ }
+
public function index(Request $request)
{
$user = Auth::user();
@@ -35,7 +46,7 @@ class ReportController extends Controller
$endDateCarbon = Carbon::now();
// Определяем даты в зависимости от роли
- [$startDate, $endDate] = $this->getDateRangeForRole($user, $request->query('startAt'), $request->query('endAt'));
+ [$startDate, $endDate] = $this->dateService->getDateRangeForUser($user, $request->query('startAt'), $request->query('endAt'));
if (Carbon::parse($startDate)->isValid()) {
$startDateCarbon = Carbon::parse($startDate)->setTimeZone('Asia/Yakutsk');
}
@@ -48,8 +59,11 @@ class ReportController extends Controller
$reportIds = $reports->pluck('report_id')->toArray();
// Определяем, используем ли мы снапшоты
- $useSnapshots = ($user->isAdmin() || $user->isHeadOfDepartment()) || Carbon::parse($endDate)->isToday() === false;
- if ($useSnapshots) {
+ $reportToday = Report::whereDate('sent_at', $endDate)
+ ->whereDate('created_at', $endDate)
+ ->first();
+ $useSnapshots = ($user->isAdmin() || $user->isHeadOfDepartment()) || (Carbon::parse($endDate)->isToday() === false || $reportToday);
+ if ($useSnapshots && ($user->isHeadOfDepartment() || $user->isAdmin())) {
$report = Report::whereDate('sent_at', $endDate)
->where('rf_department_id', $department->department_id)
->first();
@@ -58,12 +72,6 @@ class ReportController extends Controller
$fillableUserId = $request->query('userId', $user->rf_lpudoctor_id);
}
- if (Carbon::parse($startDate)->diffInDays(Carbon::parse($endDate)) > 1.0) {
- $lpuDoctor = null;
- } else {
- $lpuDoctor = MisLpuDoctor::where('LPUDoctorID', $fillableUserId)->first();
- }
-
$beds = (int)$department->metrikaDefault()->where('rf_metrika_item_id', 1)->first()->value;
$occupiedBeds = optional(Report::where('rf_department_id', $user->rf_department_id)
->join('metrika_results', 'reports.report_id', '=', 'metrika_results.rf_report_id')
@@ -82,7 +90,7 @@ class ReportController extends Controller
$unwantedEvents = UnwantedEvent::whereHas('report', function ($query) use ($user, $startDate, $endDate) {
$query->where('rf_department_id', $user->rf_department_id)
- ->whereBetween('created_at', [$startDate, $endDate]);
+ ->whereDate('created_at', $endDate);
})
->get()->map(function ($item) {
return [
@@ -95,18 +103,19 @@ class ReportController extends Controller
$isHeadOrAdmin = $user->isHeadOfDepartment() || $user->isAdmin();
// Получаем статистику в зависимости от источника данных
- if ($useSnapshots) {
+ if ($useSnapshots || $reportToday) {
// Используем снапшоты для статистики
- $plan = $this->getCountFromSnapshots('plan', $reportIds);
- $emergency = $this->getCountFromSnapshots('emergency', $reportIds);
- $outcomeCount = $this->getCountFromSnapshots('outcome', $reportIds);
- $currentCount = $this->getCurrentPatientsFromSnapshots($reportIds, $branchId);
- $deadCount = $this->getCountFromSnapshots('deceased', $reportIds);
+// $plan = $this->getCountFromSnapshots('plan', $reportIds);
+// $emergency = $this->getCountFromSnapshots('emergency', $reportIds);
+ $recipientCount = $this->getPatientsCountFromSnapshot('recipient', $reportIds);
+ $outcomeCount = $this->getPatientsCountFromSnapshot('outcome', $reportIds);
+ $currentCount = $this->getPatientsCountFromSnapshot('current', $reportIds);
+ $deadCount = $this->getPatientsCountFromSnapshot('deceased', $reportIds);
// Для операций все равно используем реплику с фильтрацией по датам
$surgicalCount = [
- $this->getSurgicalPatients('plan', $isHeadOrAdmin, $branchId, $startDate, $endDate, true),
- $this->getSurgicalPatients('emergency', $isHeadOrAdmin, $branchId, $startDate, $endDate, true)
+ $this->getSurgicalPatientsFromSnapshot('plan', $reportIds),
+ $this->getSurgicalPatientsFromSnapshot('emergency', $reportIds)
];
$recipientIds = $this->getRecipientIdsFromSnapshots($reportIds);
@@ -157,12 +166,40 @@ class ReportController extends Controller
);
}
+ $isActiveSendButton = Carbon::createFromFormat('Y-m-d H:i:s', $endDate)->isToday() &&
+ (!$user->isHeadOfDepartment() && !$user->isAdmin()) && $reportToday == null;
+
+ $reportDoctor = $reportToday?->lpuDoctor;
+ $message = null;
+ if ($reportToday) {
+ if ($reportDoctor && $reportDoctor->LPUDoctorID === intval($fillableUserId)) {
+ $isActiveSendButton = true;
+ } else {
+ $message = "Отчет уже создан пользователем: $reportDoctor->FAM_V $reportDoctor->IM_V $reportDoctor->OT_V";
+ }
+
+ $lpuDoctor = $reportDoctor;
+ } else {
+ if (Carbon::parse($startDate)->diffInDays(Carbon::parse($endDate)) > 1.0) {
+ $lpuDoctor = null;
+ } else {
+ $lpuDoctor = MisLpuDoctor::where('LPUDoctorID', $fillableUserId)->first();
+ }
+ }
+
+ $isRangeOneDay = $this->dateService->isRangeOneDay($startDate, $endDate);
+
+ $date = $isHeadOrAdmin ? [
+ $this->dateService->parseDate($isRangeOneDay ? $endDate : $startDate)->getTimestampMs(),
+ $this->dateService->parseDate($endDate)->getTimestampMs()
+ ] : $this->dateService->parseDate($endDate)->getTimestampMs();
+
return response()->json([
'department' => [
'beds' => $beds,
'percentLoadedBeds' => $percentLoadedBeds,
- 'recipientCount' => $plan + $emergency, //$recipientCount,
+ 'recipientCount' => $useSnapshots ? $recipientCount : $plan + $emergency, //$recipientCount,
'extractCount' => $outcomeCount, //$extractedCount,
'currentCount' => $currentCount,
'deadCount' => $deadCount,
@@ -174,8 +211,13 @@ class ReportController extends Controller
'endAt' => $endDateCarbon->getTimestampMs()
],
'report' => [
+ 'report_id' => $reportToday?->report_id,
'unwantedEvents' => $unwantedEvents,
- 'isActiveSendButton' => Carbon::createFromFormat('Y-m-d H:i:s', $endDate)->isToday() && (!$user->isHeadOfDepartment() && !$user->isAdmin()),
+ 'isActiveSendButton' => $isActiveSendButton,
+ 'message' => $message,
+ 'isOneDay' => $isRangeOneDay,
+ 'isHeadOrAdmin' => $isHeadOrAdmin,
+ 'dates' => $date
],
'metrikaItems' => $metrikaItems,
'userId' => $fillableUserId,
@@ -183,6 +225,68 @@ class ReportController extends Controller
]);
}
+ private function getSurgicalPatientsFromSnapshot(string $type, array $reportIds)
+ {
+ $count = 0;
+ switch ($type) {
+ case 'emergency':
+ $count = $this->getMetrikaResult(10, $reportIds);
+ break;
+ case 'plan':
+ $count = $this->getMetrikaResult(11, $reportIds);
+ break;
+ case 'recipient':
+ $count = $this->getMetrikaResult(3, $reportIds);
+ break;
+ }
+
+ return $count;
+ }
+
+ private function getPatientsCountFromSnapshot(string $type, array $reportIds)
+ {
+ $count = 0;
+ switch ($type) {
+ case 'emergency':
+ $count = $this->getMetrikaResult(10, $reportIds);
+ break;
+ case 'plan':
+ $count = $this->getMetrikaResult(11, $reportIds);
+ break;
+ case 'recipient':
+ $count = $this->getMetrikaResult(3, $reportIds);
+ break;
+ case 'outcome':
+ $count = $this->getMetrikaResult(7, $reportIds);
+ break;
+ case 'deceased':
+ $count = $this->getMetrikaResult(9, $reportIds);
+ break;
+ case 'current':
+ $count = $this->getMetrikaResult(8, $reportIds);
+ break;
+ }
+
+ return $count;
+ }
+
+ private function getMetrikaResult(int $metrikaItemId, array $reportIds)
+ {
+ $reports = Report::whereIn('report_id', $reportIds)
+ ->with('metrikaResults')
+ ->get();
+
+ $count = 0;
+ foreach ($reports as $report) {
+ foreach ($report->metrikaResults as $metrikaResult) {
+ if ($metrikaResult->rf_metrika_item_id === $metrikaItemId)
+ $count += intval($metrikaResult->value) ?? 0;
+ }
+ }
+
+ return $count;
+ }
+
/**
* Получить количество пациентов из снапшотов
*/
@@ -213,6 +317,10 @@ class ReportController extends Controller
->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
};
}
@@ -250,19 +358,13 @@ class ReportController extends Controller
*/
private function getRecipientIdsFromSnapshots(array $reportIds)
{
- $planIds = MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds)
- ->where('patient_type', 'plan')
+ $recipientIds = MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds)
+ ->where('patient_type', 'recipient')
->pluck('rf_medicalhistory_id')
->unique()
->toArray();
- $emergencyIds = MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds)
- ->where('patient_type', 'emergency')
- ->pluck('rf_medicalhistory_id')
- ->unique()
- ->toArray();
-
- return array_merge($planIds, $emergencyIds);
+ return $recipientIds;
}
// /**
@@ -310,6 +412,7 @@ class ReportController extends Controller
'unwantedEvents' => 'nullable|array',
'dates' => 'required|array',
'userId' => 'required|integer',
+ 'reportId' => 'nullable'
]);
$metrics = $data['metrics'];
$observationPatients = $data['observationPatients'];
@@ -330,13 +433,28 @@ class ReportController extends Controller
\DB::beginTransaction();
- $report = Report::create([
- 'rf_department_id' => $data['departmentId'],
- 'rf_user_id' => Auth::user()->id,
- 'rf_lpudoctor_id' => $data['userId'],
- 'created_at' => now(),
- 'sent_at' => now()
- ]);
+ if (isset($data['reportId']) && $data['reportId']) {
+ $report = Report::updateOrCreate(
+ [
+ 'report_id' => $data['reportId']
+ ],
+ [
+ 'rf_department_id' => $data['departmentId'],
+ 'rf_user_id' => Auth::user()->id,
+ 'rf_lpudoctor_id' => $data['userId'],
+ 'created_at' => now(),
+ 'sent_at' => now()
+ ]
+ );
+ } else {
+ $report = Report::create([
+ 'rf_department_id' => $data['departmentId'],
+ 'rf_user_id' => Auth::user()->id,
+ 'rf_lpudoctor_id' => $data['userId'],
+ 'created_at' => now(),
+ 'sent_at' => now()
+ ]);
+ }
if (count($unwantedEvents)) {
foreach ($unwantedEvents as $unwantedEvent) {
@@ -361,11 +479,25 @@ class ReportController extends Controller
]);
}
}
+ } else {
+ $unwantedEvents = $report->unwantedEvents;
+ foreach ($unwantedEvents as $unwantedEvent) {
+ $unwantedEvent->delete();
+ }
}
foreach ($metriks as $metrika) {
- $metrika->rf_report_id = $report->report_id;
- $metrika->save();
+ MetrikaResult::updateOrCreate(
+ [
+ 'rf_report_id' => $report->report_id,
+ 'rf_metrika_item_id' => $metrika->rf_metrika_item_id
+ ],
+ [
+ 'rf_report_id' => $report->report_id,
+ 'rf_metrika_item_id' => $metrika->rf_metrika_item_id,
+ 'value' => $metrika->value,
+ ]
+ );
}
if (count($observationPatients)) {
@@ -384,6 +516,10 @@ class ReportController extends Controller
]
);
}
+ } else {
+ foreach ($report->observationPatients as $observationPatient) {
+ $observationPatient->delete();
+ }
}
// Сохраняем снимок для каждого типа пациентов
@@ -438,6 +574,26 @@ class ReportController extends Controller
]);
}
+ // 5. Поступившие
+ $recipientIds = $this->getPlanOrEmergencyPatients(
+ null,
+ $isHeadOrAdmin,
+ $branchId,
+ $startDate,
+ $endDate,
+ false,
+ true,
+ true,
+ today: true
+ );
+ foreach ($recipientIds as $id) {
+ MedicalHistorySnapshot::create([
+ 'rf_report_id' => $report->report_id,
+ 'rf_medicalhistory_id' => $id,
+ 'patient_type' => 'recipient'
+ ]);
+ }
+
// 6. Находящиеся на лечении
// $currentIds = $this->getCurrentPatients($branchId, false, true);
// foreach ($currentIds as $id) {
@@ -1029,10 +1185,16 @@ class ReportController extends Controller
*/
private function getReportsForDateRange($departmentId, $startDate, $endDate)
{
- return Report::where('rf_department_id', $departmentId)
- ->whereBetween('created_at', [$startDate, $endDate])
- ->orderBy('created_at', 'ASC')
- ->get();
+ if (Carbon::parse($startDate)->diffInDays(Carbon::parse($endDate)) > 1.0)
+ return Report::where('rf_department_id', $departmentId)
+ ->whereBetween('created_at', [$startDate, $endDate])
+ ->orderBy('created_at', 'ASC')
+ ->get();
+ else
+ return Report::where('rf_department_id', $departmentId)
+ ->whereDate('created_at', $endDate)
+ ->orderBy('created_at', 'ASC')
+ ->get();
}
/**
diff --git a/app/Http/Controllers/Web/ReportController.php b/app/Http/Controllers/Web/ReportController.php
index aef8976..437b71d 100644
--- a/app/Http/Controllers/Web/ReportController.php
+++ b/app/Http/Controllers/Web/ReportController.php
@@ -3,22 +3,118 @@
namespace App\Http\Controllers\Web;
use App\Http\Controllers\Controller;
+use App\Models\MisStationarBranch;
+use App\Models\Report;
+use App\Services\DateRangeService;
+use App\Services\MisPatientService;
use Illuminate\Http\Request;
+use Illuminate\Support\Facades\DB;
use Inertia\Inertia;
class ReportController extends Controller
{
+ protected DateRangeService $dateService;
+ protected MisPatientService $misPatientService;
+
+ public function __construct(MisPatientService $misPatientService, DateRangeService $dateRangeService)
+ {
+ $this->misPatientService = $misPatientService;
+ $this->dateService = $dateRangeService;
+ }
+
public function index(Request $request)
{
- $user = $request->user();
+ $user = \Auth::user();
$department = $user->department;
- $beds = $department->metrikaDefault()->where('rf_metrika_item_id', 1)->first()->value;
+ $queryStartDate = $request->query('startAt');
+ $queryEndDate = $request->query('endAt');
+ [$startDate, $endDate] = $this->dateService->getDateRangeForUser($user, $queryStartDate, $queryEndDate);
+ $isRangeOneDay = $this->dateService->isRangeOneDay($startDate, $endDate);
+
+ // Если диапазон содержит сутки
+ if ($isRangeOneDay) {
+ // Устанавливаем дату отчета, как последний день из выборки
+ $dateReport = $endDate;
+ } else {
+ // Устанавливаем дату отчета, как выборку
+ $dateReport = [$startDate, $endDate];
+ }
+
+ if ($isRangeOneDay) {
+ // Статистика выводится с нарастающим числом
+ $reports = $department->reports()
+ ->whereDate('created_at', $dateReport)
+ ->get();
+ } else {
+ $reports = $department->reports()
+ ->whereBetween('created_at', $dateReport)
+ ->get();
+ }
+
+ $isReports = $reports->count() > 0;
+
+ $allCount = 0; $outcomeCount = 0; $currentCount = 0; $occupiedBeds = 0; $planCount = 0;
+ $emergencyCount = 0; $planSurgical = 0; $emergencySurgical = 0; $transferredCount = 0;
+ $deceasedCount = 0;
+ if ($isReports) {
+ 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);
+ }
+ } else {
+ $misDepartmentId = $request->user()->department->rf_mis_department_id;
+
+ $branchId = MisStationarBranch::where('rf_DepartmentID', $misDepartmentId)
+ ->value('StationarBranchID');
+
+ $planCount = $this->misPatientService->getInStationarPatients('plan', $branchId, $dateReport)->count();
+ }
+
+ $bedsCount = $department->metrikaDefault()
+ ->where('rf_metrika_item_id', 1)->value('value');
+
+ $percentLoadedBeds = $bedsCount > 0 ? round($occupiedBeds * 100 / $bedsCount) : 0;
return Inertia::render('Report/Index', [
'department' => [
- 'beds' => $beds
+ 'beds' => $bedsCount,
+ 'recipients' => [
+ 'all' => $allCount,
+ 'plan' => $planCount,
+ 'emergency' => $emergencyCount,
+ 'transferred' => $transferredCount,
+ ],
+ 'outcome' => $outcomeCount,
+ 'consist' => $currentCount,
+ 'percentLoadedBeds' => $percentLoadedBeds,
+ 'surgical' => [
+ 'plan' => $planSurgical,
+ 'emergency' => $emergencySurgical
+ ],
+ 'deceased' => $deceasedCount,
],
]);
}
+
+ private function getMetrikaResultFromReport(Report $report, int $metrikaItem, bool $sum = true)
+ {
+ if ($sum) {
+ return (int) ($report->metrikaResults()
+ ->where('rf_metrika_item_id', $metrikaItem)
+ ->sum(DB::raw('CAST(value AS INTEGER)')) ?: 0);
+ }
+
+ return (int) ($report->metrikaResults()
+ ->where('rf_metrika_item_id', $metrikaItem)
+ ->value('value') ?: 0);
+ }
}
diff --git a/app/Http/Controllers/Web/StatisticController.php b/app/Http/Controllers/Web/StatisticController.php
index 08e7aa3..783dd77 100644
--- a/app/Http/Controllers/Web/StatisticController.php
+++ b/app/Http/Controllers/Web/StatisticController.php
@@ -9,6 +9,7 @@ use App\Models\MetrikaGroup;
use App\Models\MetrikaItem;
use App\Models\MetrikaResult;
use App\Models\Report;
+use App\Services\DateRangeService;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
@@ -18,65 +19,141 @@ use Inertia\Inertia;
class StatisticController extends Controller
{
+ protected DateRangeService $dateService;
+
+ public function __construct(DateRangeService $dateRangeService)
+ {
+ $this->dateService = $dateRangeService;
+ }
+
public function index(Request $request)
{
$user = $request->user();
- $userDepartment = $user->department;
+ $queryStartDate = $request->query('startAt');
+ $queryEndDate = $request->query('endAt');
+ [$startDate, $endDate] = $this->dateService->getDateRangeForUser($user, $queryStartDate, $queryEndDate);
+ $isRangeOneDay = $this->dateService->isRangeOneDay($startDate, $endDate);
- $data = [];
+ // Если диапазон содержит сутки
+ if ($isRangeOneDay) {
+ // Устанавливаем дату отчета, как последний день из выборки
+ $dateReport = $endDate;
+ } else {
+ // Устанавливаем дату отчета, как выборку
+ $dateReport = [$startDate, $endDate];
+ }
- $departments = Department::select('department_id', 'name_short')
+ $groupedData = [];
+
+ $departments = Department::select('department_id', 'rf_department_type', 'name_short')
+ ->with(['reports'])
->orderBy('name_short')->get();
foreach ($departments as $department) {
- $allCount = MetrikaResult::whereHas('report', function ($query) use ($userDepartment, $department) {
- $query->where('rf_department_id', $department->department_id);
- })->where('rf_metrika_item_id', 3)
- ->sum(DB::raw('value::integer'));
+ $departmentType = $department->departmentType->name_full;
- $leaveCount = MetrikaResult::whereHas('report', function ($query) use ($userDepartment, $department) {
- $query->where('rf_department_id', $department->department_id);
- })->where('rf_metrika_item_id', 7)
- ->sum(DB::raw('value::integer'));
+ if (!isset($groupedData[$departmentType])) {
+ $groupedData[$departmentType] = [];
+ }
- $consistCount = optional(MetrikaResult::where('rf_metrika_item_id', 8)
- ->whereHas('report', function (Builder $query) use ($department) {
- $query->where('rf_department_id', $department->department_id);
- })->join('reports', 'metrika_results.rf_report_id', '=', 'reports.report_id')
- ->select('metrika_results.value')
- ->orderBy('reports.sent_at', 'desc')
- )->value('value') ?? 0;
+ if ($isRangeOneDay) {
+ // Статистика выводится с нарастающим числом
+ $reports = $department->reports()
+ ->whereDate('created_at', $dateReport)
+ ->get();
+ } else {
+ $reports = $department->reports()
+ ->whereBetween('created_at', $dateReport)->get();
+ }
+ // Метрики зависищие от отчетов
+ $allCount = 0; $outcomeCount = 0; $currentCount = 0; $occupiedBeds = 0; $planCount = 0;
+ $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);
+ }
- $beds = (int)optional($department->metrikaDefault()
- ->where('rf_metrika_item_id', 1)
- ->first())->value ?? 0;
+ // Независимые метрики (установки по умолчанию и т.п.)
+ $bedsCount = $department->metrikaDefault()
+ ->where('rf_metrika_item_id', 1)->value('value');
- $occupiedBeds = (int)optional(Report::where('rf_department_id', $department->department_id)
- ->join('metrika_results', 'reports.report_id', '=', 'metrika_results.rf_report_id')
- ->where('metrika_results.rf_metrika_item_id', 8)
- ->orderBy('reports.sent_at', 'desc')
- ->first())->value ?? 0;
+ $percentLoadedBeds = $bedsCount > 0 ? round($occupiedBeds * 100 / $bedsCount) : 0;
- $percentLoadedBeds = $beds > 0 ? round($occupiedBeds * 100 / $beds) : 0;
-
- $data[] = [
+ $groupedData[$departmentType][] = [
'department' => $department->name_short,
- 'beds' => $beds,
- 'all' => $allCount,
- 'plan' => '0',
- 'emergency' => '0',
- 'leave' => $leaveCount,
- 'consist' => $consistCount,
+ 'beds' => $bedsCount,
+ 'recipients' => [
+ 'all' => $allCount,
+ 'plan' => $planCount,
+ 'emergency' => $emergencyCount,
+ 'transferred' => $transferredCount,
+ ],
+ 'outcome' => $outcomeCount,
+ 'consist' => $currentCount,
'percentLoadedBeds' => $percentLoadedBeds,
+ 'surgical' => [
+ 'plan' => $planSurgical,
+ 'emergency' => $emergencySurgical
+ ],
+ 'deceased' => $deceasedCount,
+ 'type' => $departmentType
];
}
+ // Преобразуем группированные данные в плоский массив с заголовками групп
+ $finalData = [];
+ foreach ($groupedData as $type => $departmentsInType) {
+ // Добавляем строку-заголовок группы
+ $finalData[] = [
+ 'isGroupHeader' => true,
+ 'groupName' => $type,
+ 'colspan' => 12, // Количество колонок в таблице
+ 'type' => $type
+ ];
+
+ // Добавляем отделения этой группы
+ foreach ($departmentsInType as $department) {
+ $finalData[] = $department;
+ }
+ }
+
+ $isHeadOrAdmin = $user->isAdmin() || $user->isHeadOfDepartment();
+ $date = $isHeadOrAdmin ? [
+ $this->dateService->parseDate($isRangeOneDay ? $endDate : $startDate)->getTimestampMs(),
+ $this->dateService->parseDate($endDate)->getTimestampMs()
+ ] : $this->dateService->parseDate($endDate)->getTimestampMs();
+
return Inertia::render('Statistic/Index', [
- 'data' => $data
+ 'data' => $finalData,
+ 'isHeadOrAdmin' => $isHeadOrAdmin,
+ 'date' => $date,
+ 'isOneDay' => $isRangeOneDay
]);
}
+ private function getMetrikaResultFromReport(Report $report, int $metrikaItem, bool $sum = true)
+ {
+ if ($sum) {
+ return (int) ($report->metrikaResults()
+ ->where('rf_metrika_item_id', $metrikaItem)
+ ->sum(DB::raw('CAST(value AS INTEGER)')) ?: 0);
+ }
+
+ return (int) ($report->metrikaResults()
+ ->where('rf_metrika_item_id', $metrikaItem)
+ ->value('value') ?: 0);
+ }
+
public function indexOld(Request $request)
{
$user = Auth::user();
diff --git a/app/Models/Department.php b/app/Models/Department.php
index 46e4cb1..f0aa74a 100644
--- a/app/Models/Department.php
+++ b/app/Models/Department.php
@@ -31,4 +31,9 @@ class Department extends Model
{
return $this->hasMany(Report::class, 'rf_department_id', 'department_id');
}
+
+ public function departmentType()
+ {
+ return $this->belongsTo(DepartmentType::class, 'rf_department_type', 'department_type_id');
+ }
}
diff --git a/app/Models/Report.php b/app/Models/Report.php
index db7ce9c..7bc496f 100644
--- a/app/Models/Report.php
+++ b/app/Models/Report.php
@@ -31,4 +31,14 @@ class Report extends Model
{
return $this->hasMany(UnwantedEvent::class, 'rf_report_id', 'report_id');
}
+
+ public function user()
+ {
+ return $this->belongsTo(User::class, 'rf_user_id');
+ }
+
+ public function lpuDoctor()
+ {
+ return $this->belongsTo(MisLpuDoctor::class, 'rf_lpudoctor_id');
+ }
}
diff --git a/app/Models/User.php b/app/Models/User.php
index a086738..2bb88ca 100644
--- a/app/Models/User.php
+++ b/app/Models/User.php
@@ -103,6 +103,11 @@ class User extends Authenticatable
return $this->currentRole()->slug === 'head_of_department';
}
+ public function lpuDoctor()
+ {
+ return $this->belongsTo(MisLpuDoctor::class, 'rf_lpudoctor_id');
+ }
+
// Получение доступных отделений
public function availableDepartments()
{
diff --git a/app/Services/DateRangeService.php b/app/Services/DateRangeService.php
index 089b034..6e0b644 100644
--- a/app/Services/DateRangeService.php
+++ b/app/Services/DateRangeService.php
@@ -15,6 +15,18 @@ class DateRangeService
return $this->getDefaultDateRange($user);
}
+ public function isRangeOneDay($startAt = null, $endAt = null): bool
+ {
+ if (!$startAt || !$endAt) return false;
+
+ $startDate = $this->parseDate($startAt);
+ $endDate = $this->parseDate($endAt);
+
+ if ($startDate->diffInDays($endDate) === 1.0) return true;
+
+ return false;
+ }
+
private function getCustomDateRange($startAt, $endAt, $user): array
{
$startDate = $this->parseDate($startAt);
@@ -36,21 +48,12 @@ class DateRangeService
private function getDefaultDateRange($user): array
{
- if ($user->isHeadOfDepartment() || $user->isAdmin()) {
- $startDate = Carbon::now('Asia/Yakutsk')
- ->firstOfMonth()
- ->setTime(6, 0);
+ $startDate = Carbon::now('Asia/Yakutsk')
+ ->subDay()
+ ->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);
- }
+ $endDate = Carbon::now('Asia/Yakutsk')
+ ->setTime(6, 0);
return [
$startDate->format('Y-m-d H:i:s'),
@@ -58,7 +61,7 @@ class DateRangeService
];
}
- private function parseDate($dateInput): Carbon
+ public function parseDate($dateInput): Carbon
{
if (is_numeric($dateInput)) {
return Carbon::createFromTimestampMs($dateInput)
diff --git a/app/Services/MisPatientService.php b/app/Services/MisPatientService.php
new file mode 100644
index 0000000..d84be2b
--- /dev/null
+++ b/app/Services/MisPatientService.php
@@ -0,0 +1,100 @@
+getPlanPatientsQuery($branchId, $dateRange);
+ break;
+ case 'emergency':
+ $query = $this->getEmergencyPatientsQuery($branchId, $dateRange);
+ break;
+ }
+ }
+
+ /**
+ * Получение текущих пациентов по MigrationPatient
+ */
+ public function getInStationarPatients(string $status, $branchId, string|array $dateRange)
+ {
+ $emerSign = $status === 'plan' ? 1 : [2,4];
+ $query = MisMedicalHistory::query();
+ $query->when(isset($branchId), function ($query) use ($branchId, $dateRange) {
+ $query->with(['migrations', 'surgicalOperations'])
+ ->whereHas('migrations', function ($q) use ($branchId, $dateRange) {
+ $q->where('rf_StationarBranchID', $branchId)
+ ->whereDate('DateRecipient', '<', is_array($dateRange) ? $dateRange[1] : $dateRange)
+ ->whereDate('DateOut', '=', '2222-01-01 00:00:00.000')
+ ->where('rf_kl_StatCureResultID', 0)
+ ->where('rf_kl_VisitResultID', 0);
+ });
+ })
+ ->where('rf_EmerSignID', $emerSign)
+ ->where('MedicalHistoryID', '<>', 0);
+
+ return $query;
+ }
+
+ /**
+ * Получение запроса плановых пациентов по MigrationPatient
+ */
+ public function getPlanPatientsQuery($branchId, $dateRange)
+ {
+ $query = MisMedicalHistory::query();
+ $query->when(isset($branchId), function ($query) use ($branchId, $dateRange) {
+ $query->with('migrations')
+ ->whereHas('migrations', function ($q) use ($branchId, $dateRange) {
+ $q->where('rf_StationarBranchID', $branchId)
+ ->whereDate('DateRecipient', '<', $dateRange[1]);
+ });
+ })
+ ->where('rf_EmerSignID', 1)
+ ->where('MedicalHistoryID', '<>', 0);
+
+ return $query;
+ }
+
+ /**
+ * Получение запроса экстренных + неотложных пациентов по MedicalHistory
+ */
+ public function getEmergencyPatientsQuery($branchId, $dateRange)
+ {
+ $query = MisMedicalHistory::query();
+ $query->when(isset($branchId), function ($query) use ($branchId, $dateRange) {
+ $query->with('migrations')
+ ->whereHas('migrations', function ($q) use ($branchId, $dateRange) {
+ $q->where('rf_StationarBranchID', $branchId)
+ ->whereBetween('DateIngoing', $dateRange);
+ });
+ })
+ ->whereIn('rf_EmerSignID', [2, 4])
+ ->where('MedicalHistoryID', '<>', 0);
+
+ return $query;
+ }
+
+ /**
+ * Получение запроса пациентов которые попали в отделение в определенную дату
+ * @param int $branchId Индентификатор ветки стационара
+ * @param array $dateRange Массив дат (0 - начальная дата, 1 - конечная дата)
+ * @return \Illuminate\Database\Eloquent\Builder
+ */
+ public function getRecipientPatientsQuery(int $branchId, array $dateRange): \Illuminate\Database\Eloquent\Builder
+ {
+ $query = MisMedicalHistory::query();
+ $query->with('migrations')
+ ->whereHas('migrations', function ($q) use ($branchId, $dateRange) {
+ $q->where('rf_StationarBranchID', $branchId)
+ ->whereBetween('DateIngoing', $dateRange);
+ })->where('MedicalHistoryID', '<>', 0);
+
+ return $query;
+ }
+}
diff --git a/resources/js/Components/DatePickerQuery.vue b/resources/js/Components/DatePickerQuery.vue
new file mode 100644
index 0000000..c43760f
--- /dev/null
+++ b/resources/js/Components/DatePickerQuery.vue
@@ -0,0 +1,117 @@
+
+
+
+
+
+ {{ formattedValue }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/js/Components/ReportSelectDate.vue b/resources/js/Components/ReportSelectDate.vue
index 51caa40..d4e7017 100644
--- a/resources/js/Components/ReportSelectDate.vue
+++ b/resources/js/Components/ReportSelectDate.vue
@@ -1,9 +1,17 @@
-
+
+
+ {{ formattedValue }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/js/Layouts/AppLayout.vue b/resources/js/Layouts/AppLayout.vue
index e74128d..8684e5b 100644
--- a/resources/js/Layouts/AppLayout.vue
+++ b/resources/js/Layouts/AppLayout.vue
@@ -22,7 +22,11 @@ const themeOverrides = {
-
+
+
+
+
+
diff --git a/resources/js/Layouts/Components/AppHeader.vue b/resources/js/Layouts/Components/AppHeader.vue
index f2f850e..ee28895 100644
--- a/resources/js/Layouts/Components/AppHeader.vue
+++ b/resources/js/Layouts/Components/AppHeader.vue
@@ -4,13 +4,21 @@ import ReportSelectDate from "../../Components/ReportSelectDate.vue";
import AppUserButton from "./AppUserButton.vue";
import {Link} from "@inertiajs/vue3";
import AppHeaderRole from "./AppHeaderRole.vue";
+import {computed, useSlots} from "vue";
+
+const slots = useSlots()
+const hasHeaderExtra = computed(() => !!slots.headerExtra)
-
- Метрика
-
+
+
+ Метрика
+
+
+
+
diff --git a/resources/js/Pages/Index.vue b/resources/js/Pages/Index.vue
index 046916e..373d6f8 100644
--- a/resources/js/Pages/Index.vue
+++ b/resources/js/Pages/Index.vue
@@ -54,7 +54,7 @@ const reportButtonType = computed(() => authStore.isDoctor ? 'button' : Link)
/>
-import { NFlex, NButton } from 'naive-ui'
+import { NFlex, NAlert, NButton } from 'naive-ui'
import ReportHeader from "./ReportHeader.vue";
import ReportFormInput from "./ReportFormInput.vue";
import ReportSection from "./ReportSection.vue";
@@ -28,6 +28,9 @@ const onSubmit = () => {
+
+ {{ reportStore.reportInfo.report.message }}
+
diff --git a/resources/js/Pages/Report/Components/ReportFormInput.vue b/resources/js/Pages/Report/Components/ReportFormInput.vue
index bc424b2..886ffe2 100644
--- a/resources/js/Pages/Report/Components/ReportFormInput.vue
+++ b/resources/js/Pages/Report/Components/ReportFormInput.vue
@@ -5,6 +5,7 @@ import {useAuthStore} from "../../../Stores/auth.js";
const reportStore = useReportStore()
const authStore = useAuthStore()
+
@@ -66,4 +67,8 @@ const authStore = useAuthStore()
:deep(.n-statistic-value) {
@apply flex justify-center items-center;
}
+
+:deep(.n-input-wrapper) {
+ width: 120px;
+}
diff --git a/resources/js/Pages/Report/Components/ReportHeader.vue b/resources/js/Pages/Report/Components/ReportHeader.vue
index 09981a0..d9c159d 100644
--- a/resources/js/Pages/Report/Components/ReportHeader.vue
+++ b/resources/js/Pages/Report/Components/ReportHeader.vue
@@ -66,7 +66,7 @@ const currentDate = computed(() => {
-
+
diff --git a/resources/js/Pages/Report/Components/ReportSection.vue b/resources/js/Pages/Report/Components/ReportSection.vue
index b793484..ee431ad 100644
--- a/resources/js/Pages/Report/Components/ReportSection.vue
+++ b/resources/js/Pages/Report/Components/ReportSection.vue
@@ -41,7 +41,7 @@ const isReadonlyMode = computed(() => props.mode.toLowerCase() === 'readonly')
-
+
diff --git a/resources/js/Pages/Statistic/Index.vue b/resources/js/Pages/Statistic/Index.vue
index 3a2e55b..270277a 100644
--- a/resources/js/Pages/Statistic/Index.vue
+++ b/resources/js/Pages/Statistic/Index.vue
@@ -1,12 +1,22 @@
+
+
+
diff --git a/resources/js/Stores/report.js b/resources/js/Stores/report.js
index 3f0c479..8ba6830 100644
--- a/resources/js/Stores/report.js
+++ b/resources/js/Stores/report.js
@@ -23,6 +23,9 @@ export const useReportStore = defineStore('reportStore', () => {
const dataOnReport = ref(null)
+ // Открытие collapse из ReportSectionItem
+ const openedCollapsible = ref([])
+
const reportInfo = ref({
userId: null
})
@@ -85,6 +88,7 @@ export const useReportStore = defineStore('reportStore', () => {
unwantedEvents: unwantedEvents.value,
dates: timestampCurrentRange.value,
userId: reportInfo.value.userId,
+ reportId: reportInfo.value.report.report_id,
...assignForm
}
@@ -122,7 +126,7 @@ export const useReportStore = defineStore('reportStore', () => {
reportForm.value.metrika_item_3 = reportInfo.value.department?.recipientCount
reportForm.value.metrika_item_7 = reportInfo.value.department?.extractCount
- reportForm.value.metrika_item_8 = reportInfo.value.department?.currentCounts
+ reportForm.value.metrika_item_8 = reportInfo.value.department?.currentCount
reportForm.value.metrika_item_9 = reportInfo.value.department?.deadCount
reportForm.value.metrika_item_10 = reportInfo.value.department?.surgicalCount[1]
@@ -143,6 +147,13 @@ export const useReportStore = defineStore('reportStore', () => {
const getDataOnReportDate = async (dateRange) => {
isLoadReportInfo.value = true
timestampCurrentRange.value = dateRange
+ openedCollapsible.value = []
+ patientsData.value = {
+ plan: [],
+ emergency: [],
+ observation: [],
+ outcome: []
+ }
const queryParams = {
userId: reportInfo.value.userId,
startAt: timestampCurrentRange.value[0],
@@ -185,6 +196,7 @@ export const useReportStore = defineStore('reportStore', () => {
reportForm,
departmentUsers,
unwantedEvents,
+ openedCollapsible,
getColumnsByKey,
getDataOnReportDate,
diff --git a/resources/js/Utils/dateFormatter.js b/resources/js/Utils/dateFormatter.js
new file mode 100644
index 0000000..9131dff
--- /dev/null
+++ b/resources/js/Utils/dateFormatter.js
@@ -0,0 +1,46 @@
+import { format } from 'date-fns'
+import { ru } from 'date-fns/locale'
+
+/**
+ * Делает первую букву строки заглавной
+ */
+const capitalizeFirst = (str) => {
+ if (!str) return ''
+ return str.charAt(0).toUpperCase() + str.slice(1)
+}
+
+/**
+ * Форматирует дату по-русски: "Вторник, 3 февраля 2026 г."
+ */
+export const formatRussianDate = (date) => {
+ if (!date) return ''
+
+ const formatted = format(new Date(date), 'EEEE, d MMMM yyyy г.', { locale: ru })
+ return capitalizeFirst(formatted)
+}
+
+/**
+ * Форматирует диапазон дат по-русски:
+ * "С понедельника, 2 февраля 2026 г. по вторник, 3 февраля 2026 г."
+ */
+export const formatRussianDateRange = (dateRange) => {
+ if (!dateRange || !Array.isArray(dateRange) || dateRange.length < 2) {
+ return ''
+ }
+
+ const [startDate, endDate] = dateRange
+
+ if (!startDate || !endDate) return ''
+
+ const formattedStart = format(new Date(startDate), 'd MMMM yyyy г.', { locale: ru })
+ const formattedEnd = format(new Date(endDate), 'd MMMM yyyy г.', { locale: ru })
+
+ return `С ${formattedStart.toLowerCase()} по ${formattedEnd.toLowerCase()}`
+}
+
+/**
+ * Для совместимости со старым кодом
+ */
+export const formatDateWithCapital = (date) => {
+ return formatRussianDate(date)
+}