*/ protected function casts(): array { return [ 'summary' => 'array', 'generated_at' => 'datetime', 'published_at' => 'datetime', 'archived_at' => 'datetime', ]; } /** * @return BelongsTo */ public function workspace(): BelongsTo { return $this->belongsTo(Workspace::class); } /** * @return BelongsTo */ public function tenant(): BelongsTo { return $this->belongsTo(Tenant::class); } /** * @return BelongsTo */ public function evidenceSnapshot(): BelongsTo { return $this->belongsTo(EvidenceSnapshot::class); } /** * @return BelongsTo */ public function operationRun(): BelongsTo { return $this->belongsTo(OperationRun::class); } /** * @return BelongsTo */ public function initiator(): BelongsTo { return $this->belongsTo(User::class, 'initiated_by_user_id'); } /** * @return BelongsTo */ public function publisher(): BelongsTo { return $this->belongsTo(User::class, 'published_by_user_id'); } /** * @return BelongsTo */ public function currentExportReviewPack(): BelongsTo { return $this->belongsTo(ReviewPack::class, 'current_export_review_pack_id'); } /** * @return BelongsTo */ public function supersededByReview(): BelongsTo { return $this->belongsTo(self::class, 'superseded_by_review_id'); } /** * @return HasMany */ public function supersededReviews(): HasMany { return $this->hasMany(self::class, 'superseded_by_review_id'); } /** * @return HasMany */ public function sections(): HasMany { return $this->hasMany(TenantReviewSection::class)->orderBy('sort_order')->orderBy('id'); } /** * @return HasMany */ public function reviewPacks(): HasMany { return $this->hasMany(ReviewPack::class)->latest('generated_at'); } /** * @param Builder $query * @return Builder */ public function scopeForTenant(Builder $query, int $tenantId): Builder { return $query->where('tenant_id', $tenantId); } /** * @param Builder $query * @return Builder */ public function scopeForWorkspace(Builder $query, int $workspaceId): Builder { return $query->where('workspace_id', $workspaceId); } /** * @param Builder $query * @return Builder */ public function scopePublished(Builder $query): Builder { return $query->where('status', TenantReviewStatus::Published->value); } /** * @param Builder $query * @return Builder */ public function scopeMutable(Builder $query): Builder { return $query->whereIn('status', [ TenantReviewStatus::Draft->value, TenantReviewStatus::Ready->value, TenantReviewStatus::Failed->value, ]); } public function statusEnum(): TenantReviewStatus { return TenantReviewStatus::from((string) $this->status); } public function completenessEnum(): TenantReviewCompletenessState { return TenantReviewCompletenessState::tryFrom((string) $this->completeness_state) ?? TenantReviewCompletenessState::Missing; } public function isPublished(): bool { return $this->statusEnum()->isPublished(); } public function isMutable(): bool { return $this->statusEnum()->isMutable(); } /** * @return list */ public function publishBlockers(): array { $summary = is_array($this->summary) ? $this->summary : []; $blockers = $summary['publish_blockers'] ?? []; return is_array($blockers) ? array_values(array_map('strval', $blockers)) : []; } }