81 lines
4.0 KiB
PHP
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']];
|
|
}
|
|
}
|