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
@@ -1,6 +1,7 @@
import { SimulationPublishedIva, type PublishedIvaInput } from './simulation-published-iva';
import { SimulationMonthlyIva } from './simulation-monthly-iva';
import { inheritSimulatorFields, reusedInvestmentValue } from '@/lib/context-inheritance';
import { investmentLandPreparation } from '@/lib/investment-land-preparation';
import {
PublicDocumentContent,
type PublicDocument,
@@ -3283,6 +3284,16 @@ export function SimulationForm() {
: 'Avalie um projecto definido. A análise fiscal acompanha os fluxos financeiros, mas não prova rentabilidade nem concede incentivos.'}
</output>
)}
{current.id === 'location' && contextId && (
<aside className="rounded border p-3" aria-label="Necessidade de terra">
<p>{investmentLandPreparation(draft.context_applicability_snapshot).need === 'no'
? 'Não precisa de terra neste investimento. A situação do terreno não é pedida; os valores anteriores ficam conservados.'
: investmentLandPreparation(draft.context_applicability_snapshot).need === 'yes'
? 'Precisa de terra neste investimento. Reveja a situação do terreno já preenchida ou indique-a quando disponível.'
: 'Falta definir se precisa de terra. Pode guardar o rascunho e continuar sem preencher a situação do terreno.'}</p>
<a className="underline" href={`/investimentos/${contextId}`}>Rever a necessidade de terra no dossier</a>
</aside>
)}
<div className="grid gap-4 sm:grid-cols-2">
{fields.map((field) => {
const fieldRequired =
+6
View File
@@ -19,6 +19,12 @@ export function inheritCompatibleFields(scope:string, values:Record<string,unkno
const geoConflict=geo.some(key=>(!empty(values[key]) || key in previous) && signature(values[key])!==signature(source[key]) && previous[key]!==signature(values[key]));
for(const [key,value] of Object.entries(source)) {
if ((allowed && !allowed.includes(key)) || (empty(value) && !(key in previous))) continue;
if (key === 'context_applicability_snapshot') {
// Read-only assessment of the current dossier, not a deliberate form answer.
if (signature(values[key]) !== signature(value)) patch[key] = value;
next[key] = signature(value); inherited.push(key);
continue;
}
if (key.endsWith('_label')) {
const base = key.slice(0, -6);
const baseOwned = previous[base] === signature(values[base]);
@@ -0,0 +1,19 @@
export type LandPreparation = { need: 'yes' | 'no' | 'unknown'; reason: string };
/** Preparation decision from the same saved dossier, never evidence of a land right. */
export function investmentLandPreparation(raw: unknown): LandPreparation {
try {
const snapshot = JSON.parse(String(raw ?? ''));
if (snapshot.version !== 1) return { need: 'unknown', reason: '' };
const decision = snapshot.land_preparation;
if (decision && ['yes', 'no', 'unknown'].includes(decision.need))
return { need: decision.need, reason: typeof decision.reason === 'string' ? decision.reason : '' };
// Existing snapshots already carry S10 applicability; no reimport is required.
const step = Array.isArray(snapshot.path?.steps) ? snapshot.path.steps.find((row: { code?: string } | null) => row?.code === 'S10') : null;
return { need: step?.applicability === 'not_needed_for_preparation' ? 'no' : step?.applicability === 'applicable' ? 'yes' : 'unknown', reason: typeof step?.reason === 'string' ? step.reason : '' };
} catch { return { need: 'unknown', reason: '' }; }
}
export function landStatusVisible(draft: Record<string, unknown>): boolean {
return !draft.context_applicability_snapshot || investmentLandPreparation(draft.context_applicability_snapshot).need === 'yes';
}
+7
View File
@@ -1,4 +1,5 @@
import { displayValue } from './display-value.ts';
import { investmentLandPreparation } from './investment-land-preparation.ts';
import { monthlyIvaInput, monthlyIvaKey, readMonthlyIvaRecord, readMonthlyIvaSettings } from './monthly-iva.ts';
import { modeloAPurchaseReferences } from './iva-modelo-a.ts';
import { fiscalQaRows } from './fiscal-qa-preview.ts';
@@ -686,6 +687,12 @@ export function buildProjectDossierRows(
]);
}
}
if (draft.context_applicability_snapshot) {
const land = investmentLandPreparation(draft.context_applicability_snapshot);
rows.push(['Localização', 'Necessidade de terra', land.need === 'no' ? 'Não' : land.need === 'yes' ? 'Sim' : 'Por definir', land.reason || 'Decisão de preparação do dossier; não comprova direitos sobre a terra']);
if (land.need === 'yes') rows.push(['Localização', 'Situação do terreno', String(draft.land_status_label ?? ({ owned: 'Próprio', duat: 'DUAT', leased: 'Arrendado', requested: 'Pedido em curso', unknown: 'Por determinar' } as Record<string, string>)[String(draft.land_status)] ?? 'Informação em falta'), 'Informação declarada; não comprova título ou disponibilidade']);
else rows.push(['Localização', 'Preparação do terreno', land.need === 'no' ? 'Situação do terreno não aplicável à preparação actual; valores anteriores conservados.' : 'Definir se precisa de terra; pode continuar a preparar o investimento.', 'Sem inferência de dispensa legal']);
}
return rows;
}
+2 -1
View File
@@ -1,4 +1,5 @@
import { inheritSimulatorFields } from './context-inheritance.ts';
import { landStatusVisible } from './investment-land-preparation.ts';
import { csvCell } from './csv.js';
import { fiscalQaRows } from './fiscal-qa-preview.ts';
import { isApiSimulationResultSnapshotV1 } from './simulation-contract.ts';
@@ -738,7 +739,7 @@ export const simulationFieldGroups: Record<
label: 'Situação do terreno',
options: 'land_status',
showWhen: (d) =>
d.purpose !== 'simulate_only' || isInvestorProjectJourney(d),
landStatusVisible(d) && (d.purpose !== 'simulate_only' || isInvestorProjectJourney(d)),
},
{
key: 'area_required',