Files
Investidor-V2/resources/js/components/simulation-public-authority-base.tsx
T

343 lines
15 KiB
TypeScript

import { inputText } from '../lib/input-text.ts';
import { useState } from 'react';
import {
newPublicBase,
publicBaseBook,
publicBaseConditions,
publicBaseCurrent,
publicBaseReportRows,
publicBaseSource,
publicBaseTreatments,
recordPublicBases,
savePublicBaseBook,
type PublicBaseBook,
type PublicBaseRow,
} from '@/lib/iva-public-authority-base';
export function SimulationPublicAuthorityBase({
draft,
onChange,
}: {
draft: Record<string, unknown>;
onChange: (field: string, value: string) => void;
}) {
const [error, setError] = useState('');
let book: PublicBaseBook;
try {
book = publicBaseBook(draft);
} catch (cause) {
return <p role="alert">{String(cause)}</p>;
}
const save = (next: PublicBaseBook) => {
try {
onChange('modelo_a_snapshot', savePublicBaseBook(draft, next));
setError('');
} catch (cause) {
setError(String(cause));
}
};
const change = (id: string, patch: Partial<PublicBaseRow>) =>
save({
...book,
rows: book.rows.map((row) =>
row.id === id ? { ...row, ...patch } : row,
),
});
return (
<details className="space-y-3 rounded border p-3">
<summary>Base tributável — actos de autoridades públicas</summary>
<p>
Oportunidade:{' '}
{inputText(draft.context_opportunity || 'entrada geral')}.
Prepare apenas as operações relevantes. A base do artigo15(2)(d)
é a indemnização ou outra compensação ligada à transmissão de
bens ou prestação de serviços resultante do acto público. Um
pagamento ou a natureza pública da entidade não determinam
incidência nem taxa de IVA.
</p>
<p>
A indemnização judicial por incumprimento de contrato tem
exclusão própria no artigo15(6)(a); não é incluída por analogia.
Indique as componentes sem IVA e o fundamento da composição. Na
compensação em espécie ou mista, identifique o valor normal
conforme15(3)/(4). Valores em falta permanecem indeterminados;
não se convertem em zero.
</p>
<p>{publicBaseSource}</p>
{error && <p role="alert">{error}</p>}
<button
type="button"
className="underline"
onClick={() =>
save({
...book,
rows: [
...book.rows,
newPublicBase(
crypto.randomUUID(),
inputText(draft.currency ?? ''),
),
],
})
}
>
Acrescentar operação de acto público
</button>
{book.rows.map((row) => (
<details key={row.id} className="rounded border p-3">
<summary>
{row.description || 'Operação por identificar'} —{' '}
{row.period || 'mês por indicar'}
</summary>
<div className="grid gap-3 md:grid-cols-2">
<label>
Tratamento da compensação
<select
className="block rounded border p-2"
value={row.treatment}
onChange={(e) =>
change(row.id, {
treatment: e.target.value,
})
}
>
<option value="">Por indicar</option>
{publicBaseTreatments.map((item) => (
<option key={item.id} value={item.id}>
{item.label}
</option>
))}
</select>
</label>
<label>
Natureza da operação
<select
className="block rounded border p-2"
value={row.operationNature}
onChange={(e) =>
change(row.id, {
operationNature: e.target
.value as PublicBaseRow['operationNature'],
})
}
>
<option value="">Por indicar</option>
<option value="goods">
Transmissão de bens
</option>
<option value="services">
Prestação de serviços
</option>
</select>
</label>
<label>
Natureza dos dados
<select
className="block rounded border p-2"
value={row.origin}
onChange={(e) =>
change(row.id, {
origin: e.target
.value as PublicBaseRow['origin'],
})
}
>
<option value="projected">
Projectados / pressupostos
</option>
<option value="effective">
Efectivos declarados
</option>
</select>
</label>
{(
[
['description', 'Operação identificada'],
['authority', 'Autoridade pública'],
[
'actRef',
'Acto da autoridade / referência da previsão',
],
[
'documentRef',
'Documento da compensação / referência da previsão',
],
[
'documentDate',
'Data do documento / prevista',
],
['period', 'Mês da operação'],
['currency', 'Moeda de origem'],
[
'compositionRef',
'Referência da composição sem IVA — inclusões e exclusões aplicáveis',
],
[
'evidence',
'Fundamento factual / pressupostos',
],
] as const
).map(([field, label]) => (
<label key={field}>
{label}
<input
className="block rounded border p-2"
type={
field === 'documentDate'
? 'date'
: field === 'period'
? 'month'
: 'text'
}
value={row[field]}
onChange={(e) =>
change(row.id, {
[field]: e.target.value,
})
}
/>
</label>
))}
<label>
Forma da compensação
<select
className="block rounded border p-2"
value={row.compensationForm}
onChange={(e) =>
change(row.id, {
compensationForm: e.target
.value as PublicBaseRow['compensationForm'],
})
}
>
<option value="">Por indicar</option>
<option value="money">Em dinheiro</option>
<option value="non_money">Em espécie</option>
<option value="mixed">
Mista — dinheiro e espécie
</option>
</select>
</label>
{['money', 'mixed'].includes(row.compensationForm) && (
<label>
Componente monetária sem IVA (moeda indicada)
<input
className="block rounded border p-2"
value={row.moneyAmount}
onChange={(e) =>
change(row.id, {
moneyAmount: e.target.value,
})
}
/>
</label>
)}
{['non_money', 'mixed'].includes(
row.compensationForm,
) && (
<>
<label>
Valor normal não monetário sem IVA (moeda
indicada)
<input
className="block rounded border p-2"
value={row.nonMoneyValue}
onChange={(e) =>
change(row.id, {
nonMoneyValue: e.target.value,
})
}
/>
</label>
<label>
Referência da valorização / pressuposto do
valor normal
<input
className="block rounded border p-2"
value={row.valuationRef}
onChange={(e) =>
change(row.id, {
valuationRef: e.target.value,
})
}
/>
</label>
</>
)}
</div>
<p>
Ao mudar de tratamento ou forma, os valores e
referências anteriores conservam-se; só entram no
cálculo as componentes aplicáveis à forma escolhida.
</p>
<fieldset>
<legend>Condições próprias desta base</legend>
{publicBaseConditions.map(([key, label]) => (
<label className="block" key={key}>
{label}
<select
className="block rounded border p-2"
value={row.conditions[key] ?? ''}
onChange={(e) =>
change(row.id, {
conditions: {
...row.conditions,
[key]: e.target.value,
},
})
}
>
<option value="">Por indicar</option>
<option value="yes">Sim</option>
<option value="no">Não</option>
</select>
</label>
))}
</fieldset>
</details>
))}
{!!book.rows.length && (
<button
type="button"
className="rounded bg-emerald-800 p-2 text-white"
onClick={() => {
try {
save(recordPublicBases(draft, book));
} catch (cause) {
setError(String(cause));
}
}}
>
Determinar bases e guardar versão
</button>
)}
{book.result && (
<>
<p role="status">
{publicBaseCurrent(draft, book)
? 'Bases correspondentes às entradas guardadas.'
: 'Entradas alteradas; bases históricas conservadas. Reveja e determine novamente.'}{' '}
{book.history?.length ?? 0} versões anteriores. IVA e
campo11 não são preenchidos por estas bases.
</p>
<dl>
{publicBaseReportRows(draft).map(
([, label, value, origin], index) => (
<div key={index}>
<dt className="font-semibold">{label}</dt>
<dd>
{value}
<small className="block">
{origin}
</small>
</dd>
</div>
),
)}
</dl>
</>
)}
</details>
);
}