339 lines
16 KiB
TypeScript
339 lines
16 KiB
TypeScript
import { useState } from 'react';
|
|
import {
|
|
foreignOperationBook,
|
|
foreignOperationCurrent,
|
|
foreignOperationReportRows,
|
|
foreignOperationSource,
|
|
foreignSources,
|
|
foreignEvents,
|
|
newForeignOperation,
|
|
recordForeignOperations,
|
|
saveForeignOperationBook,
|
|
switchForeignSource,
|
|
type ForeignOperationBook,
|
|
type ForeignOperationRow,
|
|
} from '@/lib/iva-foreign-operations';
|
|
export function SimulationForeignOperations({
|
|
draft,
|
|
onChange,
|
|
}: {
|
|
draft: Record<string, unknown>;
|
|
onChange: (field: string, value: string) => void;
|
|
}) {
|
|
const [error, setError] = useState('');
|
|
let book: ForeignOperationBook;
|
|
try {
|
|
book = foreignOperationBook(draft);
|
|
} catch (e) {
|
|
return <p role="alert">{String(e)}</p>;
|
|
}
|
|
const save = (next: ForeignOperationBook) => {
|
|
try {
|
|
onChange(
|
|
'modelo_a_snapshot',
|
|
saveForeignOperationBook(draft, next),
|
|
);
|
|
setError('');
|
|
} catch (e) {
|
|
setError(String(e));
|
|
}
|
|
};
|
|
const change = (id: string, patch: Partial<ForeignOperationRow>) =>
|
|
save({
|
|
...book,
|
|
rows: book.rows.map((r) => (r.id === id ? { ...r, ...patch } : r)),
|
|
});
|
|
return (
|
|
<details className="rounded border p-3">
|
|
<summary>
|
|
Operações no estrangeiro — localização e enquadramento
|
|
</summary>
|
|
<p>
|
|
Reutilize uma localização já guardada nesta oportunidade. Fora
|
|
do território não significa isenção,IVA zero ou dedução
|
|
concedida. Não é necessário repetir os factos/documentos da
|
|
localização.
|
|
</p>
|
|
<p>{foreignOperationSource}</p>
|
|
{error && <p role="alert">{error}</p>}
|
|
<button
|
|
type="button"
|
|
onClick={() =>
|
|
save({
|
|
...book,
|
|
rows: [
|
|
...book.rows,
|
|
newForeignOperation(crypto.randomUUID()),
|
|
],
|
|
})
|
|
}
|
|
>
|
|
Acrescentar análise de operação no estrangeiro
|
|
</button>
|
|
{book.rows.map((row) => {
|
|
let sources: ReturnType<typeof foreignSources> = [],
|
|
events: ReturnType<typeof foreignEvents> = [];
|
|
try {
|
|
sources = foreignSources(draft, row.sourceKind);
|
|
events = foreignEvents(draft);
|
|
} catch {
|
|
/* Existing readers retain corrupt originals. */
|
|
}
|
|
const source = sources.find((s) => s.id === row.sourceRowId);
|
|
return (
|
|
<details key={row.id}>
|
|
<summary>
|
|
{source?.description || 'Operação por associar'} —{' '}
|
|
{source?.period || 'período da origem'}
|
|
</summary>
|
|
<label>
|
|
Origem da localização
|
|
<select
|
|
value={row.sourceKind}
|
|
onChange={(e) =>
|
|
change(
|
|
row.id,
|
|
switchForeignSource(
|
|
row,
|
|
e.target
|
|
.value as ForeignOperationRow['sourceKind'],
|
|
),
|
|
)
|
|
}
|
|
>
|
|
<option value="">Por indicar</option>
|
|
<option value="goods">Bens corpóreos</option>
|
|
<option value="services">
|
|
Serviços e transporte
|
|
</option>
|
|
<option value="listed">
|
|
Serviços enumerados não digitais
|
|
</option>
|
|
<option value="digital">
|
|
Bens e serviços digitais
|
|
</option>
|
|
</select>
|
|
</label>
|
|
<label>
|
|
Resultado de localização existente
|
|
<select
|
|
value={row.sourceRowId}
|
|
onChange={(e) =>
|
|
change(
|
|
row.id,
|
|
switchForeignSource(
|
|
row,
|
|
row.sourceKind,
|
|
e.target.value,
|
|
),
|
|
)
|
|
}
|
|
>
|
|
<option value="">Escolher operação</option>
|
|
{sources.map((s) => (
|
|
<option key={s.id} value={s.id}>
|
|
{s.description} — {s.period} —{' '}
|
|
{s.territory ?? 'Indeterminado'}
|
|
{s.current ? '' : ' — histórico'}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</label>
|
|
{row.sourceKind && !sources.length && (
|
|
<p>
|
|
Prepare a localização nas secções de
|
|
bens/serviços existentes desta oportunidade.
|
|
Nenhuma localização é presumida ou copiada de
|
|
outro projecto.
|
|
</p>
|
|
)}
|
|
{source && (
|
|
<p>
|
|
Operação: {source.operationRef}. Âmbito:{' '}
|
|
{source.scope}. {source.reason}
|
|
</p>
|
|
)}
|
|
{source?.territory === 'partial' && (
|
|
<label>
|
|
Parcela deste trajecto analisada
|
|
<select
|
|
value={row.territorialPart}
|
|
onChange={(e) =>
|
|
change(row.id, {
|
|
territorialPart: e.target
|
|
.value as ForeignOperationRow['territorialPart'],
|
|
})
|
|
}
|
|
>
|
|
<option value="">Delimitar parcela</option>
|
|
<option value="outside">
|
|
Parcela fora do território nacional
|
|
</option>
|
|
<option value="national">
|
|
Parcela nacional
|
|
</option>
|
|
</select>
|
|
</label>
|
|
)}
|
|
{source && source.territory !== 'national' && (
|
|
<>
|
|
<label>
|
|
Evento já guardado da mesma operação,quando
|
|
aplicável
|
|
<select
|
|
value={row.eventRowId}
|
|
onChange={(e) =>
|
|
change(row.id, {
|
|
eventRowId: e.target.value,
|
|
})
|
|
}
|
|
>
|
|
<option value="">
|
|
Sem associação necessária nesta
|
|
análise territorial
|
|
</option>
|
|
{events
|
|
.filter(
|
|
(e) =>
|
|
e.operationRef ===
|
|
source.operationRef &&
|
|
e.period === source.period,
|
|
)
|
|
.map((e) => (
|
|
<option key={e.id} value={e.id}>
|
|
{e.description} — {e.period}
|
|
{e.current
|
|
? ''
|
|
: ' — histórico'}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</label>
|
|
<label>
|
|
Seria tributável se efectuada no território
|
|
nacional?
|
|
<select
|
|
value={row.hypotheticalTaxable}
|
|
onChange={(e) =>
|
|
change(row.id, {
|
|
hypotheticalTaxable: e.target
|
|
.value as ForeignOperationRow['hypotheticalTaxable'],
|
|
})
|
|
}
|
|
>
|
|
<option value="">Por esclarecer</option>
|
|
<option value="yes">
|
|
Sim,segundo o fundamento
|
|
identificado
|
|
</option>
|
|
<option value="no">
|
|
Não,segundo o fundamento
|
|
identificado
|
|
</option>
|
|
</select>
|
|
</label>
|
|
<label>
|
|
Fundamento da hipótese interna e exclusões
|
|
aplicáveis
|
|
<input
|
|
value={row.hypotheticalRef}
|
|
onChange={(e) =>
|
|
change(row.id, {
|
|
hypotheticalRef: e.target.value,
|
|
})
|
|
}
|
|
/>
|
|
</label>
|
|
{row.hypotheticalTaxable === 'yes' && (
|
|
<label>
|
|
Existe exclusão19(2) aplicável:17-A ou
|
|
pagamentos15(6)c?
|
|
<select
|
|
value={row.excluded19_2}
|
|
onChange={(e) =>
|
|
change(row.id, {
|
|
excluded19_2: e.target
|
|
.value as ForeignOperationRow['excluded19_2'],
|
|
})
|
|
}
|
|
>
|
|
<option value="">
|
|
Por esclarecer
|
|
</option>
|
|
<option value="yes">Sim</option>
|
|
<option value="no">Não</option>
|
|
</select>
|
|
</label>
|
|
)}
|
|
</>
|
|
)}
|
|
<label>
|
|
Âmbito da análise,conjunto/parcela e período
|
|
<input
|
|
value={row.scope}
|
|
onChange={(e) =>
|
|
change(row.id, { scope: e.target.value })
|
|
}
|
|
/>
|
|
</label>
|
|
<label>
|
|
Factos ou pressupostos expressos
|
|
<input
|
|
value={row.evidence}
|
|
onChange={(e) =>
|
|
change(row.id, { evidence: e.target.value })
|
|
}
|
|
/>
|
|
</label>
|
|
<p>
|
|
Trocar origem conserva fundamentos ocultos;reveja as
|
|
confirmações. Nenhuma dedução é lançada por esta
|
|
análise.
|
|
</p>
|
|
</details>
|
|
);
|
|
})}
|
|
{!!book.rows.length && (
|
|
<button
|
|
type="button"
|
|
onClick={() => {
|
|
try {
|
|
save(recordForeignOperations(draft, book));
|
|
} catch (e) {
|
|
setError(String(e));
|
|
}
|
|
}}
|
|
>
|
|
Analisar operação estrangeira e guardar versão
|
|
</button>
|
|
)}
|
|
{book.result && (
|
|
<>
|
|
<p role="status">
|
|
{foreignOperationCurrent(draft, book)
|
|
? 'Análise corresponde às entradas e origens guardadas.'
|
|
: 'Entradas ou origens alteradas;resultado histórico conservado. Reveja apenas o delta afectado.'}{' '}
|
|
{book.history?.length ?? 0} versões anteriores.Sem
|
|
dedução/IVA calculados.
|
|
</p>
|
|
<dl>
|
|
{foreignOperationReportRows(draft).map(
|
|
([, label, value, origin], i) => (
|
|
<div key={i}>
|
|
<dt>{label}</dt>
|
|
<dd>
|
|
{value}
|
|
<small className="block">
|
|
{origin}
|
|
</small>
|
|
</dd>
|
|
</div>
|
|
),
|
|
)}
|
|
</dl>
|
|
</>
|
|
)}
|
|
</details>
|
|
);
|
|
}
|