Отчет для врача дежурного

This commit is contained in:
Brusnitsyn
2026-07-06 07:37:53 +00:00
parent 1d9fbb362e
commit 7ef20061f0
3 changed files with 179 additions and 34 deletions

View File

@@ -4,38 +4,110 @@ namespace App\Exports;
use App\Models\Department; use App\Models\Department;
use App\Models\User; use App\Models\User;
use Maatwebsite\Excel\Concerns\FromArray; use Illuminate\Support\Carbon;
use Maatwebsite\Excel\Concerns\WithTitle; use Illuminate\Support\Collection;
class DutyReportExport implements FromArray, WithTitle class DutyReportExport
{ {
protected array $data; private const RU_MONTHS = [
protected array $dateRange; 1 => 'январь', 2 => 'февраль', 3 => 'март', 4 => 'апрель',
protected User $user; 5 => 'май', 6 => 'июнь', 7 => 'июль', 8 => 'август',
protected Department $department; 9 => 'сентябрь', 10 => 'октябрь', 11 => 'ноябрь', 12 => 'декабрь',
protected string $reportName; ];
public function __construct( public function __construct(
array $data, protected array $patients,
array $dateRange, protected array $dateRange,
User $user, protected User $user,
Department $department, protected Department $department,
string $reportName = 'Статистика по отделениям', protected string $reportName = 'Отчёт дежурного врача по приёмному покою и отделению',
) { ) {}
$this->data = $data;
$this->dateRange = $dateRange; /**
$this->user = $user; * Данные для Blade-шаблона PDF-отчёта.
$this->department = $department; */
$this->reportName = $reportName; public function toViewData(): array
{
$patients = collect($this->patients);
$endDate = Carbon::parse($this->dateRange[1]);
return [
'title' => sprintf('%s «%s»', $this->reportName, $this->department->name_full ?? $this->department->name_short),
'day' => $endDate->format('d'),
'month' => self::RU_MONTHS[(int) $endDate->format('n')],
'year' => $endDate->format('Y'),
'planned' => $this->urgencyGroup($patients, 'planned'),
'urgent' => $this->urgencyGroup($patients, 'urgent'),
'inDepartment' => $patients->where('period_flags.current_at_end', true)->count(),
'plannedOperations' => $this->operationsGroup($patients, [6]),
'urgentOperations' => $this->operationsGroup($patients, [4, 5]),
'reanimation' => $this->flagGroup($patients, 'in_reanimation'),
'observableCount' => $patients->where('in_observable', true)->count(),
'deceased' => $this->flagGroup($patients, 'period_flags.deceased'),
];
} }
public function array(): array private function urgencyGroup(Collection $patients, string $flag): array
{ {
// TODO: Implement array() method. $filtered = $patients
->where('period_flags.current_at_end', true)
->where("period_flags.{$flag}", true)
->values();
return [
'count' => $filtered->count(),
'patients' => $filtered->map(fn (array $p) => $this->presentPatient($p))->all(),
];
} }
public function title(): string private function operationsGroup(Collection $patients, array $urgentStatuses): array
{ {
return $this->sheetTitle; $filtered = $patients->filter(function (array $p) use ($urgentStatuses) {
return collect($p['operations'] ?? [])->whereIn('urgent_status', $urgentStatuses)->isNotEmpty();
})->values();
return [
'count' => $filtered->count(),
'patients' => $filtered->map(fn (array $p) => $this->presentPatient($p, withDiagnosis: false))->all(),
];
}
private function flagGroup(Collection $patients, string $flag): array
{
$filtered = $patients->where($flag, true)->values();
return [
'count' => $filtered->count(),
'patients' => $filtered->map(fn (array $p) => $this->presentPatient($p))->all(),
];
}
private function presentPatient(array $patient, bool $withDiagnosis = true): array
{
$age = ! empty($patient['birth_date'])
? Carbon::parse($patient['birth_date'])->diffInYears(Carbon::parse($this->dateRange[1]))
: null;
return [
'full_name' => $patient['full_name'],
'age' => $age !== null ? "{$age} ".$this->pluralizeAge($age) : '',
'diagnosis' => $withDiagnosis ? (data_get($patient, 'latest_migration.diagnosis_name') ?? '') : null,
];
}
private function pluralizeAge(int $age): string
{
$mod100 = $age % 100;
$mod10 = $age % 10;
if ($mod100 >= 11 && $mod100 <= 14) {
return 'лет';
}
return match ($mod10) {
1 => 'год',
2, 3, 4 => 'года',
default => 'лет',
};
} }
} }

View File

@@ -16,13 +16,12 @@ use App\Services\DutyReportService;
use App\Services\MedicalHistoryService; use App\Services\MedicalHistoryService;
use App\Services\NurseMedicalHistoryService; use App\Services\NurseMedicalHistoryService;
use App\Services\NurseReportService; use App\Services\NurseReportService;
use App\Services\StatisticsService;
use App\Services\UnifiedMedicalHistoryService; use App\Services\UnifiedMedicalHistoryService;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Carbon; use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Auth;
use Inertia\Inertia; use Inertia\Inertia;
use Maatwebsite\Excel\Facades\Excel;
class DutyReportController extends Controller class DutyReportController extends Controller
{ {
@@ -32,7 +31,6 @@ class DutyReportController extends Controller
protected DutyMedicalHistoryService $dutyMedicalHistoryService, protected DutyMedicalHistoryService $dutyMedicalHistoryService,
protected DutyReportService $dutyReportService, protected DutyReportService $dutyReportService,
protected NurseMedicalHistoryService $nurseMedicalHistoryService, protected NurseMedicalHistoryService $nurseMedicalHistoryService,
protected StatisticsService $statisticsService
) )
{} {}
@@ -362,22 +360,38 @@ class DutyReportController extends Controller
} }
/** /**
* Формирование отчета дежурного в Excel * Формирование отчета дежурного врача в PDF
*/ */
public function generateReport(Request $request) public function generateReport(Request $request)
{ {
$user = $request->user(); $user = $request->user();
$queryStartDate = $request->query('startAt');
$queryEndDate = $request->query('endAt');
$queryDepartmentId = $request->query('departmentId', $user->department->department_id); $queryDepartmentId = $request->query('departmentId', $user->department->department_id);
$department = Department::findOrFail($queryDepartmentId);
[$startDate, $endDate] = $this->dateRangeService->getStatisticsDateRange($user, $queryStartDate, $queryEndDate); $dateRange = $this->dateRangeService->getDateRangeFromRequest($request, $user);
$isRangeOneDay = $this->dateRangeService->isRangeOneDay($startDate, $endDate);
$department = Department::find($queryDepartmentId);
$finalData = $this->statisticsService->getStatisticsDataForDepartment($department->department_id, $startDate, $endDate, $isRangeOneDay); $reportDutyIds = ReportDuty::where('rf_department_id', $department->department_id)
->where('period_start', '>=', $dateRange->startSql())
->where('period_end', '<=', $dateRange->endSql())
->pluck('id')
->toArray();
return Excel::download(new DutyReportExport($finalData['data'], [$startDate, $endDate], $user, $department), 'statistics.xlsx'); $patients = $this->dutyMedicalHistoryService->getGroupedHistories(
$dateRange,
$department->rf_mis_department_id,
$reportDutyIds
);
$export = new DutyReportExport(
$patients['data'],
[$dateRange->startDateRaw, $dateRange->endDateRaw],
$user,
$department
);
return Pdf::loadView('reports.duty', $export->toViewData())
->setPaper('a4')
->download('duty-report.pdf');
} }
} }

View File

@@ -0,0 +1,59 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>{{ $title }}</title>
<style>
body { font-family: 'DejaVu Sans', sans-serif; font-size: 12px; color: #1f2430; line-height: 1.5; }
h1 { font-size: 14px; text-align: center; margin: 0 0 4px; }
.sub { text-align: center; margin: 0 0 16px; }
.label { font-weight: bold; margin: 10px 0 2px; }
.patient { margin: 2px 0 2px 12px; }
.signature { margin-top: 32px; }
.signature p { margin: 12px 0; }
</style>
</head>
<body>
<h1>{{ $title }}</h1>
<p class="sub">За «{{ $day }}» {{ $month }} {{ $year }} год.</p>
<p class="label">Плановые: {{ $planned['count'] }}</p>
@foreach ($planned['patients'] as $i => $p)
<p class="patient">{{ $i + 1 }}-{{ $p['full_name'] }} {{ $p['age'] }}.@if ($p['diagnosis']) Д/З: {{ $p['diagnosis'] }}.@endif</p>
@endforeach
<p class="label">Экстренные: {{ $urgent['count'] }}</p>
@foreach ($urgent['patients'] as $i => $p)
<p class="patient">{{ $i + 1 }}-{{ $p['full_name'] }} {{ $p['age'] }}.@if ($p['diagnosis']) Д/З: {{ $p['diagnosis'] }}.@endif</p>
@endforeach
<p>В отделении состоит: {{ $inDepartment }}</p>
<p class="label">ПЛАНОВЫХ ОПЕРАЦИЙ: {{ $plannedOperations['count'] }}</p>
@foreach ($plannedOperations['patients'] as $p)
<p class="patient">{{ $p['full_name'] }} {{ $p['age'] }}.</p>
@endforeach
<p class="label">Экстренных операций: {{ $urgentOperations['count'] }}</p>
@foreach ($urgentOperations['patients'] as $p)
<p class="patient">{{ $p['full_name'] }} {{ $p['age'] }}.</p>
@endforeach
<p class="label">Реанимация: {{ $reanimation['count'] }}</p>
@foreach ($reanimation['patients'] as $i => $p)
<p class="patient">{{ $i + 1 }}. {{ $p['full_name'] }} {{ $p['age'] }}.@if ($p['diagnosis']) {{ $p['diagnosis'] }}.@endif</p>
@endforeach
<p>Под наблюдением: {{ $observableCount }}</p>
<p>Летальные исходы: {{ $deceased['count'] > 0 ? '' : 'не было.' }}</p>
@foreach ($deceased['patients'] as $i => $p)
<p class="patient">{{ $i + 1 }}. {{ $p['full_name'] }} {{ $p['age'] }}.@if ($p['diagnosis']) {{ $p['diagnosis'] }}.@endif</p>
@endforeach
<div class="signature">
<p>Дежурный врач ПДО: _____________</p>
<p>Дежурный врач отделения: _____________</p>
</div>
</body>
</html>