Добавил ограничение по отказникам

This commit is contained in:
brusnitsyn
2026-06-17 17:38:48 +09:00
parent fe59410187
commit 839a534bb2
6 changed files with 105 additions and 3 deletions

View File

@@ -31,6 +31,7 @@ class SnapshotPatientSource
?array $recipientReportIds = null ?array $recipientReportIds = null
): Collection { ): Collection {
$snapshots = MedicalHistorySnapshot::query() $snapshots = MedicalHistorySnapshot::query()
->withoutDenials()
->whereIn('rf_report_id', $reportIds) ->whereIn('rf_report_id', $reportIds)
->where('patient_type', $type) ->where('patient_type', $type)
->get() ->get()
@@ -45,6 +46,7 @@ class SnapshotPatientSource
if ($markRecipients) { if ($markRecipients) {
$recipientReportIds ??= $reportIds; $recipientReportIds ??= $reportIds;
$recipientIds = MedicalHistorySnapshot::query() $recipientIds = MedicalHistorySnapshot::query()
->withoutDenials()
->whereIn('rf_report_id', $recipientReportIds) ->whereIn('rf_report_id', $recipientReportIds)
->where('patient_type', 'recipient') ->where('patient_type', 'recipient')
->get() ->get()
@@ -91,6 +93,7 @@ class SnapshotPatientSource
?array $recipientReportIds = null ?array $recipientReportIds = null
): Collection { ): Collection {
$snapshots = MedicalHistorySnapshot::query() $snapshots = MedicalHistorySnapshot::query()
->withoutDenials()
->whereIn('rf_report_id', $reportIds) ->whereIn('rf_report_id', $reportIds)
->where('patient_type', 'current') ->where('patient_type', 'current')
->get(); ->get();
@@ -107,6 +110,7 @@ class SnapshotPatientSource
$recipientReportIds ??= $reportIds; $recipientReportIds ??= $reportIds;
$recipientIds = MedicalHistorySnapshot::query() $recipientIds = MedicalHistorySnapshot::query()
->withoutDenials()
->whereIn('rf_report_id', $recipientReportIds) ->whereIn('rf_report_id', $recipientReportIds)
->where('patient_type', 'recipient') ->where('patient_type', 'recipient')
->get() ->get()
@@ -151,7 +155,7 @@ class SnapshotPatientSource
private function getCountFromSnapshots(string $type, array $reportIds): int private function getCountFromSnapshots(string $type, array $reportIds): int
{ {
$query = MedicalHistorySnapshot::whereIn('rf_report_id', $reportIds); $query = MedicalHistorySnapshot::withoutDenials()->whereIn('rf_report_id', $reportIds);
if ($type === 'outcome') { if ($type === 'outcome') {
$query->whereIn('patient_type', ['discharged', 'deceased']); $query->whereIn('patient_type', ['discharged', 'deceased']);

View File

@@ -53,6 +53,24 @@ class MedicalHistory extends MaterializedViewModel
->latest('observable_in'); ->latest('observable_in');
} }
public function denial()
{
return $this->hasOne(MisDenial::class, 'rf_MedicalHistoryID', 'id');
}
/**
* Исключить отказников от госпитализации.
* Безопасно: пока таблицы отказов нет в реплике no-op (см. MisDenial::tableAvailable()).
*/
public function scopeWithoutDenials($query)
{
if (MisDenial::tableAvailable()) {
$query->whereDoesntHave('denial');
}
return $query;
}
public function operationsInDepartment($query, $departmentId) public function operationsInDepartment($query, $departmentId)
{ {
return $this->operations()->where('department_id', $departmentId); return $this->operations()->where('department_id', $departmentId);

View File

@@ -72,4 +72,25 @@ class MedicalHistorySnapshot extends Model
$q->department($departmentId); $q->department($departmentId);
}); });
} }
/**
* Исключить из снимка карты, по которым в МИС оформлен отказ от госпитализации.
* Снимок остаётся неизменным (immutable) отсекаем только при чтении.
* Безопасно: пока таблицы отказов нет в реплике no-op (см. MisDenial::tableAvailable()).
*/
public function scopeWithoutDenials($query)
{
if (! MisDenial::tableAvailable()) {
return $query;
}
$denialTable = (new MisDenial)->getTable();
$snapshotTable = $this->getTable();
return $query->whereNotExists(function ($sub) use ($denialTable, $snapshotTable) {
$sub->selectRaw('1')
->from($denialTable)
->whereColumn("{$denialTable}.rf_MedicalHistoryID", "{$snapshotTable}.rf_medicalhistory_id");
});
}
} }

41
app/Models/MisDenial.php Normal file
View File

@@ -0,0 +1,41 @@
<?php
namespace App\Models;
use Illuminate\Support\Facades\Schema;
/**
* Отказ от госпитализации (МИС).
*
* ВНИМАНИЕ: ЗАГЛУШКА. На момент написания таблицы отказов ещё нет в реплике.
* Когда репликатор её добавит поменять $table на реальное имя.
* Пока таблицы нет, MisDenial::tableAvailable() === false и вся
* фильтрация отказников превращается в no-op (отчёты не ломаются).
*
* Структура в МИС:
* DenialID (PK), rf_MedicalHistoryID -> mv_medicalhistory_summary.id,
* rf_kl_HospRefusalID, DateTime, FAM/Name/OT, rf_StationarTypeID, ...
* Признак отказа = сам факт наличия строки с rf_MedicalHistoryID
* (по причинам/типам не фильтруем).
*/
class MisDenial extends MaterializedViewModel
{
protected $table = 'stt_denial'; // TODO: подставить реальное имя таблицы в реплике
protected $primaryKey = 'DenialID';
private static ?bool $tableExists = null;
public function medicalHistory()
{
return $this->belongsTo(MedicalHistory::class, 'rf_MedicalHistoryID', 'id');
}
/**
* Доступна ли таблица отказов в текущей БД (реплике).
* Мемоизируем на процесс, чтобы не дёргать information_schema на каждый запрос.
*/
public static function tableAvailable(): bool
{
return self::$tableExists ??= Schema::hasTable((new self)->getTable());
}
}

View File

@@ -35,6 +35,7 @@ class MedicalHistoryService
// 1. Один запрос: получаем "сырые" данные (без вычисляемых статусов) // 1. Один запрос: получаем "сырые" данные (без вычисляемых статусов)
$all = MedicalHistory::query() $all = MedicalHistory::query()
->withoutDenials()
->whereHas('migrations', $periodMigrationFilter) ->whereHas('migrations', $periodMigrationFilter)
->when($search, function ($query, $search) { ->when($search, function ($query, $search) {
// Поиск по ФИО (точное совпадение или LIKE) // Поиск по ФИО (точное совпадение или LIKE)
@@ -143,7 +144,8 @@ class MedicalHistoryService
{ {
$query = MedicalHistory::query(); $query = MedicalHistory::query();
$query->where('recipient_date', '>=', $dateRange->startSql()) $query->withoutDenials()
->where('recipient_date', '>=', $dateRange->startSql())
->where('recipient_date', '<', $dateRange->endSql()) ->where('recipient_date', '<', $dateRange->endSql())
// 1. Оставляем только тех пациентов, у которых БЫЛО движение в этом отделении // 1. Оставляем только тех пациентов, у которых БЫЛО движение в этом отделении
->whereHas('latestMigration', fn($q) => $q->where('department_id', $departmentId)) ->whereHas('latestMigration', fn($q) => $q->where('department_id', $departmentId))
@@ -160,7 +162,8 @@ class MedicalHistoryService
{ {
$query = MedicalHistory::query(); $query = MedicalHistory::query();
$query->where('recipient_date', '>=', $dateRange->startSql()) $query->withoutDenials()
->where('recipient_date', '>=', $dateRange->startSql())
->where('recipient_date', '<', $dateRange->endSql()) ->where('recipient_date', '<', $dateRange->endSql())
->urgency($urgencyId) ->urgency($urgencyId)
->whereHas('migrations', function ($m) use ($departmentId) { ->whereHas('migrations', function ($m) use ($departmentId) {
@@ -179,6 +182,7 @@ class MedicalHistoryService
public function getDepartmentHistories(DateRange $dateRange, int $departmentId) public function getDepartmentHistories(DateRange $dateRange, int $departmentId)
{ {
return MedicalHistory::query() return MedicalHistory::query()
->withoutDenials()
->whereHas('migrations', function ($q) use ($departmentId, $dateRange) { ->whereHas('migrations', function ($q) use ($departmentId, $dateRange) {
$q->department($departmentId)->currentOrAdmitted($dateRange); $q->department($departmentId)->currentOrAdmitted($dateRange);
}) })
@@ -194,6 +198,7 @@ class MedicalHistoryService
public function getPlannedHistories(DateRange $dateRange, int $departmentId) public function getPlannedHistories(DateRange $dateRange, int $departmentId)
{ {
return MedicalHistory::query() return MedicalHistory::query()
->withoutDenials()
->whereHas('migrations', function ($q) use ($departmentId, $dateRange) { ->whereHas('migrations', function ($q) use ($departmentId, $dateRange) {
$q->department($departmentId)->currentOrAdmitted($dateRange); $q->department($departmentId)->currentOrAdmitted($dateRange);
}) })
@@ -211,6 +216,7 @@ class MedicalHistoryService
public function getEmergencyHistories(DateRange $dateRange, int $departmentId) public function getEmergencyHistories(DateRange $dateRange, int $departmentId)
{ {
return MedicalHistory::query() return MedicalHistory::query()
->withoutDenials()
->whereHas('migrations', function ($q) use ($departmentId, $dateRange) { ->whereHas('migrations', function ($q) use ($departmentId, $dateRange) {
$q->department($departmentId)->currentOrAdmitted($dateRange); $q->department($departmentId)->currentOrAdmitted($dateRange);
}) })
@@ -234,6 +240,7 @@ class MedicalHistoryService
$now = Carbon::now(); $now = Carbon::now();
return MedicalHistory::query() return MedicalHistory::query()
->withoutDenials()
->whereHas('migrations', function ($q) use ($departmentId, $dateRange) { ->whereHas('migrations', function ($q) use ($departmentId, $dateRange) {
$q->department($departmentId)->admitted($dateRange->startSql(), $dateRange->endSql()); $q->department($departmentId)->admitted($dateRange->startSql(), $dateRange->endSql());
}) })
@@ -246,6 +253,7 @@ class MedicalHistoryService
public function getDischargedHistories(DateRange $dateRange, int $departmentId) public function getDischargedHistories(DateRange $dateRange, int $departmentId)
{ {
return MedicalHistory::query() return MedicalHistory::query()
->withoutDenials()
->whereHas('migrations', function ($q) use ($departmentId, $dateRange) { ->whereHas('migrations', function ($q) use ($departmentId, $dateRange) {
$q->department($departmentId)->discharged($dateRange->startSql(), $dateRange->endSql()); $q->department($departmentId)->discharged($dateRange->startSql(), $dateRange->endSql());
}) })
@@ -258,6 +266,7 @@ class MedicalHistoryService
public function getDeceasedHistories(DateRange $dateRange, int $departmentId) public function getDeceasedHistories(DateRange $dateRange, int $departmentId)
{ {
return MedicalHistory::query() return MedicalHistory::query()
->withoutDenials()
->whereHas('migrations', function ($q) use ($departmentId, $dateRange) { ->whereHas('migrations', function ($q) use ($departmentId, $dateRange) {
$q->department($departmentId)->deceased($dateRange->startSql(), $dateRange->endSql()); $q->department($departmentId)->deceased($dateRange->startSql(), $dateRange->endSql());
}) })
@@ -270,6 +279,7 @@ class MedicalHistoryService
public function getTransferredHistories(DateRange $dateRange, int $departmentId) public function getTransferredHistories(DateRange $dateRange, int $departmentId)
{ {
return MedicalHistory::query() return MedicalHistory::query()
->withoutDenials()
->whereHas('migrations', function ($q) use ($departmentId, $dateRange) { ->whereHas('migrations', function ($q) use ($departmentId, $dateRange) {
$q->department($departmentId)->transferred($dateRange->startSql(), $dateRange->endSql()); $q->department($departmentId)->transferred($dateRange->startSql(), $dateRange->endSql());
}) })
@@ -282,6 +292,7 @@ class MedicalHistoryService
public function getReanimationHistories(DateRange $dateRange, int $departmentId) public function getReanimationHistories(DateRange $dateRange, int $departmentId)
{ {
return MedicalHistory::query() return MedicalHistory::query()
->withoutDenials()
->whereHas('migrations', function ($q) use ($departmentId, $dateRange) { ->whereHas('migrations', function ($q) use ($departmentId, $dateRange) {
$q->department($departmentId)->currentOrAdmitted($dateRange); $q->department($departmentId)->currentOrAdmitted($dateRange);
}) })

View File

@@ -32,6 +32,12 @@ class PlanCalculator extends BaseMetricService implements MetricCalculatorInterf
$startCurrentMonth = $endDate->copy()->startOfMonth()->setHours(9); $startCurrentMonth = $endDate->copy()->startOfMonth()->setHours(9);
$currentMonth = $endDate->month; $currentMonth = $endDate->month;
// Кол-во календарных месяцев, затронутых выбранным диапазоном (для нормы за период)
$monthsInRange = max(
1,
($endDate->year * 12 + $endDate->month) - ($startDate->year * 12 + $startDate->month) + 1
);
// Получаем фактические выписки с начала года по прошлый месяц // Получаем фактические выписки с начала года по прошлый месяц
$previousOutcomeMonth = DB::table('report_duties as r') $previousOutcomeMonth = DB::table('report_duties as r')
->join('duty_report_metric_results as mr', 'r.id', '=', 'mr.rf_report_id') ->join('duty_report_metric_results as mr', 'r.id', '=', 'mr.rf_report_id')
@@ -99,6 +105,7 @@ class PlanCalculator extends BaseMetricService implements MetricCalculatorInterf
$results[$departmentId] = [ $results[$departmentId] = [
'year_plan' => $annualPlan, 'year_plan' => $annualPlan,
'month_plan' => $oneMonthPlan, 'month_plan' => $oneMonthPlan,
'period_plan' => $oneMonthPlan * $monthsInRange, // норма за выбранный период (мес. × кол-во месяцев)
'total_debt' => $totalDebt, 'total_debt' => $totalDebt,
'current_mouth_dept' => $currentMonthPlanOnly, 'current_mouth_dept' => $currentMonthPlanOnly,
'cumulative_plan' => $cumulativePlan, 'cumulative_plan' => $cumulativePlan,