versao compilada
tests / ci (push) Canceled after 0s

This commit is contained in:
2026-10-15 17:37:00 +02:00
parent 9a96ee8dfe
commit c13f250b87
935 changed files with 210656 additions and 272 deletions
@@ -15,6 +15,7 @@ use App\Services\VerifiedDocumentBinary;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
@@ -55,8 +56,17 @@ class LandAssistanceController extends Controller
public function create(Request $request): Response
{
$context = $request->validate(['context' => ['nullable', 'uuid']])['context'] ?? null;
$dossier = null;
if ($context !== null) {
$case = app(\App\Services\VirtualDesk::class)->case($request->user(), $context);
abort_unless(config('investor-path.enabled') && (int) $case->user_id === $request->user()->id && ! $case->closed_at && $case->journey_id
&& DB::table('investor_journeys')->where('id', $case->journey_id)->whereNotNull('path_definition_id')->whereNull('deleted_at')->exists(), 403);
$dossier = ['id' => $case->id, 'title' => $case->title, 'url' => '/investimentos/'.$case->id];
}
return Inertia::render('land-assistance/form', [
'case' => null,
'returnDossier' => $dossier,
'user' => ['name' => $request->user()->name, 'email' => $request->user()->email],
'disclaimer' => self::DISCLAIMER,
'modelVersion' => 'land-assistance-v1',
@@ -71,6 +81,7 @@ class LandAssistanceController extends Controller
return Inertia::render('land-assistance/form', [
'case' => $case->only(['id', 'reference', 'version', 'status', 'payload']),
'returnDossier' => $this->dossierContext($case),
'user' => ['name' => $request->user()->name, 'email' => $request->user()->email],
'disclaimer' => self::DISCLAIMER,
'modelVersion' => 'land-assistance-v1',
@@ -152,8 +163,11 @@ class LandAssistanceController extends Controller
$submittedPayload = $request->payload();
$case = DB::transaction(function () use ($request, $id, $data, $submission, $submittedPayload): LandAssistanceCase {
$case = $this->owned($request, $id, true);
abort_unless(in_array($case->status, ['draft', 'waiting_applicant'], true), 409, 'Este pedido já não pode ser submetido neste estado.');
$this->checkVersion($case, (int) $data['version']);
$replaying = $case->status === 'support_requested';
abort_unless(in_array($case->status, ['draft', 'waiting_applicant', 'support_requested'], true), 409, 'Este pedido já não pode ser submetido neste estado.');
if (! $replaying) {
$this->checkVersion($case, (int) $data['version']);
}
$payload = [...$case->payload, ...$submittedPayload];
$payload['full_name'] = $payload['full_name'] ?? $request->user()->name;
if (! array_key_exists('email', $payload)) {
@@ -161,6 +175,14 @@ class LandAssistanceController extends Controller
}
$payload['email'] = is_string($payload['email']) && trim($payload['email']) !== '' ? strtolower(trim($payload['email'])) : null;
abort_if(empty($payload['email']) && empty($payload['phone']), 422, 'Indique pelo menos um contacto valido.');
if ($replaying) {
// A retry of the same accepted attempt recovers its original receipt.
// Changed data, another version or a missing receipt must not reopen the case.
abort_unless((int) $case->version === (int) $data['version'] + 1 && $case->interest_request_id
&& hash_equals(hash('sha256', json_encode(Arr::sortRecursive($case->payload), JSON_THROW_ON_ERROR)),
hash('sha256', json_encode(Arr::sortRecursive($payload), JSON_THROW_ON_ERROR))),
409, 'O pedido mudou; recupere o estado atual antes de voltar a enviar.');
}
$message = trim(($payload['request_description'] ?? 'Pedido de apoio sobre Terra e DUAT.')."\nReferência interna: {$case->reference}. Tipo: ".($payload['support_kind'] ?? 'não indicado').'.');
$interest = $submission->receive([
'kind' => 'support', 'fullName' => $payload['full_name'], 'email' => $payload['email'],
@@ -172,13 +194,25 @@ class LandAssistanceController extends Controller
], $case->crm_submission_key, fn () => InterestRequest::create([
'reference' => 'AP-'.now()->year.'-'.Str::ulid(), 'full_name' => $payload['full_name'],
'email' => $payload['email'], 'phone' => $payload['phone'] ?? null,
'country' => $payload['country'] ?? 'Não indicado', 'investor_type' => 'individual',
'country' => $payload['country'] ?? 'Não indicado',
// Use the existing intake vocabulary; retain the more specific declared category.
// This factory only runs for a new receipt. Older submissions/replay hashes stay intact.
'investor_type' => match ($payload['applicant_category'] ?? null) {
'individual' => 'individual', 'entity' => 'empresa', default => 'outro',
},
'sector_interest' => 'Terra e DUAT', 'message' => mb_substr($message, 0, 2000),
'privacy_consent' => true, 'consented_at' => now(), 'status' => 'received', 'source_page' => '/terra-duat',
'metadata' => ['kind' => 'support', 'portal_user_id' => $request->user()->id,
'land_case_id' => $case->id, 'land_case_reference' => $case->reference,
'applicant_category' => $payload['applicant_category'] ?? null,
'declared_origin_country_code' => $payload['declared_origin_country_code'] ?? null],
]));
if ($replaying) {
abort_unless((int) $interest->getKey() === (int) $case->interest_request_id && ! $interest->trashed(),
409, 'O recibo original não está disponível; recupere o estado do pedido.');
return $case;
}
$case->forceFill([
'payload' => $payload,
'status' => 'support_requested',
@@ -287,7 +321,13 @@ class LandAssistanceController extends Controller
public function download(Request $request, string $id, string $attachment): \Illuminate\Http\Response|BinaryFileResponse
{
$case = $this->owned($request, $id);
// Management uses its existing explicit permission; the owner URL stays owner-only.
if ($request->routeIs('land-assistance.manage.download')) {
abort_unless($request->user()->can('manage-portal'), 403);
$case = LandAssistanceCase::whereKey($id)->firstOrFail();
} else {
$case = $this->owned($request, $id);
}
$document = DB::table('land_assistance_documents as link')->join('documents as document', 'document.id', '=', 'link.document_id')
->where('link.case_id', $id)->where('link.id', $attachment)->where('document.status', 'active')->whereNull('document.deleted_at')
->where('document.malware_scan_status', 'clean')->whereColumn('document.malware_scanned_sha256', 'document.sha256')
@@ -327,7 +367,18 @@ class LandAssistanceController extends Controller
}
return ['countryName' => $country['code'] !== null ? $country['name'] : ($payload['country'] ?? 'Não informado'),
'declaredOrigin' => $countries->describe($payload['declared_origin_country_code'] ?? null), 'territoryPaths' => $paths];
'declaredOrigin' => $countries->describe($payload['declared_origin_country_code'] ?? null), 'territoryPaths' => $paths, 'returnDossier' => $this->dossierContext($case)];
}
private function dossierContext(LandAssistanceCase $case): ?array
{
$id = $case->payload['investor_path_case_id'] ?? null;
$actor = request()->user();
if (! $actor || (int) $case->user_id !== $actor->id || ! is_string($id) || ! Str::isUuid($id) || ! config('investor-path.enabled')) {
return null;
}
$dossier = DB::table('desk_cases')->where('id', $id)->where('user_id', $actor->id)->first();
return $dossier ? ['id' => $dossier->id, 'title' => $dossier->title, 'url' => '/investimentos/'.$dossier->id] : null;
}
public function manage(Request $request): Response