'date', 'ends_on' => 'date', ]; } public function organization(): BelongsTo { return $this->belongsTo(Organization::class, 'organization_id'); } public function member(): BelongsTo { return $this->belongsTo(Member::class, 'member_id'); } public function department(): BelongsTo { return $this->belongsTo(Department::class, 'department_id'); } public function careUnit(): BelongsTo { return $this->belongsTo(CareUnit::class, 'care_unit_id'); } /** * Legacy link from when duty roster auto-created unit placements. * Shift assignments live on RosterEntry; new roster rows do not set this. */ public function rosterEntries(): HasMany { return $this->hasMany(RosterEntry::class, 'staff_assignment_id'); } public function scopeUnitPlacements(Builder $query): Builder { return $query->whereDoesntHave('rosterEntries'); } public function scopeEffectiveOn(Builder $query, CarbonInterface|string|null $date = null): Builder { $day = $date ? (\Illuminate\Support\Carbon::parse($date)->toDateString()) : now()->toDateString(); return $query ->whereDate('starts_on', '<=', $day) ->where(function (Builder $q) use ($day) { $q->whereNull('ends_on')->orWhereDate('ends_on', '>=', $day); }); } public function scopeActiveStatus(Builder $query): Builder { return $query->where('status', 'active'); } public function isEffectiveOn(CarbonInterface|string|null $date = null): bool { $day = $date ? \Illuminate\Support\Carbon::parse($date)->startOfDay() : now()->startOfDay(); if ($this->starts_on->gt($day)) { return false; } if ($this->ends_on !== null && $this->ends_on->lt($day)) { return false; } return $this->status === 'active'; } }