Adapt land status to saved investment land need

This commit is contained in:
2026-10-23 14:13:58 +02:00
parent f2150b1912
commit 5bda74aedc
10 changed files with 192 additions and 3 deletions
+14
View File
@@ -563,6 +563,7 @@ final class InvestorContext
$case = $this->desk->case($actor, $id);
abort_unless($this->desk->owns($actor, $case, false) || $actor->can('manage-portal'), 403);
$ctx = $this->state($case);
$ctx['land_preparation'] = $this->landPreparation($case);
$path = app(InvestorPath::class)->view($actor, $id);
$ctx['path_applicability'] = ['definition_id' => $path['definition_id'], 'definition_version' => $path['definition_version'], 'source_sha256' => $path['source_sha256'],
'steps' => array_values(array_map(fn ($step) => Arr::only($step, ['code', 'title', 'applicability', 'reason', 'state', 'context_review_required']), array_filter($path['steps'], fn ($step) => ! empty($step['branch']))))];
@@ -591,6 +592,14 @@ final class InvestorContext
return DB::table('desk_documents as l')->join('documents as d', 'd.id', '=', 'l.document_id')->where('l.case_id', $id)->whereNull('d.deleted_at')->get(['l.id', 'l.document_id', 'd.file_name', 'd.status', 'd.malware_scan_status', 'd.version_no', 'd.sha256'])->map(fn ($row) => (array) $row)->all();
}
public function landPreparation(\stdClass $case): array
{
$path = $this->desk->decode($case->payload)['investor_path'] ?? [];
$need = $path['facts']['land_needed'] ?? 'unknown';
return ['need' => in_array($need, ['yes', 'no'], true) ? $need : 'unknown', 'reason' => $path['fact_reasons']['land_needed'] ?? ''];
}
public function prefill(array $ctx): array
{
$f = $ctx['fields'];
@@ -598,6 +607,10 @@ final class InvestorContext
// Entering this tool from an investment dossier selects the existing investment-analysis route.
// The reconciliation preserves any purpose deliberately chosen in an existing simulator draft.
$sim += ['journey_type' => 'investor', 'journey_type_label' => 'Avaliar o meu investimento', 'purpose' => 'simulate_only', 'purpose_label' => 'Apenas simular'];
if (($ctx['land_preparation']['need'] ?? null) === 'yes') {
$tenure = ['duat' => ['duat', 'DUAT'], 'lease' => ['leased', 'Arrendado']][$f['site_tenure'] ?? ''] ?? null;
if ($tenure) [$sim['land_status'], $sim['land_status_label']] = $tenure;
}
// Human labels travel with the exact existing identifiers, not inferred locations.
foreach (app(InvestmentGeography::class)->publicReferences()->whereIn('id', array_values(Arr::only($f, array_keys(InvestmentGeography::FIELDS))))->get(['id', 'name']) as $place) {
foreach (array_keys(InvestmentGeography::FIELDS) as $key) {
@@ -613,6 +626,7 @@ final class InvestorContext
'opportunity_choices' => $ctx['opportunity_choices'] ?? [],
'requirements_assessment' => $ctx['requirements_assessment'] ?? null,
'path' => $ctx['path_applicability'] ?? null,
'land_preparation' => $ctx['land_preparation'] ?? null,
'preparation' => isset($ctx['preparation_assessment']) ? Arr::except($ctx['preparation_assessment'], ['questions']) : null,
'fields' => Arr::only($f, ['investor_origin', 'tax_residency_country', 'entity_stage', 'currency', 'expected_start_date', 'investment_regime']),
'notice' => 'Referências e declarações para preparação; sem reconhecimento automático de elegibilidade ou benefício.',
+32 -1
View File
@@ -60,9 +60,11 @@ final class SimulationFormDrafts
throw new HttpException(409, 'Já existe um rascunho activo. Carregue-o antes de guardar novas alterações.');
}
if ($case) $data['economic_snapshot'] = $this->landContext($data['economic_snapshot'], $case);
$snapshotHash = hash('sha256', json_encode($this->canonical($data), JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE));
$draft = SimulationFormDraft::query()->create([
'id' => (string) Str::uuid(), 'owner_user_id' => $actorId, 'anonymous_token_hash' => $tokenHash,
'version_no' => 1, 'create_key_hash' => $keyHash, 'create_payload_hash' => $payloadHash, 'snapshot_sha256' => $payloadHash,
'version_no' => 1, 'create_key_hash' => $keyHash, 'create_payload_hash' => $payloadHash, 'snapshot_sha256' => $snapshotHash,
'personal_snapshot' => SimulationFormDraft::encryptSnapshot($data['personal_snapshot']),
'economic_snapshot' => SimulationFormDraft::encryptSnapshot($data['economic_snapshot']),
'expires_at' => $actorId === null ? now()->addDays(30) : null,
@@ -89,6 +91,7 @@ final class SimulationFormDrafts
$this->checkContext($request, $draft);
$this->requireMatch($request, $draft);
$previousEconomic = SimulationFormDraft::decryptSnapshot($draft->economic_snapshot);
$data['economic_snapshot'] = $this->preserveInactiveLandStatus($draft, $data['economic_snapshot'], $previousEconomic, $actorId);
if (($data['economic_snapshot']['reuse_simulation'] ?? null) === 'no') {
unset($data['economic_snapshot']['simulation_sources']);
} elseif (is_array($previousEconomic['simulation_sources'] ?? null)) {
@@ -456,6 +459,34 @@ final class SimulationFormDrafts
return $query;
}
private function preserveInactiveLandStatus(SimulationFormDraft $draft, array $economic, array $previous, ?int $actorId): array
{
if (! $actorId || ! config('investor-path.enabled') || ! app(VirtualDesk::class)->installed()) return $economic;
// Use the real owner-bound dossier even if a request omits its context parameter.
$case = DB::table('desk_cases')->where('simulation_id', $draft->id)->where('user_id', $actorId)->first();
if (! $case) return $economic;
return $this->landContext($economic, $case, $previous);
}
private function landContext(array $economic, \stdClass $case, ?array $previous = null): array
{
$decision = app(InvestorContext::class)->landPreparation($case);
if ($decision['need'] !== 'yes' && $previous !== null) {
if (array_key_exists('land_status', $previous)) $economic['land_status'] = $previous['land_status'];
else unset($economic['land_status']);
if (isset($previous['selection_labels']['land_status'])) $economic['selection_labels']['land_status'] = $previous['selection_labels']['land_status'];
else unset($economic['selection_labels']['land_status']);
}
$snapshot = json_decode($economic['context_applicability_snapshot'] ?? $previous['context_applicability_snapshot'] ?? '', true);
if (! is_array($snapshot) || ($snapshot['version'] ?? null) !== 1) {
$snapshot = ['version' => 1, 'context_revision' => app(InvestorContext::class)->state($case)['revision']];
}
$snapshot['land_preparation'] = $decision;
$economic['context_applicability_snapshot'] = json_encode($snapshot, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE);
return $economic;
}
private function checkContext(Request $request, SimulationFormDraft $draft): void
{
$context = $request->input('context');