* добавил исход спец контингенту
* оптимизация обновления при редактировании спец контингента * добавил поддержку заключительных диагнозов * изменил определение законченной операции * добавил поддержку исхода операции * добавил определение отмены для операции через назначение * работа над диапазонами календарей, подсчет статистики * добавил статусы отчетов и подкорректировал привязку спец контингента к отчету * добавил новые сервисы для будущего кеширования * частичное разделение логики подсчета пациентов
This commit is contained in:
@@ -7,6 +7,7 @@ use App\Models\Department;
|
||||
use App\Models\DepartmentPatient;
|
||||
use App\Models\MisMedicalHistory;
|
||||
use App\Models\ObservationPatient;
|
||||
use App\Models\Report;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
@@ -26,7 +27,8 @@ class UnifiedPatientService
|
||||
int $branchId,
|
||||
bool $onlyIds = false,
|
||||
?bool $includeCurrent = null,
|
||||
bool $fillableAuto = false
|
||||
bool $fillableAuto = false,
|
||||
bool $forSnapshots = false
|
||||
): Collection {
|
||||
[$baseStatus, $sourceScope] = $this->parseScopedStatus($status);
|
||||
|
||||
@@ -35,9 +37,9 @@ class UnifiedPatientService
|
||||
}
|
||||
|
||||
$patients = match ($sourceScope) {
|
||||
'mis' => $this->getMisPatientDtos($user, $baseStatus, $dateRange, $branchId, $includeCurrent, $fillableAuto),
|
||||
'special' => $this->getSpecialPatientDtos($department, $baseStatus, $dateRange),
|
||||
default => $this->getAggregatedPatientDtos($department, $user, $baseStatus, $dateRange, $branchId, $includeCurrent, $fillableAuto),
|
||||
'mis' => $this->getMisPatientDtos($user, $baseStatus, $dateRange, $branchId, $includeCurrent, $fillableAuto, $forSnapshots),
|
||||
'special' => $this->getSpecialPatientDtos($department, $baseStatus, $dateRange, $forSnapshots),
|
||||
default => $this->getAggregatedPatientDtos($department, $user, $baseStatus, $dateRange, $branchId, $includeCurrent, $fillableAuto, $forSnapshots),
|
||||
};
|
||||
|
||||
if ($onlyIds) {
|
||||
@@ -56,13 +58,79 @@ class UnifiedPatientService
|
||||
?bool $includeCurrent = null,
|
||||
bool $fillableAuto = false
|
||||
): int {
|
||||
return $this->getLivePatientsByStatus($department, $user, $status, $dateRange, $branchId, false, $includeCurrent, $fillableAuto)->count();
|
||||
[$baseStatus, $sourceScope] = $this->parseScopedStatus($status);
|
||||
|
||||
if ($baseStatus === 'observation') {
|
||||
$query = ObservationPatient::query()
|
||||
->where('rf_department_id', $department->department_id);
|
||||
|
||||
if ($sourceScope === 'special') {
|
||||
return $query->whereNotNull('rf_department_patient_id')->count();
|
||||
}
|
||||
|
||||
if ($sourceScope === 'mis') {
|
||||
return $query->whereNull('rf_department_patient_id')->count();
|
||||
}
|
||||
|
||||
return $query->count();
|
||||
}
|
||||
|
||||
if ($sourceScope === 'special') {
|
||||
return $this->getManualPatientsCount($department, $baseStatus, $dateRange, self::SPECIAL_SOURCE_TYPES);
|
||||
}
|
||||
|
||||
$misCount = $this->getMisPatientsCount(
|
||||
$user,
|
||||
$baseStatus,
|
||||
$dateRange,
|
||||
$branchId,
|
||||
$includeCurrent,
|
||||
$fillableAuto
|
||||
);
|
||||
|
||||
if ($sourceScope === 'mis') {
|
||||
return $misCount;
|
||||
}
|
||||
|
||||
$specialCount = $this->getManualPatientsCount($department, $baseStatus, $dateRange, self::SPECIAL_SOURCE_TYPES);
|
||||
return $misCount + $specialCount;
|
||||
}
|
||||
|
||||
public function createManualPatient(Department $department, User $user, array $data): DepartmentPatient
|
||||
public function getRecipientIdsForReport(
|
||||
Department $department,
|
||||
User $user,
|
||||
DateRange $dateRange,
|
||||
int $branchId,
|
||||
bool $fillableAuto = false
|
||||
): array {
|
||||
$isHeadOrAdmin = $user->isHeadOfDepartment() || $user->isAdmin();
|
||||
|
||||
$misIds = $this->patientService->getPlanOrEmergencyPatients(
|
||||
null,
|
||||
$isHeadOrAdmin,
|
||||
$branchId,
|
||||
$dateRange,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
$fillableAuto
|
||||
);
|
||||
|
||||
$manualIds = $this->buildManualPatientsQuery($department, $dateRange, self::SPECIAL_SOURCE_TYPES, false)
|
||||
->whereBetween('admitted_at', [$dateRange->startSql(), $dateRange->endSql()])
|
||||
->pluck('department_patient_id');
|
||||
|
||||
$misUids = collect($misIds)->map(fn ($id) => "mis:{$id}")->all();
|
||||
$manualUids = collect($manualIds)->map(fn ($id) => "manual:{$id}")->all();
|
||||
|
||||
return array_values(array_unique(array_merge($misUids, $manualUids)));
|
||||
}
|
||||
|
||||
public function createManualPatient(Department $department, User $user, array $data, int $reportId): DepartmentPatient
|
||||
{
|
||||
return DepartmentPatient::create([
|
||||
'rf_department_id' => $department->department_id,
|
||||
'rf_report_id' => $reportId,
|
||||
'source_type' => 'special',
|
||||
'full_name' => $data['full_name'],
|
||||
'birth_date' => $data['birth_date'],
|
||||
@@ -88,6 +156,13 @@ class UnifiedPatientService
|
||||
|
||||
public function updateManualPatient(DepartmentPatient $patient, array $data): DepartmentPatient
|
||||
{
|
||||
$manualStatus = $data['manual_status'] ?? null;
|
||||
$isCurrent = $manualStatus === 'current' || $manualStatus === null;
|
||||
$outcomeType = match ($manualStatus) {
|
||||
'discharged', 'deceased', 'transferred' => $manualStatus,
|
||||
default => null,
|
||||
};
|
||||
|
||||
$patient->update([
|
||||
'full_name' => $data['full_name'],
|
||||
'birth_date' => $data['birth_date'],
|
||||
@@ -95,6 +170,9 @@ class UnifiedPatientService
|
||||
'diagnosis_code' => $data['diagnosis_code'] ?? null,
|
||||
'diagnosis_name' => $data['diagnosis_name'] ?? null,
|
||||
'admitted_at' => $data['admitted_at'] ?? $patient->admitted_at,
|
||||
'is_current' => $isCurrent,
|
||||
'outcome_type' => $outcomeType,
|
||||
'outcome_at' => $isCurrent ? null : ($data['outcome_at'] ?? now()),
|
||||
]);
|
||||
|
||||
return $patient->fresh();
|
||||
@@ -194,11 +272,24 @@ class UnifiedPatientService
|
||||
DateRange $dateRange,
|
||||
int $branchId,
|
||||
?bool $includeCurrent = null,
|
||||
bool $fillableAuto = false
|
||||
bool $fillableAuto = false,
|
||||
bool $forSnapshots = false
|
||||
): Collection {
|
||||
$misPatients = $this->getMisPatients($user, $status, $dateRange, $branchId, $includeCurrent, $fillableAuto);
|
||||
$manualPatients = $this->getManualPatients($department, $status, $dateRange);
|
||||
$linkedManualPatients = DepartmentPatient::where('rf_department_id', $department->department_id)
|
||||
$misPatients = $this->getMisPatients($user, $status, $dateRange, $branchId, $includeCurrent, $fillableAuto, $forSnapshots);
|
||||
$manualPatients = $this->getManualPatients($department, $status, $dateRange, self::SPECIAL_SOURCE_TYPES, !$forSnapshots);
|
||||
$reportIds = $this->getReportIdsForDepartmentPeriod($department, $dateRange);
|
||||
|
||||
$linkedManualPatients = DepartmentPatient::query()
|
||||
->where(function ($builder) use ($department, $reportIds) {
|
||||
if (!empty($reportIds)) {
|
||||
$builder->whereIn('rf_report_id', $reportIds);
|
||||
}
|
||||
|
||||
$builder->orWhere(function ($legacyQuery) use ($department) {
|
||||
$legacyQuery->whereNull('rf_report_id')
|
||||
->where('rf_department_id', $department->department_id);
|
||||
});
|
||||
})
|
||||
->whereIn('source_type', self::SPECIAL_SOURCE_TYPES)
|
||||
->whereNotNull('rf_medicalhistory_id')
|
||||
->get()
|
||||
@@ -228,9 +319,10 @@ class UnifiedPatientService
|
||||
DateRange $dateRange,
|
||||
int $branchId,
|
||||
?bool $includeCurrent = null,
|
||||
bool $fillableAuto = false
|
||||
bool $fillableAuto = false,
|
||||
bool $forSnapshots = false
|
||||
): Collection {
|
||||
return $this->getMisPatients($user, $status, $dateRange, $branchId, $includeCurrent, $fillableAuto)
|
||||
return $this->getMisPatients($user, $status, $dateRange, $branchId, $includeCurrent, $fillableAuto, $forSnapshots)
|
||||
->map(fn ($patient) => UnifiedPatientData::fromMisMedicalHistory(
|
||||
$patient,
|
||||
(bool) ($patient->is_recipient_today ?? false),
|
||||
@@ -242,10 +334,11 @@ class UnifiedPatientService
|
||||
private function getSpecialPatientDtos(
|
||||
Department $department,
|
||||
string $status,
|
||||
DateRange $dateRange
|
||||
DateRange $dateRange,
|
||||
bool $forSnapshots = false
|
||||
): Collection {
|
||||
return $this->mapManualPatients(
|
||||
$this->getManualPatients($department, $status, $dateRange, self::SPECIAL_SOURCE_TYPES),
|
||||
$this->getManualPatients($department, $status, $dateRange, self::SPECIAL_SOURCE_TYPES, !$forSnapshots),
|
||||
$dateRange
|
||||
);
|
||||
}
|
||||
@@ -256,7 +349,8 @@ class UnifiedPatientService
|
||||
DateRange $dateRange,
|
||||
int $branchId,
|
||||
?bool $includeCurrent = null,
|
||||
bool $fillableAuto = false
|
||||
bool $fillableAuto = false,
|
||||
bool $forSnapshots = false
|
||||
): Collection {
|
||||
$isHeadOrAdmin = $user->isHeadOfDepartment() || $user->isAdmin();
|
||||
$includeCurrent = $includeCurrent ?? in_array($status, ['plan', 'emergency'], true);
|
||||
@@ -299,19 +393,66 @@ class UnifiedPatientService
|
||||
};
|
||||
}
|
||||
|
||||
private function getMisPatientsCount(
|
||||
User $user,
|
||||
string $status,
|
||||
DateRange $dateRange,
|
||||
int $branchId,
|
||||
?bool $includeCurrent = null,
|
||||
bool $fillableAuto = false
|
||||
): int {
|
||||
$isHeadOrAdmin = $user->isHeadOfDepartment() || $user->isAdmin();
|
||||
$includeCurrent = $includeCurrent ?? in_array($status, ['plan', 'emergency'], true);
|
||||
|
||||
return match ($status) {
|
||||
'plan', 'emergency' => $includeCurrent
|
||||
? $this->patientService->getPatientsCountWithCurrent($status, $isHeadOrAdmin, $branchId, $dateRange)
|
||||
: $this->patientService->getPlanOrEmergencyPatients(
|
||||
$status,
|
||||
$isHeadOrAdmin,
|
||||
$branchId,
|
||||
$dateRange,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
$fillableAuto
|
||||
),
|
||||
'current' => $this->patientService->getAllPatientsInDepartment(
|
||||
$isHeadOrAdmin,
|
||||
$branchId,
|
||||
$dateRange,
|
||||
true,
|
||||
false,
|
||||
$fillableAuto
|
||||
),
|
||||
'recipient' => $this->patientService->getPlanOrEmergencyPatients(
|
||||
null,
|
||||
$isHeadOrAdmin,
|
||||
$branchId,
|
||||
$dateRange,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
$fillableAuto
|
||||
),
|
||||
'outcome' => $this->patientService->getOutcomePatients($branchId, $dateRange, 'without-transferred', true)->count(),
|
||||
'outcome-discharged' => $this->patientService->getOutcomePatients($branchId, $dateRange, 'discharged', true)->count(),
|
||||
'outcome-transferred' => $this->patientService->getOutcomePatients($branchId, $dateRange, 'transferred', true)->count(),
|
||||
'outcome-deceased' => $this->patientService->getOutcomePatients($branchId, $dateRange, 'deceased', true)->count(),
|
||||
'reanimation' => $this->patientService->getReanimationPatients($branchId, $dateRange, true)->count(),
|
||||
default => 0,
|
||||
};
|
||||
}
|
||||
|
||||
private function getManualPatients(
|
||||
Department $department,
|
||||
string $status,
|
||||
DateRange $dateRange,
|
||||
?array $sourceTypes = self::SPECIAL_SOURCE_TYPES
|
||||
?array $sourceTypes = self::SPECIAL_SOURCE_TYPES,
|
||||
bool $withOperations = true
|
||||
): Collection
|
||||
{
|
||||
$query = DepartmentPatient::where('rf_department_id', $department->department_id)
|
||||
->with(['operations.serviceMedical']);
|
||||
|
||||
if ($sourceTypes !== null) {
|
||||
$query->whereIn('source_type', $sourceTypes);
|
||||
}
|
||||
$query = $this->buildManualPatientsQuery($department, $dateRange, $sourceTypes, $withOperations);
|
||||
|
||||
return match ($status) {
|
||||
'plan', 'emergency' => $query
|
||||
@@ -338,11 +479,91 @@ class UnifiedPatientService
|
||||
};
|
||||
}
|
||||
|
||||
private function getManualPatientsCount(
|
||||
Department $department,
|
||||
string $status,
|
||||
DateRange $dateRange,
|
||||
?array $sourceTypes = self::SPECIAL_SOURCE_TYPES
|
||||
): int {
|
||||
$query = $this->buildManualPatientsQuery($department, $dateRange, $sourceTypes, false);
|
||||
|
||||
return match ($status) {
|
||||
'plan', 'emergency' => $query
|
||||
->current()
|
||||
->where('patient_kind', $status)
|
||||
->count(),
|
||||
'current' => $query
|
||||
->current()
|
||||
->count(),
|
||||
'recipient' => $query
|
||||
->whereBetween('admitted_at', [$dateRange->startSql(), $dateRange->endSql()])
|
||||
->count(),
|
||||
'outcome' => $query
|
||||
->whereNotNull('outcome_type')
|
||||
->whereIn('outcome_type', ['discharged', 'deceased'])
|
||||
->whereBetween('outcome_at', [$dateRange->startSql(), $dateRange->endSql()])
|
||||
->count(),
|
||||
'outcome-discharged', 'outcome-transferred', 'outcome-deceased' => $query
|
||||
->where('outcome_type', str_replace('outcome-', '', $status))
|
||||
->whereBetween('outcome_at', [$dateRange->startSql(), $dateRange->endSql()])
|
||||
->count(),
|
||||
default => 0,
|
||||
};
|
||||
}
|
||||
|
||||
private function buildManualPatientsQuery(
|
||||
Department $department,
|
||||
DateRange $dateRange,
|
||||
?array $sourceTypes,
|
||||
bool $withOperations
|
||||
) {
|
||||
$reportIds = $this->getReportIdsForDepartmentPeriod($department, $dateRange);
|
||||
|
||||
$query = DepartmentPatient::query()
|
||||
->where(function ($builder) use ($department, $reportIds) {
|
||||
if (!empty($reportIds)) {
|
||||
$builder->whereIn('rf_report_id', $reportIds);
|
||||
}
|
||||
|
||||
$builder->orWhere(function ($legacyQuery) use ($department) {
|
||||
$legacyQuery->whereNull('rf_report_id')
|
||||
->where('rf_department_id', $department->department_id);
|
||||
});
|
||||
});
|
||||
|
||||
if ($withOperations) {
|
||||
$query->with(['operations.serviceMedical']);
|
||||
}
|
||||
|
||||
if ($sourceTypes !== null) {
|
||||
$query->whereIn('source_type', $sourceTypes);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
private function getReportIdsForDepartmentPeriod(Department $department, DateRange $dateRange): array
|
||||
{
|
||||
return Report::query()
|
||||
->where('rf_department_id', $department->department_id)
|
||||
->when(
|
||||
$dateRange->isOneDay,
|
||||
fn ($query) => $query->exactPeriod($dateRange->startSql(), $dateRange->endSql()),
|
||||
fn ($query) => $query->withinPeriod($dateRange->startSql(), $dateRange->endSql()),
|
||||
)
|
||||
->pluck('report_id')
|
||||
->all();
|
||||
}
|
||||
|
||||
private function mapManualPatients(Collection $manualPatients, DateRange $dateRange): Collection
|
||||
{
|
||||
return $manualPatients
|
||||
->map(function (DepartmentPatient $patient) use ($dateRange) {
|
||||
$operations = $patient->operations->map(fn ($operation) => [
|
||||
$operationsRelation = $patient->relationLoaded('operations')
|
||||
? $patient->operations
|
||||
: collect();
|
||||
|
||||
$operations = $operationsRelation->map(fn ($operation) => [
|
||||
'id' => $operation->department_patient_operation_id,
|
||||
'code' => $operation->serviceMedical?->ServiceMedicalCode ?? $operation->service_code,
|
||||
'name' => $operation->serviceMedical?->ServiceMedicalName ?? $operation->service_name,
|
||||
|
||||
Reference in New Issue
Block a user