ApprovalChainStatus::class, 'completed_at' => 'datetime', ]; } public function approvable(): MorphTo { return $this->morphTo(); } public function initiator(): BelongsTo { return $this->belongsTo(User::class, 'initiated_by')->withoutGlobalScopes(); } public function steps(): HasMany { return $this->hasMany(ApprovalStep::class)->orderBy('order'); } public function currentStep(): ?ApprovalStep { return $this->steps()->where('status', 'pending')->first(); } public function isFullyApproved(): bool { return $this->steps()->where('status', '!=', 'approved')->doesntExist(); } public function transitionTo(ApprovalChainStatus $newStatus): void { $allowed = $this->status->allowedTransitions(); if (!in_array($newStatus, $allowed)) { throw new \InvalidArgumentException( "Cannot transition chain from {$this->status->label()} to {$newStatus->label()}" ); } $updates = ['status' => $newStatus]; if (in_array($newStatus, [ApprovalChainStatus::Approved, ApprovalChainStatus::Rejected])) { $updates['completed_at'] = now(); } $this->update($updates); } }