Обновлен стартовый экран
Переписаны запросы для статистики, отчетов Добавлена интеграция отчета сестры
This commit is contained in:
28
app/Models/DutyReportMetricResult.php
Normal file
28
app/Models/DutyReportMetricResult.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class DutyReportMetricResult extends Model
|
||||
{
|
||||
protected $primaryKey = 'metrika_result_id';
|
||||
|
||||
public $timestamps = false;
|
||||
|
||||
protected $fillable = [
|
||||
'rf_metrika_item_id',
|
||||
'rf_report_id',
|
||||
'value',
|
||||
];
|
||||
|
||||
public function report(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
{
|
||||
return $this->belongsTo(ReportDuty::class, 'rf_report_id', 'id');
|
||||
}
|
||||
|
||||
public function metrikaItem()
|
||||
{
|
||||
return $this->belongsTo(MetrikaItem::class, 'rf_metrika_item_id', 'metrika_item_id');
|
||||
}
|
||||
}
|
||||
19
app/Models/DutyUnwantedEvent.php
Normal file
19
app/Models/DutyUnwantedEvent.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class DutyUnwantedEvent extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'report_duty_id',
|
||||
'title',
|
||||
'comment',
|
||||
];
|
||||
|
||||
public function reportDuty()
|
||||
{
|
||||
return $this->belongsTo(ReportDuty::class, 'report_duty_id');
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ class MedicalHistory extends MaterializedViewModel
|
||||
public function latestMigration()
|
||||
{
|
||||
return $this->hasOne(MigrationPatient::class, 'medical_history_id', 'id')
|
||||
->whereNotNull('out_date')
|
||||
->latest('ingoing_date');
|
||||
}
|
||||
|
||||
@@ -41,6 +42,17 @@ class MedicalHistory extends MaterializedViewModel
|
||||
return $this->hasMany(Reanimation::class, 'medical_history_id', 'id');
|
||||
}
|
||||
|
||||
public function observables()
|
||||
{
|
||||
return $this->hasMany(ObservableMedicalHistory::class, 'original_id', 'id');
|
||||
}
|
||||
|
||||
public function observable()
|
||||
{
|
||||
return $this->hasOne(ObservableMedicalHistory::class, 'original_id', 'id')
|
||||
->latest('observable_in');
|
||||
}
|
||||
|
||||
public function operationsInDepartment($query, $departmentId)
|
||||
{
|
||||
return $this->operations()->where('department_id', $departmentId);
|
||||
|
||||
@@ -116,11 +116,10 @@ class MigrationPatient extends MaterializedViewModel
|
||||
{
|
||||
return $query->where('medical_history_id', $historyId)
|
||||
->department($departmentId)
|
||||
->orderBy('ingoing_date', 'desc')
|
||||
->limit(1)->first();
|
||||
->orderBy('ingoing_date', 'desc');
|
||||
}
|
||||
|
||||
public function getAdmittedInCurrentAttribute(): bool
|
||||
public function getAdmittedInCurrentAttribute(DateRange $dateRange): bool
|
||||
{
|
||||
// Получаем дату поступления из последнего движения
|
||||
$ingoing = $this->ingoing_date;
|
||||
@@ -130,7 +129,7 @@ class MigrationPatient extends MaterializedViewModel
|
||||
}
|
||||
|
||||
$ingoingLocal = Carbon::parse($ingoing)->setTimezone(config('app.timezone', 'Europe/Moscow'));
|
||||
$now = Carbon::now(config('app.timezone', 'Europe/Moscow'));
|
||||
$now = $dateRange->endDate;//Carbon::now(config('app.timezone', 'Europe/Moscow'));
|
||||
|
||||
// Окно смены: вчера 09:00 → сегодня 09:00
|
||||
$shiftStart = $now->copy()->subDay()->setTime(9, 0);
|
||||
|
||||
@@ -43,7 +43,6 @@ class MigrationPatientNurse extends Model
|
||||
{
|
||||
return $query->where('medical_history_id', $historyId)
|
||||
->department($departmentId)
|
||||
->orderBy('ingoing_date', 'desc')
|
||||
->limit(1)->first();
|
||||
->orderBy('ingoing_date', 'desc');
|
||||
}
|
||||
}
|
||||
|
||||
36
app/Models/ObservableMedicalHistory.php
Normal file
36
app/Models/ObservableMedicalHistory.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ObservableMedicalHistory extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'source_type',
|
||||
'original_id',
|
||||
|
||||
'observable_in',
|
||||
'observable_out',
|
||||
'observable_reason',
|
||||
'out_reason',
|
||||
|
||||
'medical_card_number',
|
||||
'full_name',
|
||||
'birth_date',
|
||||
'recipient_date',
|
||||
'extract_date',
|
||||
'death_date',
|
||||
'male',
|
||||
'urgency_id',
|
||||
'hospital_result_id',
|
||||
'visit_result_id',
|
||||
'comment',
|
||||
'user_id',
|
||||
];
|
||||
|
||||
public function medicalHistory(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
{
|
||||
return $this->belongsTo(ReportDutyPatient::class, 'original_id', 'original_id');
|
||||
}
|
||||
}
|
||||
@@ -39,4 +39,22 @@ class ReportDuty extends Model
|
||||
{
|
||||
return $this->belongsTo(ReportStatus::class);
|
||||
}
|
||||
|
||||
public function doctor()
|
||||
{
|
||||
return $this->belongsTo(MisLpuDoctor::class, 'rf_lpudoctor_id', 'LPUDoctorID');
|
||||
}
|
||||
|
||||
public function unwantedEvents()
|
||||
{
|
||||
return $this->hasMany(DutyUnwantedEvent::class, 'report_duty_id', 'id');
|
||||
}
|
||||
|
||||
public function getLoadedDepartmentAttribute(int $patientsInDepartment)
|
||||
{
|
||||
$beds = DutyReportMetricResult::where('rf_report_id', $this->id)
|
||||
->where('rf_metrika_item_id', 1)->pluck('value')->first() ?? 1;
|
||||
|
||||
return round((($patientsInDepartment ?? 0) * 100) / $beds);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,11 +4,13 @@ namespace App\Models;
|
||||
|
||||
use App\Services\DateRange;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class ReportDutyMigrationPatient extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'original_id',
|
||||
'medical_history_id',
|
||||
'ingoing_date',
|
||||
'out_date',
|
||||
@@ -32,12 +34,18 @@ class ReportDutyMigrationPatient extends Model
|
||||
|
||||
public function medicalHistory()
|
||||
{
|
||||
return $this->belongsTo(ReportNursePatient::class, 'medical_history_id', 'id');
|
||||
return $this->belongsTo(ReportDutyPatient::class, 'medical_history_id', 'id');
|
||||
}
|
||||
|
||||
public function operations()
|
||||
{
|
||||
return $this->hasMany(SurgicalOperation::class, 'migration_patient_id', 'id');
|
||||
return $this->hasMany(SurgicalOperation::class, 'migration_patient_id', 'original_id');
|
||||
}
|
||||
|
||||
public function reanimations()
|
||||
{
|
||||
return $this->hasMany(Reanimation::class, 'migration_patient_id', 'original_id')
|
||||
->orderBy('out_date', 'desc');
|
||||
}
|
||||
|
||||
// Пересечение с отчетным периодом
|
||||
@@ -105,4 +113,23 @@ class ReportDutyMigrationPatient extends Model
|
||||
->orderBy('ingoing_date', 'desc')
|
||||
->limit(1)->first();
|
||||
}
|
||||
|
||||
public function getAdmittedInCurrentAttribute(DateRange $dateRange): bool
|
||||
{
|
||||
// Получаем дату поступления из последнего движения
|
||||
$ingoing = $this->ingoing_date;
|
||||
|
||||
if (!$ingoing) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$ingoingLocal = Carbon::parse($ingoing)->setTimezone(config('app.timezone', 'Europe/Moscow'));
|
||||
$now = $dateRange->endDate;//Carbon::now(config('app.timezone', 'Europe/Moscow'));
|
||||
|
||||
// Окно смены: вчера 09:00 → сегодня 09:00
|
||||
$shiftStart = $now->copy()->subDay()->setTime(9, 0);
|
||||
$shiftEnd = $now->copy()->setTime(9, 0);
|
||||
|
||||
return $ingoingLocal->between($shiftStart, $shiftEnd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,13 +39,24 @@ class ReportDutyPatient extends Model
|
||||
|
||||
public function operations(): \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
{
|
||||
return $this->hasMany(SurgicalOperation::class, 'medical_history_id', 'id');
|
||||
return $this->hasMany(SurgicalOperation::class, 'medical_history_id', 'original_id');
|
||||
}
|
||||
|
||||
public function latestMigration()
|
||||
{
|
||||
return $this->hasOne(ReportDutyMigrationPatient::class, 'medical_history_id', 'id')
|
||||
->latest('ingoing_date');
|
||||
->orderBy('ingoing_date', 'desc');
|
||||
}
|
||||
|
||||
public function observables()
|
||||
{
|
||||
return $this->hasMany(ObservableMedicalHistory::class, 'original_id', 'original_id');
|
||||
}
|
||||
|
||||
public function observable()
|
||||
{
|
||||
return $this->hasOne(ObservableMedicalHistory::class, 'original_id', 'original_id')
|
||||
->orderBy('observable_in', 'desc');
|
||||
}
|
||||
|
||||
public function operationsInDepartment($query, $departmentId)
|
||||
|
||||
55
app/Models/ReportDutyReanimation.php
Normal file
55
app/Models/ReportDutyReanimation.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Services\DateRange;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ReportDutyReanimation extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'original_id',
|
||||
'migration_patient_id',
|
||||
'medical_history_id',
|
||||
'in_date',
|
||||
'out_date',
|
||||
'description',
|
||||
'comment',
|
||||
'stationar_branch_id',
|
||||
'migration_stationar_branch_id',
|
||||
'migration_department_id',
|
||||
'doctor_id',
|
||||
'user_id',
|
||||
'mis_user_id',
|
||||
];
|
||||
|
||||
public function medicalHistory()
|
||||
{
|
||||
return $this->belongsTo(ReportDutyPatient::class, 'medical_history_id', 'id');
|
||||
}
|
||||
|
||||
public function migration()
|
||||
{
|
||||
return $this->belongsTo(ReportDutyMigrationPatient::class, 'migration_patient_id', 'id');
|
||||
}
|
||||
|
||||
// Фильтр по подразделению
|
||||
public function scopeDepartment($query, int $departmentId)
|
||||
{
|
||||
return $query->where('migration_department_id', $departmentId);
|
||||
}
|
||||
|
||||
public function scopeCurrentOrAdmitted($query, DateRange $dateRange)
|
||||
{
|
||||
return $query->where(function ($q) use ($dateRange) {
|
||||
// Вариант А: Пациент уже лежит (текущий)
|
||||
$q->whereNull('out_date')
|
||||
->whereNotNull('medical_history_id')
|
||||
->where('in_date', '<', $dateRange->startSql());
|
||||
})
|
||||
->orWhere(function ($q) use ($dateRange) {
|
||||
$q->where('in_date', '<=', $dateRange->endSql())
|
||||
->where('in_date', '>', $dateRange->startSql());
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ namespace App\Models;
|
||||
|
||||
use App\Services\DateRange;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Carbon;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class ReportNurseMigrationPatient extends Model
|
||||
@@ -105,4 +106,23 @@ class ReportNurseMigrationPatient extends Model
|
||||
->orderBy('ingoing_date', 'desc')
|
||||
->limit(1)->first();
|
||||
}
|
||||
|
||||
public function getAdmittedInCurrentAttribute(DateRange $dateRange): bool
|
||||
{
|
||||
// Получаем дату поступления из последнего движения
|
||||
$ingoing = $this->ingoing_date;
|
||||
|
||||
if (!$ingoing) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$ingoingLocal = Carbon::parse($ingoing)->setTimezone(config('app.timezone', 'Europe/Moscow'));
|
||||
$now = $dateRange->endDate;//Carbon::now(config('app.timezone', 'Europe/Moscow'));
|
||||
|
||||
// Окно смены: вчера 09:00 → сегодня 09:00
|
||||
$shiftStart = $now->copy()->subDay()->setTime(9, 0);
|
||||
$shiftEnd = $now->copy()->setTime(9, 0);
|
||||
|
||||
return $ingoingLocal->between($shiftStart, $shiftEnd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +91,7 @@ class UnifiedMigrationPatient extends Model
|
||||
->limit(1)->first();
|
||||
}
|
||||
|
||||
public function getAdmittedInCurrentAttribute(): bool
|
||||
public function getAdmittedInCurrentAttribute(DateRange $dateRange): bool
|
||||
{
|
||||
// Получаем дату поступления из последнего движения
|
||||
$ingoing = $this->ingoing_date;
|
||||
@@ -101,7 +101,7 @@ class UnifiedMigrationPatient extends Model
|
||||
}
|
||||
|
||||
$ingoingLocal = Carbon::parse($ingoing)->setTimezone(config('app.timezone', 'Europe/Moscow'));
|
||||
$now = Carbon::now(config('app.timezone', 'Europe/Moscow'));
|
||||
$now = $dateRange->endDate;//Carbon::now(config('app.timezone', 'Europe/Moscow'));
|
||||
|
||||
// Окно смены: вчера 09:00 → сегодня 09:00
|
||||
$shiftStart = $now->copy()->subDay()->setTime(9, 0);
|
||||
|
||||
@@ -26,8 +26,10 @@ class User extends Authenticatable
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'login',
|
||||
'email',
|
||||
'password',
|
||||
'is_active',
|
||||
'rf_lpudoctor_id',
|
||||
'rf_department_id',
|
||||
'current_role_id',
|
||||
@@ -76,7 +78,8 @@ class User extends Authenticatable
|
||||
return $this->hasMany(UserRole::class, 'rf_user_id', 'id');
|
||||
}
|
||||
|
||||
public function roles(): HasManyThrough
|
||||
// Переименовано из roles() чтобы не конфликтовать с Spatie HasRoles::roles()
|
||||
public function appRoles(): HasManyThrough
|
||||
{
|
||||
return $this->hasManyThrough(
|
||||
Role::class,
|
||||
@@ -90,7 +93,7 @@ class User extends Authenticatable
|
||||
|
||||
public function currentRole()
|
||||
{
|
||||
$defaultRoleId = $this->roles()->where('is_default', true)->first()->role_id;
|
||||
$defaultRoleId = $this->appRoles()->where('is_default', true)->first()->role_id;
|
||||
|
||||
if (app()->runningInConsole()) {
|
||||
// Код выполняется в CLI (команда artisan, тесты и т.д.)
|
||||
@@ -128,19 +131,47 @@ class User extends Authenticatable
|
||||
}
|
||||
|
||||
// Методы для проверки ролей
|
||||
public function isAdmin()
|
||||
public function isAdmin(): bool
|
||||
{
|
||||
return $this->currentRole()->slug === 'admin';
|
||||
}
|
||||
|
||||
public function isDoctor()
|
||||
public function isChiefDoctor(): bool
|
||||
{
|
||||
return $this->currentRole()->slug === 'doctor';
|
||||
return $this->currentRole()->slug === 'gv';
|
||||
}
|
||||
|
||||
public function isHeadOfDepartment()
|
||||
public function isDeputyChief(): bool
|
||||
{
|
||||
return $this->currentRole()->slug === 'head_of_department';
|
||||
return $this->currentRole()->slug === 'zam';
|
||||
}
|
||||
|
||||
public function isHeadOfDepartment(): bool
|
||||
{
|
||||
return $this->currentRole()->slug === 'zav';
|
||||
}
|
||||
|
||||
public function isDoctor(): bool
|
||||
{
|
||||
return $this->currentRole()->slug === 'dej';
|
||||
}
|
||||
|
||||
public function isNurse(): bool
|
||||
{
|
||||
return $this->currentRole()->slug === 'nurse';
|
||||
}
|
||||
|
||||
public function isSeniorStaff(): bool
|
||||
{
|
||||
return $this->isAdmin() || $this->isChiefDoctor() || $this->isDeputyChief() || $this->isHeadOfDepartment();
|
||||
}
|
||||
|
||||
public function currentRoleCan(string $permission): bool
|
||||
{
|
||||
$slug = $this->currentRole()->slug;
|
||||
return \Spatie\Permission\Models\Role::findByName($slug)
|
||||
?->permissions->pluck('name')
|
||||
->contains($permission) ?? false;
|
||||
}
|
||||
|
||||
public function lpuDoctor()
|
||||
@@ -153,29 +184,12 @@ class User extends Authenticatable
|
||||
{
|
||||
$departments = Department::all();
|
||||
|
||||
if ($this->isAdmin()) {
|
||||
if ($this->isSeniorStaff()) {
|
||||
return $departments;
|
||||
}
|
||||
|
||||
return $this->department ? [$this->department] : [];
|
||||
}
|
||||
|
||||
// Получение доступных действий
|
||||
public function permissions()
|
||||
{
|
||||
$permissions = [
|
||||
'view_dashboard' => true,
|
||||
'view_metrics' => true,
|
||||
'view_reports' => true,
|
||||
];
|
||||
|
||||
if ($this->isAdmin() || $this->isDoctor() || $this->isHeadOfDepartment()) {
|
||||
$permissions['create_metrics'] = true;
|
||||
$permissions['edit_metrics'] = true;
|
||||
$permissions['delete_metrics'] = true;
|
||||
$permissions['manage_users'] = $this->isAdmin();
|
||||
}
|
||||
|
||||
return $permissions;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ class UserDepartment extends Model
|
||||
{
|
||||
public $timestamps = false;
|
||||
|
||||
protected $primaryKey = 'user_department_id';
|
||||
|
||||
protected $fillable = [
|
||||
'rf_user_id',
|
||||
'rf_department_id',
|
||||
|
||||
Reference in New Issue
Block a user