$query * @return array */ public function build(Builder $query, User $actor): array { $tasks = []; $approvals = []; $alerts = []; $total = 0; $active = 0; $contractDrafts = 0; $workflow = app(PeopleHiring::class); $access = app(PeopleAccess::class); $maintenance = app(OrganizationAccess::class)->maintenanceOnly($actor); foreach ((clone $query)->orderBy('id')->lazyById(100) as $record) { $total++; // Preparing a draft does not close a hiring process or prove a signed contract. $active++; $contractDrafts += $record->status === 'contract_draft' ? 1 : 0; $scopes = $access->scopes($actor, $record); $prepare = ! $maintenance && in_array('prepare', $scopes, true) && $workflow->allowed($actor, $record->kind, 'prepare'); $review = ! $maintenance && in_array('review', $scopes, true) && $workflow->allowed($actor, $record->kind, 'review'); $approve = ! $maintenance && in_array('approve', $scopes, true) && $workflow->allowed($actor, $record->kind, 'review'); $contract = ! $maintenance && in_array('contract', $scopes, true) && $workflow->allowed($actor, $record->kind, 'prepare'); $base = ['process_id' => $record->id, 'reference' => 'PES-'.$record->id, 'title' => $record->title, 'kind' => $record->kind, 'status' => $record->status]; $pendingTriage = in_array($record->status, ['open', 'assessment'], true) ? app(CandidateTriage::class)->pending($record) : 0; $item = fn (string $action, string $label, ?string $due = null, ?int $application = null) => [...$base, 'id' => $record->id.':'.$action.($application === null ? '' : ':'.$application), 'action' => $action, 'label' => $label, 'due_on' => $due, 'href' => '/pessoas/'.$record->id.($application === null ? '' : '?candidatura='.$application).'#people-'.$action]; if (in_array($record->status, ['draft', 'open'], true) && $record->closes_on < today()->toDateString()) { $alerts[] = [...$base, 'id' => $record->id.':reception', 'label' => 'Prazo de candidaturas vencido', 'due_on' => $record->closes_on, 'href' => '/pessoas/'.$record->id]; } if ($pendingTriage > 0) { if ($prepare) { $tasks[] = [...$item('triage', 'Triar candidaturas electrónicas ('.$pendingTriage.')'), 'href' => '/pessoas/'.$record->id.'/recebidas']; } } elseif ($record->status === 'draft' && $review && $actor->id !== $record->created_by && $record->closes_on >= today()->toDateString()) { $approvals[] = $item('open', 'Conferir abertura', $record->closes_on); } elseif ($record->status === 'open' && $prepare) { $tasks[] = today()->toDateString() >= $record->closes_on ? $item('close', 'Encerrar recepção e iniciar avaliação', $record->closes_on) : $item('apply', 'Gerir recepção de candidaturas', $record->closes_on); } elseif ($record->status === 'assessment' && ($prepare || $review)) { $rows = $workflow->matrix($record); if ($review && $actor->id !== $record->created_by) { foreach ($rows as $row) { if ($row['registered_by'] !== $actor->id && ! in_array($actor->id, array_column($row['reviews'], 'reviewed_by'), true) && $row['evaluation_count'] < $record->rules['minimum_reviews']) { $tasks[] = $item('evaluate', 'Avaliar candidatura #'.$row['id'], null, $row['id']); } } } if ($prepare && $rows !== [] && ! in_array(null, array_column($rows, 'score_minor'), true)) { $tasks[] = $item('select', 'Preparar proposta de selecção'); } } elseif ($record->status === 'selection_review' && $approve && ! in_array($actor->id, $workflow->decisionContributors($record), true)) { $approvals[] = $item('confirm', 'Conferir selecção ou devolver para análise'); } elseif ($record->status === 'selected' && $contract) { $tasks[] = $item('contract', 'Preparar minuta contratual'); } if (Schema::hasTable('people_clarifications')) { $clarifications = app(PeopleClarifications::class); foreach ($clarifications->sources($actor, $record) as $source) { $tasks[] = [...$base, 'id' => 'clarification-source-'.$source['review_id'].'-'.$source['criterion_index'], 'action' => 'clarification_assign', 'label' => 'Atribuir esclarecimento da avaliação #'.$source['review_id'].' · critério '.($source['criterion_index'] + 1), 'due_on' => null, 'href' => '/pessoas/'.$record->id.'#clarification-source-'.$source['review_id'].'-'.$source['criterion_index']]; } foreach (DB::table('people_clarifications')->where('tenant_id', $record->tenant_id)->where('people_process_id', $record->id)->where('status', '!=', 'resolved')->orderBy('id')->get() as $clarification) { $link = '/pessoas/'.$record->id.'#clarification-'.$clarification->id; if ($clarification->due_on < today()->toDateString()) { $alerts[] = [...$base, 'id' => 'clarification-'.$clarification->id, 'label' => 'Esclarecimento #'.$clarification->id.' com prazo vencido', 'due_on' => $clarification->due_on, 'href' => $link]; } if ($clarifications->canRespond($actor, $record, $clarification)) { $tasks[] = [...$base, 'id' => 'clarification-'.$clarification->id.':respond', 'action' => 'clarification_respond', 'label' => 'Responder ao esclarecimento #'.$clarification->id, 'due_on' => $clarification->due_on, 'href' => $link]; } elseif ($clarification->status === 'responded' && $clarifications->canReview($actor, $record, $clarification)) { $tasks[] = [...$base, 'id' => 'clarification-'.$clarification->id.':review', 'action' => 'clarification_review', 'label' => 'Conferir esclarecimento #'.$clarification->id, 'due_on' => $clarification->due_on, 'href' => $link]; } } } } return ['updated_at' => now()->toIso8601String(), 'counts' => ['processes' => $total, 'active' => $active, 'contract_drafts' => $contractDrafts, 'tasks' => count($tasks), 'approvals' => count($approvals), 'overdue' => count($alerts)], 'tasks' => $tasks, 'approvals' => $approvals, 'alerts' => $alerts]; } }