'integer', 'parallel_group' => 'integer', 'acted_at' => 'datetime', 'is_read' => 'boolean', 'read_at' => 'datetime', ]; protected $fillable = [ 'approval_id', 'step_order', 'step_type', 'parallel_group', 'approver_id', 'acted_by', 'approver_name', 'approver_department', 'approver_position', 'status', 'approval_type', 'comment', 'acted_at', 'is_read', 'read_at', ]; protected $attributes = [ 'status' => 'pending', 'is_read' => false, ]; // ========================================================================= // 상태 상수 // ========================================================================= public const STATUS_PENDING = 'pending'; public const STATUS_APPROVED = 'approved'; public const STATUS_REJECTED = 'rejected'; public const STATUS_SKIPPED = 'skipped'; public const STATUS_ON_HOLD = 'on_hold'; public const STATUSES = [ self::STATUS_PENDING, self::STATUS_APPROVED, self::STATUS_REJECTED, self::STATUS_SKIPPED, self::STATUS_ON_HOLD, ]; // ========================================================================= // 관계 정의 // ========================================================================= public function approval(): BelongsTo { return $this->belongsTo(Approval::class, 'approval_id'); } public function approver(): BelongsTo { return $this->belongsTo(User::class, 'approver_id'); } public function actedBy(): BelongsTo { return $this->belongsTo(User::class, 'acted_by'); } // ========================================================================= // 스코프 // ========================================================================= public function scopePending($query) { return $query->where('status', self::STATUS_PENDING); } public function scopeApproved($query) { return $query->where('status', self::STATUS_APPROVED); } public function scopeByApprover($query, int $userId) { return $query->where('approver_id', $userId); } public function scopeApprovalOnly($query) { return $query->whereIn('step_type', [ApprovalLine::STEP_TYPE_APPROVAL, ApprovalLine::STEP_TYPE_AGREEMENT]); } public function scopeReferenceOnly($query) { return $query->where('step_type', ApprovalLine::STEP_TYPE_REFERENCE); } // ========================================================================= // 헬퍼 메서드 // ========================================================================= public function isActionable(): bool { return $this->status === self::STATUS_PENDING && in_array($this->step_type, [ApprovalLine::STEP_TYPE_APPROVAL, ApprovalLine::STEP_TYPE_AGREEMENT]); } public function isReference(): bool { return $this->step_type === ApprovalLine::STEP_TYPE_REFERENCE; } public function getStatusLabelAttribute(): string { return match ($this->status) { self::STATUS_PENDING => '대기', self::STATUS_APPROVED => '승인', self::STATUS_REJECTED => '반려', self::STATUS_SKIPPED => '건너뜀', self::STATUS_ON_HOLD => '보류', default => $this->status, }; } public function getStepTypeLabelAttribute(): string { return match ($this->step_type) { ApprovalLine::STEP_TYPE_APPROVAL => '결재', ApprovalLine::STEP_TYPE_AGREEMENT => '합의', ApprovalLine::STEP_TYPE_REFERENCE => '참조', default => $this->step_type, }; } }