Files
Investidor-V2/app/Services/ExistingBusinessVerification.php
nala 3dc7c5c649
tests / ci (push) Canceled after 0s
commit 30/9
2026-09-30 09:05:43 +02:00

80 lines
3.7 KiB
PHP

<?php
namespace App\Services;
use App\Models\ExistingBusiness;
use App\Models\ExistingBusinessObject;
use App\Models\User;
use App\Services\Mne\BusinessRepository;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Validator;
final class ExistingBusinessVerification
{
public const CHECKS = ['identity', 'representation', 'declared_ownership', 'documentary_ownership', 'document_received', 'document_validated', 'independent_valuation', 'land_right_documented', 'license_confirmed', 'validity', 'legal_transfer', 'prior_authorization'];
private function contentVersion(string $id): int
{
return (int) DB::table('existing_business_revisions')->where('business_id', $id)
->whereIn('action', ['created', 'updated', 'object_added', 'object_updated'])->max('version_no');
}
/** @param array<string,mixed> $input */
public function save(User $actor, string $id, array $input): ExistingBusiness
{
$data = Validator::make($input, [
'version_no' => ['required', 'integer', 'min:1'],
'object_id' => ['nullable', 'uuid'],
'check' => ['required', 'in:'.implode(',', self::CHECKS)],
'outcome' => ['required', 'in:confirmed,rejected,inconclusive,revoked'],
'evidence_reference' => ['required', 'string', 'min:10', 'max:2000'],
'note' => ['required', 'string', 'min:10', 'max:2000'],
'valid_until' => ['required_if:outcome,confirmed', 'nullable', 'date_format:Y-m-d', 'after_or_equal:today'],
])->validate();
return DB::transaction(function () use ($actor, $id, $data) {
$business = app(BusinessRepository::class)->find($id, true);
Gate::forUser($actor)->authorize('review', $business);
abort_unless($business->status === 'in_review' && $business->version_no === $data['version_no'], 409, 'Reabra a versão actual em revisão.');
if (! empty($data['object_id'])) {
ExistingBusinessObject::where('business_id', $id)->whereKey($data['object_id'])->firstOrFail();
}
$data['content_version'] = $this->contentVersion($id);
$business->version_no++;
$business->save();
app(ExistingBusinessDrafts::class)->snapshot($business, $actor, 'verification', $data['note'], $data);
return $business;
});
}
/** Latest finding for each check/subject; complete evidence remains in immutable revisions.
* @return array<int,array<string,mixed>>
*/
public function findings(ExistingBusiness $business): array
{
$contentVersion = $this->contentVersion($business->id);
$findings = [];
foreach (DB::table('existing_business_revisions')->where('business_id', $business->id)
->where('action', 'verification')->orderByDesc('version_no')->cursor() as $revision) {
$data = json_decode(Crypt::decryptString($revision->snapshot), true, 512, JSON_THROW_ON_ERROR)['context'];
$key = ($data['object_id'] ?? 'business').':'.$data['check'];
if (isset($findings[$key])) {
continue;
}
$effective = $data['outcome'];
if ($data['content_version'] !== $contentVersion) {
$effective = 'stale';
} elseif (! empty($data['valid_until']) && $data['valid_until'] < today()->toDateString()) {
$effective = 'expired';
}
$findings[$key] = [...$data, 'effective_outcome' => $effective,
'version_no' => $revision->version_no, 'created_at' => $revision->created_at];
}
return array_values($findings);
}
}