'date', 'membership_expires_at' => 'date', 'disability_verified_at' => 'datetime', ]; protected $appends = ['national_id']; public function user() { return $this->belongsTo(User::class); } public function payments() { return $this->hasMany(MembershipPayment::class); } /** * 關聯的收入記錄 */ public function incomes() { return $this->hasMany(Income::class); } /** * 取得會員的會費收入記錄 */ public function getMembershipFeeIncomes() { return $this->incomes() ->whereIn('income_type', [ Income::TYPE_MEMBERSHIP_FEE, Income::TYPE_ENTRANCE_FEE ]) ->get(); } /** * 取得會員的總收入金額 */ public function getTotalIncomeAttribute(): float { return $this->incomes() ->where('status', Income::STATUS_CONFIRMED) ->sum('amount'); } /** * Get the decrypted national ID */ public function getNationalIdAttribute(): ?string { if (empty($this->national_id_encrypted)) { return null; } try { return Crypt::decryptString($this->national_id_encrypted); } catch (\Exception $e) { \Log::error('Failed to decrypt national_id', [ 'member_id' => $this->id, 'error' => $e->getMessage(), ]); return null; } } /** * Set the national ID (encrypt and hash) */ public function setNationalIdAttribute(?string $value): void { if (empty($value)) { $this->attributes['national_id_encrypted'] = null; $this->attributes['national_id_hash'] = null; return; } $this->attributes['national_id_encrypted'] = Crypt::encryptString($value); $this->attributes['national_id_hash'] = hash('sha256', $value); } /** * Check if membership status is pending (not yet paid/verified) */ public function isPending(): bool { return $this->membership_status === self::STATUS_PENDING; } /** * Check if membership is active (paid & activated) */ public function isActive(): bool { return $this->membership_status === self::STATUS_ACTIVE; } /** * Check if membership is expired */ public function isExpired(): bool { return $this->membership_status === self::STATUS_EXPIRED; } /** * Check if membership is suspended */ public function isSuspended(): bool { return $this->membership_status === self::STATUS_SUSPENDED; } /** * Check if member has paid membership (active status with valid dates) */ public function hasPaidMembership(): bool { return $this->isActive() && $this->membership_started_at && $this->membership_expires_at && $this->membership_expires_at->isFuture(); } /** * Get the membership status badge class for display */ public function getMembershipStatusBadgeAttribute(): string { $label = $this->membership_status_label; $class = match($this->membership_status) { self::STATUS_PENDING => 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200', self::STATUS_ACTIVE => 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200', self::STATUS_EXPIRED => 'bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-200', self::STATUS_SUSPENDED => 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200', default => 'bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-200', }; return trim("{$label} {$class}"); } /** * Get the membership status label in Chinese */ public function getMembershipStatusLabelAttribute(): string { return match($this->membership_status) { self::STATUS_PENDING => '待繳費', self::STATUS_ACTIVE => '已啟用', self::STATUS_EXPIRED => '已過期', self::STATUS_SUSPENDED => '已暫停', default => $this->membership_status, }; } /** * Get the membership type label in Chinese */ public function getMembershipTypeLabelAttribute(): string { return match($this->membership_type) { self::TYPE_REGULAR => '一般會員', self::TYPE_HONORARY => '榮譽會員', self::TYPE_LIFETIME => '終身會員', self::TYPE_STUDENT => '學生會員', default => $this->membership_type, }; } /** * Get pending payment (if any) */ public function getPendingPayment() { return $this->payments() ->where('status', MembershipPayment::STATUS_PENDING) ->orWhere('status', MembershipPayment::STATUS_APPROVED_CASHIER) ->orWhere('status', MembershipPayment::STATUS_APPROVED_ACCOUNTANT) ->latest() ->first(); } /** * Check if member can submit payment */ public function canSubmitPayment(): bool { // Can submit if pending status and no pending payment return $this->isPending() && !$this->getPendingPayment(); } // ========== 身心障礙相關 ========== /** * 身心障礙手冊審核人 */ public function disabilityVerifiedBy() { return $this->belongsTo(User::class, 'disability_verified_by'); } /** * 是否有上傳身心障礙手冊 */ public function hasDisabilityCertificate(): bool { return !empty($this->disability_certificate_path); } /** * 身心障礙手冊是否待審核 */ public function isDisabilityPending(): bool { return $this->disability_certificate_status === self::DISABILITY_STATUS_PENDING; } /** * 身心障礙手冊是否已通過審核 */ public function hasApprovedDisability(): bool { return $this->disability_certificate_status === self::DISABILITY_STATUS_APPROVED; } /** * 身心障礙手冊是否被駁回 */ public function isDisabilityRejected(): bool { return $this->disability_certificate_status === self::DISABILITY_STATUS_REJECTED; } /** * 取得身心障礙狀態標籤 */ public function getDisabilityStatusLabelAttribute(): string { if (!$this->hasDisabilityCertificate()) { return '未上傳'; } return match ($this->disability_certificate_status) { self::DISABILITY_STATUS_PENDING => '審核中', self::DISABILITY_STATUS_APPROVED => '已通過', self::DISABILITY_STATUS_REJECTED => '已駁回', default => '未知', }; } /** * 取得身心障礙狀態的 Badge 樣式 */ public function getDisabilityStatusBadgeAttribute(): string { if (!$this->hasDisabilityCertificate()) { return 'bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-200'; } return match ($this->disability_certificate_status) { self::DISABILITY_STATUS_PENDING => 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200', self::DISABILITY_STATUS_APPROVED => 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200', self::DISABILITY_STATUS_REJECTED => 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200', default => 'bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-200', }; } /** * 審核通過身心障礙手冊 */ public function approveDisabilityCertificate(User $verifier): void { $this->update([ 'disability_certificate_status' => self::DISABILITY_STATUS_APPROVED, 'disability_verified_by' => $verifier->id, 'disability_verified_at' => now(), 'disability_rejection_reason' => null, ]); } /** * 駁回身心障礙手冊 */ public function rejectDisabilityCertificate(User $verifier, string $reason): void { $this->update([ 'disability_certificate_status' => self::DISABILITY_STATUS_REJECTED, 'disability_verified_by' => $verifier->id, 'disability_verified_at' => now(), 'disability_rejection_reason' => $reason, ]); } /** * 判斷下一次應繳哪種會費 */ public function getNextFeeType(): string { // 新會員(從未啟用過)= 入會會費 if ($this->membership_started_at === null) { return MembershipPayment::FEE_TYPE_ENTRANCE; } // 已有會籍 = 常年會費 return MembershipPayment::FEE_TYPE_ANNUAL; } }