kinds($this->actor($request)); abort_unless($kinds !== [], 403); $availableKinds = $kinds; $filter = $request->validate(['kind' => 'nullable|in:service,staff', 'search' => 'nullable|string|max:100', 'status' => ['nullable', Rule::in(PeopleWorkdesk::STATES)], 'from' => 'nullable|date_format:Y-m-d', 'to' => ['nullable', 'date_format:Y-m-d', Rule::when($request->filled('from'), 'after_or_equal:from')]]); if (isset($filter['kind'])) { abort_unless(in_array($filter['kind'], $kinds, true), 403); $kinds = [$filter['kind']]; } $ready = Schema::hasTable('people_processes') && Schema::hasTable('people_process_access'); $query = app(PeopleAccess::class)->visible(PeopleProcess::query()->whereIn('kind', $kinds), $this->actor($request)); if (($filter['search'] ?? '') !== '') { $search = $filter['search']; $query->where(function ($q) use ($search) { $q->whereLike('title', '%'.$search.'%'); if (preg_match('/^(?:PES-)?([1-9][0-9]*)$/D', $search, $matches)) { $reference = filter_var($matches[1], FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]); if ($reference !== false) { $q->orWhere('id', $reference); } } }); } if (! empty($filter['status'])) { $query->where('status', $filter['status']); } if (! empty($filter['from'])) { $query->whereDate('created_at', '>=', $filter['from']); } if (! empty($filter['to'])) { $query->whereDate('created_at', '<=', $filter['to']); } $workdesk = $ready ? app(PeopleWorkdesk::class)->build(clone $query, $this->actor($request)) : null; Inertia::encryptHistory(); $response = Inertia::render('people/index', ['ready' => $ready, 'kinds' => $kinds, 'availableKinds' => $availableKinds, 'filters' => $filter, 'workdesk' => $workdesk, 'insights' => $workdesk === null ? null : app(PeopleOperationalInsights::class)->build($workdesk), 'company' => Tenant::query()->findOrFail($this->actor($request)->tenant_id)->only(['name']), 'canPrepare' => array_values(array_filter($availableKinds, fn ($kind) => $workflow->allowed($this->actor($request), $kind, 'prepare'))), 'formFields' => ['staff' => PeopleForms::STAFF, 'service' => PeopleForms::SERVICE], 'administration' => $ready && $this->actor($request)->hasFunctions(['admin']) ? PeopleProcess::query()->whereIn('kind', $kinds)->orderByDesc('id')->get(['id', 'kind', 'status']) : [], 'processes' => $ready ? (clone $query)->select(['id', 'title', 'kind', 'status', 'closes_on', 'created_at'])->latest('id')->paginate(15)->withQueryString() : null])->toResponse($request); $response->headers->set('Cache-Control', 'private, no-store, max-age=0'); return $response; }); } public function show(Request $request, int $process, PeopleHiring $workflow): Response { return DB::transaction(function () use ($request, $process, $workflow) { if (DB::getDriverName() === 'pgsql' && DB::transactionLevel() === 1) { DB::statement('SET TRANSACTION ISOLATION LEVEL REPEATABLE READ READ ONLY'); } $record = PeopleProcess::query()->findOrFail($process); $access = app(PeopleAccess::class); abort_unless($access->can($this->actor($request), $record, 'view'), 403); $registrySearch = $request->validate(['cadastro' => 'nullable|string|max:100'])['cadastro'] ?? ''; $snapshot = $workflow->snapshot($record); $rows = $workflow->matrix($record); if (! $access->can($this->actor($request), $record, 'financial')) { unset($snapshot['rules']['annex']['fields']['financial']); unset($snapshot['selection']['rules']['annex']['fields']['financial']); } if (! $access->can($this->actor($request), $record, 'interviews')) { $rows = array_map(fn ($row) => [...$row, 'reviews' => []], $rows); if (isset($snapshot['selection']['rows'])) { $snapshot['selection']['rows'] = array_map(fn ($row) => [...$row, 'reviews' => []], $snapshot['selection']['rows']); } } if (! $access->can($this->actor($request), $record, 'contract') && ! $access->can($this->actor($request), $record, 'contract_view')) { $snapshot['contract'] = null; } $clarifications = app(PeopleClarifications::class)->page($this->actor($request), $record); $company = Tenant::query()->findOrFail($this->actor($request)->tenant_id); $policy = app(IntelligencePolicy::class)->company($company); $brief = app(PeopleEvidenceBrief::class)->build($snapshot, $rows, $clarifications, $access->can($this->actor($request), $record, 'financial'), $access->can($this->actor($request), $record, 'contract') || $access->can($this->actor($request), $record, 'contract_view')); Inertia::encryptHistory(); $response = Inertia::render('people/show', ['process' => $snapshot, 'candidates' => $rows, 'formFields' => $record->kind === 'staff' ? PeopleForms::STAFF : PeopleForms::SERVICE, 'clarifications' => $clarifications, 'brief' => $policy['people_brief'] ? $brief : null, 'registry' => app(PeopleRegistry::class)->lookup($this->actor($request), $record, $registrySearch), 'aiPreparation' => app(AiPreparationPlan::class)->preview($company, 'people', $brief), 'canPrepare' => $workflow->allowed($this->actor($request), $record->kind, 'prepare') && $access->can($this->actor($request), $record, 'prepare'), 'canViewElectronicSources' => app(CandidateTriage::class)->canRead($this->actor($request), $record), 'canReview' => $workflow->allowed($this->actor($request), $record->kind, 'review') && $access->can($this->actor($request), $record, 'review') && $this->actor($request)->id !== $record->created_by, 'canApprove' => $workflow->allowed($this->actor($request), $record->kind, 'review') && $access->can($this->actor($request), $record, 'approve') && ! in_array($this->actor($request)->id, $workflow->decisionContributors($record), true), 'canContract' => $workflow->allowed($this->actor($request), $record->kind, 'prepare') && $access->can($this->actor($request), $record, 'contract'), 'canManage' => $access->manages($this->actor($request), $record), 'history' => AuditLog::query()->where('action', 'like', 'people.%')->where('after->id', $record->id)->orderBy('id')->get(['id', 'action', 'user_id', 'created_at'])])->toResponse($request); $response->headers->set('Cache-Control', 'private, no-store, max-age=0'); return $response; }); } public function access(Request $request, int $process, PeopleAccess $access): Response { $record = PeopleProcess::query()->findOrFail($process); abort_unless($access->manages($this->actor($request), $record), 403); Inertia::encryptHistory(); $response = Inertia::render('people/access', ['process' => $record->only(['id', 'kind', 'status']), 'version' => DB::table('people_access_states')->where('people_process_id', $record->id)->value('version'), 'scopes' => PeopleAccess::SCOPES, 'users' => User::query()->where('tenant_id', $record->tenant_id)->orderBy('name')->get(['id', 'name', 'tenant_id', 'purchasing_role']) ->filter(fn ($user) => app(PeopleHiring::class)->allowed($user, $record->kind) || DB::table('people_process_access')->where('people_process_id', $record->id)->where('user_id', $user->id)->exists()) ->map(fn ($user) => $user->only(['id', 'name']))->values(), 'assignments' => DB::table('people_process_access')->where('tenant_id', $record->tenant_id)->where('people_process_id', $record->id)->get()->map(fn ($row) => ['user_id' => $row->user_id, 'scopes' => json_decode($row->scopes, true, 16, JSON_THROW_ON_ERROR), 'expires_at' => $row->expires_at]), 'canView' => $access->can($this->actor($request), $record, 'view')])->toResponse($request); $response->headers->set('Cache-Control', 'private, no-store, max-age=0'); return $response; } public function assign(Request $request, int $process, PeopleAccess $access): Response { $access->assign($this->actor($request), $process, $request->all()); return redirect('/pessoas/'.$process.'/acessos'); } public function command(Request $request, int $process, string $command, PeopleHiring $workflow): Response { abort_unless(Schema::hasTable('people_processes'), 503, 'Instalação de Pessoas e contratação pendente.'); $record = $workflow->execute($this->actor($request), $process, $command, $request->all()); return redirect('/pessoas/'.$record->id); } public function store(Request $request, PeopleHiring $workflow): Response { abort_unless(Schema::hasTable('people_processes'), 503); $record = $workflow->execute($this->actor($request), null, 'create', $request->all()); return redirect('/pessoas/'.$record->id); } public function clarification(Request $request, int $process, string $command, PeopleClarifications $workflow): Response { abort_unless(Schema::hasTable('people_clarifications'), 503, 'Instalação dos esclarecimentos pendente.'); $data = $request->validate(['clarification_id' => 'nullable|integer|min:1']); $workflow->execute($this->actor($request), $process, $data['clarification_id'] ?? null, $command, $request->all()); return redirect('/pessoas/'.$process.'#people-clarifications'); } private function actor(Request $request): User { $actor = $request->user('web'); abort_unless($actor instanceof User, 403); return $actor; } }