Files
compras/app/Services/ReceivedCandidateAvailability.php
nala 63d20b10fc
tests / ci (push) Canceled after 0s
commit de armazens
2026-11-04 09:40:26 +02:00

81 lines
4.0 KiB
PHP

<?php
namespace App\Services;
use App\Models\PeopleProcess;
use App\Models\User;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
class ReceivedCandidateAvailability
{
/** Add received availability only to the authorized read projection, never to decision matrices.
* @param list<array<string,mixed>> $rows
* @return list<array<string,mixed>>
*/
public function attach(User $actor, PeopleProcess $record, array $rows): array
{
if ($rows === [] || ! Schema::hasTable('candidate_triage') || ! Schema::hasTable('candidate_submissions')) {
return $rows;
}
$current = User::query()->where('tenant_id', $record->tenant_id)->whereKey($actor->id)->first();
if ($current === null || ! app(CandidateTriage::class)->canRead($current, $record)) {
return $rows;
}
$sources = DB::table('candidate_triage as t')->join('candidate_submissions as s', function (JoinClause $join) {
$join->on('s.id', '=', 't.candidate_submission_id')->on('s.tenant_id', '=', 't.tenant_id')->on('s.people_process_id', '=', 't.people_process_id');
})->where('t.tenant_id', $record->tenant_id)->where('t.people_process_id', $record->id)->where('t.outcome', 'accepted_for_analysis')
->whereIn('t.people_application_id', array_column($rows, 'id'))
->get(['t.people_application_id', 't.profile_version', 's.id', 's.reference', 's.snapshot'])->keyBy('people_application_id');
return array_map(function (array $row) use ($sources) {
$source = $sources->get($row['id']);
if ($source === null) {
return $row;
}
$snapshot = json_decode($source->snapshot, true, 32, JSON_THROW_ON_ERROR);
$data = $snapshot['profile']['data'] ?? [];
$main = $this->location($data['availability'] ?? null);
if ($main === null || ($snapshot['profile']['version'] ?? null) !== $source->profile_version) {
return $row;
}
$additional = [];
$references = [];
foreach ($data['additional_availability'] ?? [] as $index => $location) {
$value = $this->location($location);
if ($value !== null) {
$additional[] = $value;
$references[] = $this->reference($data['additional_availability_references'][$index] ?? null);
}
}
return [...$row, 'received_availability' => ['submission_id' => $source->id, 'reference' => $source->reference, 'profile_version' => $source->profile_version,
'availability' => $main, 'additional_availability' => $additional,
'availability_reference' => $this->reference($data['availability_reference'] ?? null), 'additional_availability_references' => $references]];
}, $rows);
}
/** @return array{province:string,district:string,locality:string}|null */
private function location(mixed $value): ?array
{
if (! is_array($value) || ! is_string($value['province'] ?? null) || ! is_string($value['district'] ?? null) || ! is_string($value['locality'] ?? null)
|| trim($value['province']) === '' || trim($value['district']) === '' || trim($value['locality']) === '') {
return null;
}
return ['province' => $value['province'], 'district' => $value['district'], 'locality' => $value['locality']];
}
/** @return array{country:string,catalogue:string,record_id:string}|null */
private function reference(mixed $value): ?array
{
if (! is_array($value) || ($value['country'] ?? null) !== 'MZ' || ! is_string($value['catalogue'] ?? null) || ! preg_match('/^[a-f0-9]{64}$/D', $value['catalogue'])
|| ! is_string($value['record_id'] ?? null) || ! preg_match('/^[0-9]{1,20}$/D', $value['record_id'])) {
return null;
}
return ['country' => 'MZ', 'catalogue' => $value['catalogue'], 'record_id' => $value['record_id']];
}
}