Модуль отчетов

This commit is contained in:
brusnitsyn
2026-06-21 23:40:55 +09:00
parent f163b95663
commit bd2cc24b98
27 changed files with 2781 additions and 3 deletions

View File

@@ -0,0 +1,49 @@
<?php
namespace App\Services\Reports\BuiltIn;
use App\Models\Department;
use App\Services\DateRange;
use App\Services\Reports\Contracts\ReportDefinition;
use App\Services\Reports\ReportPayload;
use App\Services\Reports\ReportSourceRegistry;
class DutyDoctorReport implements ReportDefinition
{
public function __construct(private readonly ReportSourceRegistry $sources) {}
public function code(): string
{
return 'duty';
}
public function label(): string
{
return 'Отчёт дежурного врача';
}
public function requiredPermissions(): array
{
return ['report.view'];
}
public function build(Department $department, DateRange $dateRange): ReportPayload
{
$sections = [
$this->sources->get('duty_metrics')->toSection($department, $dateRange),
$this->sources->get('duty_patients')->toSection($department, $dateRange),
$this->sources->get('unwanted_events')->toSection($department, $dateRange),
$this->sources->get('observable_patients')->toSection($department, $dateRange),
];
return new ReportPayload(
title: $this->label(),
meta: [
'Отделение' => $department->name_full ?? $department->name_short,
'Период' => $dateRange->start()->format('d.m.Y H:i').' — '.$dateRange->end()->format('d.m.Y H:i'),
'Сформирован' => now('Asia/Yakutsk')->format('d.m.Y H:i'),
],
sections: $sections,
);
}
}

View File

@@ -0,0 +1,106 @@
<?php
namespace App\Services\Reports\BuiltIn;
use App\Domain\Reports\ValueObjects\MetrikaConfig;
use App\Models\Department;
use App\Models\DepartmentMetrikaDefault;
use App\Models\ReportNursePatient;
use App\Services\Classification\PatientStatusClassifier;
use App\Services\DateRange;
use App\Services\Reports\Contracts\ReportDefinition;
use App\Services\Reports\ReportPayload;
use App\Services\Reports\ReportSection;
use App\Services\Reports\ReportSourceRegistry;
class HeadNurseReport implements ReportDefinition
{
public function __construct(private readonly ReportSourceRegistry $sources) {}
public function code(): string
{
return 'nurse';
}
public function label(): string
{
return 'Отчёт старшей медсестры';
}
public function requiredPermissions(): array
{
return ['nurse.report.view'];
}
public function build(Department $department, DateRange $dateRange): ReportPayload
{
$sections = [
$this->buildMetricsSection($department, $dateRange),
$this->sources->get('nurse_patients')->toSection($department, $dateRange, null, [], 'Журнал пациентов'),
];
return new ReportPayload(
title: $this->label(),
meta: [
'Отделение' => $department->name_full ?? $department->name_short,
'Период' => $dateRange->start()->format('d.m.Y H:i').' — '.$dateRange->end()->format('d.m.Y H:i'),
'Сформирован' => now('Asia/Yakutsk')->format('d.m.Y H:i'),
],
sections: $sections,
);
}
private function buildMetricsSection(Department $department, DateRange $dateRange): ReportSection
{
$reportIds = $this->sources->nurseReports($department, $dateRange)->pluck('id');
$counts = [
'recipient' => 0,
'discharged' => 0,
'transferred' => 0,
'deceased' => 0,
'in_department' => 0,
];
if ($reportIds->isNotEmpty()) {
$patients = ReportNursePatient::whereIn('report_nurse_id', $reportIds)->with('migrations')->get();
foreach ($patients as $patient) {
match (PatientStatusClassifier::classify($patient, $dateRange)) {
PatientStatusClassifier::STATUS_RECIPIENT => $counts['recipient']++,
PatientStatusClassifier::STATUS_DISCHARGED => $counts['discharged']++,
PatientStatusClassifier::STATUS_TRANSFERRED => $counts['transferred']++,
PatientStatusClassifier::STATUS_DECEASED => $counts['deceased']++,
PatientStatusClassifier::STATUS_IN_DEPARTMENT => $counts['in_department']++,
default => null,
};
}
}
$beds = (int) (DepartmentMetrikaDefault::where('rf_department_id', $department->department_id)
->where('rf_metrika_item_id', MetrikaConfig::BEDS)
->value('value') ?? 0);
$occupancy = $beds > 0 ? round($counts['in_department'] * 100 / $beds, 1) : 0;
$row = [
'beds' => $beds,
'recipient' => $counts['recipient'],
'discharged' => $counts['discharged'],
'transferred' => $counts['transferred'],
'deceased' => $counts['deceased'],
'in_department' => $counts['in_department'],
'occupancy_percent' => $occupancy,
];
return new ReportSection('Показатели', [
'beds' => 'Коек',
'recipient' => 'Поступило',
'discharged' => 'Выписано',
'transferred' => 'Переведено',
'deceased' => 'Умерло',
'in_department' => 'В отделении',
'occupancy_percent' => 'Занятость, %',
], [$row]);
}
}