'datetime', 'accepted_at' => 'datetime', ]; /** * Get the full contact name */ public function getContactFullNameAttribute(): ?string { $parts = array_filter([ $this->contact_first_name, $this->contact_last_name, ]); return !empty($parts) ? implode(' ', $parts) : null; } /** * Generate a unique invitation token */ public static function generateToken(): string { do { $token = Str::random(64); } while (self::where('token', $token)->exists()); return $token; } /** * Check if invitation is still valid */ public function isValid(): bool { return $this->status === 'pending' && $this->expires_at->isFuture(); } /** * Check if invitation is expired */ public function isExpired(): bool { return $this->expires_at->isPast() && $this->status === 'pending'; } /** * Mark invitation as expired */ public function markAsExpired(): void { $this->update(['status' => 'expired']); } /** * Accept the invitation */ public function accept(Partner $partner): void { $this->update([ 'status' => 'accepted', 'accepted_at' => now(), 'partner_id' => $partner->id, ]); } public function markAsAccepted(Partner $partner): void { $this->update([ 'status' => 'accepted', 'accepted_at' => now(), 'partner_id' => $partner->id, ]); } /** * Role assigned to the invitation */ public function role(): BelongsTo { return $this->belongsTo(Role::class); } /** * User who sent the invitation */ public function invitedBy(): BelongsTo { return $this->belongsTo(User::class, 'invited_by'); } /** * Partner created from this invitation */ public function partner(): BelongsTo { return $this->belongsTo(Partner::class); } /** * Scope to get pending invitations */ public function scopePending($query) { return $query->where('status', 'pending') ->where('expires_at', '>', now()); } /** * Scope to get expired invitations */ public function scopeExpired($query) { return $query->where('status', 'pending') ->where('expires_at', '<=', now()); } }