'datetime', 'checked_in_at' => 'datetime', 'checked_out_at' => 'datetime', 'badge_expires_at' => 'datetime', 'policies_accepted' => 'boolean', 'vehicle_info' => 'array', 'contractor_details' => 'array', 'delivery_details' => 'array', 'allowed_areas' => 'array', 'integration_metadata' => 'array', ]; } protected static function booted(): void { static::creating(function (Visit $visit) { if (! $visit->public_id) { $visit->public_id = (string) Str::uuid(); } if (! $visit->qr_token) { $visit->qr_token = Str::random(32); } if (! $visit->badge_code) { $visit->badge_code = strtoupper(Str::random(8)); } }); } public function organization(): BelongsTo { return $this->belongsTo(Organization::class, 'organization_id'); } public function branch(): BelongsTo { return $this->belongsTo(Branch::class, 'branch_id'); } public function receptionDesk(): BelongsTo { return $this->belongsTo(ReceptionDesk::class, 'reception_desk_id'); } public function visitor(): BelongsTo { return $this->belongsTo(Visitor::class, 'visitor_id'); } public function host(): BelongsTo { return $this->belongsTo(Host::class, 'host_id'); } public function isInside(): bool { return $this->status === self::STATUS_CHECKED_IN; } public function isPending(): bool { return in_array($this->status, [ self::STATUS_EXPECTED, self::STATUS_SCHEDULED, self::STATUS_WAITING, self::STATUS_OVERDUE, ], true); } public function awaitingApproval(): bool { if ($this->status !== self::STATUS_WAITING || $this->checked_in_at !== null) { return false; } return (bool) ( data_get($this->contractor_details, '_awaiting_approval') || data_get($this->delivery_details, '_awaiting_approval') ); } /** @return array */ public function typeDetailEntries(): array { $entries = []; $labels = collect(config('frontdesk.visitor_type_config', [])) ->flatMap(fn (array $config) => collect($config['fields'] ?? []) ->mapWithKeys(fn (array $field) => [$field['name'] => $field['label']])); foreach (array_filter([$this->contractor_details, $this->delivery_details]) as $details) { foreach ($details as $key => $value) { if (str_starts_with((string) $key, '_') || $value === null || $value === '') { continue; } $label = $labels->get($key, str_replace('_', ' ', (string) $key)); if (str_contains((string) $key, 'photo') || str_contains((string) $key, 'signature')) { $entries[$label] = 'On file'; } elseif ($key === 'received_at') { $entries[$label] = \Illuminate\Support\Carbon::parse($value)->format('M j, Y g:i A'); } else { $entries[$label] = is_bool($value) ? ($value ? 'Yes' : 'No') : (string) $value; } } } return $entries; } public function canActivateCheckIn(): bool { return $this->isPending(); } public function isBadgeExpired(): bool { return $this->badge_expires_at && $this->badge_expires_at->isPast(); } public function scopeToday($query) { return $query->whereDate('checked_in_at', today()) ->orWhereDate('scheduled_at', today()); } public function scopeCurrentlyInside($query) { return $query->where('status', self::STATUS_CHECKED_IN); } }