Files
Investidor-V2/resources/js/components/acquisition-substitution.tsx
T

387 lines
17 KiB
TypeScript

import { useState } from 'react';
import { closureSources } from '@/lib/iva-closure';
import {
substitutionBook,
saveSubstitutionBook,
newSubstitution,
previousDeclarations,
recordSubstitution,
substitutionReportRows,
type SubstitutionBook,
type SubstitutionInput,
} from '@/lib/iva-substitution';
export function AcquisitionSubstitution({
draft,
onChange,
}: {
draft: Record<string, unknown>;
onChange: (field: string, value: string) => void;
}) {
const [error, setError] = useState('');
let b: SubstitutionBook;
try {
b = substitutionBook(draft);
} catch (e) {
return <p role="alert">{String(e)}</p>;
}
const save = (next: SubstitutionBook) => {
try {
onChange('modelo_a_snapshot', saveSubstitutionBook(draft, next));
setError('');
} catch (e) {
setError(String(e));
}
};
const change = (id: string, p: Partial<SubstitutionInput>) =>
save({
...b,
rows: b.rows.map((r) => (r.id === id ? { ...r, ...p } : r)),
});
const previous = previousDeclarations(draft, b),
sources = closureSources(draft);
return (
<details className="rounded border p-3">
<summary>L3-C — substituições e diferencial</summary>
<p>
Escolha a declaração anterior e o resultado corrigido do mesmo
período. Créditos utilizados e pagamentos anteriores
conservam-se no histórico;registe apenas pagamentos adicionais.
</p>
<p>
O diferencial credor aguarda comunicação dos serviços para
utilização posterior no campo20. Não compensa automaticamente
dívida nem cria reporte26. A preparação não submete à
AT;formulário de substituição e formalidades permanecem fora do
InvesteMoz.
</p>
{error && <p role="alert">{error}</p>}
<button
type="button"
onClick={() =>
save({
...b,
rows: [...b.rows, newSubstitution(crypto.randomUUID())],
})
}
>
Preparar substituição
</button>
{b.rows.map((r) => (
<fieldset className="rounded border p-3" key={r.id}>
<legend>
Substituição —{' '}
{r.prior?.period || 'período por escolher'}
</legend>
<label>
Declaração anterior congelada
<select
value={previous.findIndex(
(p) =>
JSON.stringify(p) ===
JSON.stringify(r.prior),
)}
onChange={(e) => {
const p = previous[Number(e.target.value)];
if (p) {
const matches = sources.filter(
(s) => s.period === p.period,
);
change(r.id, {
prior: structuredClone(p),
...(r.priorDate
? {}
: { priorDate: p.declarationDate }),
...(r.priorReference
? {}
: {
priorReference:
p.declarationReference,
}),
...(r.source === null &&
matches.length === 1
? {
source: structuredClone(
matches[0],
),
}
: {}),
});
}
}}
>
<option value={-1}>
Escolha a versão anterior
</option>
{previous
.filter((p) => p.id !== r.id)
.map((p) => (
<option
key={JSON.stringify(p)}
value={previous.indexOf(p)}
>
{p.period} — {p.entityRef} — {p.kind}/
{p.id} — {p.calculatedAt}
</option>
))}
</select>
</label>
{r.prior && (
<p>
Anterior:{r.prior.entityRef};
{r.prior.origin === 'projected'
? 'pressuposto de simulação'
: 'dados efectivos declarados'}
;pagamentos e reporte conservados na versão
escolhida.
</p>
)}
<label>
Resultado L2 corrigido congelado
<select
value={sources.findIndex(
(s) =>
JSON.stringify(s) ===
JSON.stringify(r.source),
)}
onChange={(e) => {
const s = sources[Number(e.target.value)];
if (s)
change(r.id, {
source: structuredClone(s),
});
}}
>
<option value={-1}>
Escolha a versão corrigida
</option>
{sources
.filter(
(s) =>
!r.prior || s.period === r.prior.period,
)
.map((s) => (
<option
key={JSON.stringify(s)}
value={sources.indexOf(s)}
>
{s.period} — revisão{s.revision} —{' '}
{s.calculatedAt}
</option>
))}
</select>
</label>
<label>
Referência da declaração anterior
<input
value={r.priorReference}
onChange={(e) =>
change(r.id, { priorReference: e.target.value })
}
/>
</label>
<label>
Data da declaração anterior
<input
type="date"
value={r.priorDate}
onChange={(e) =>
change(r.id, { priorDate: e.target.value })
}
/>
</label>
<label>
Referência da substituição ou preparação
<input
value={r.reference}
onChange={(e) =>
change(r.id, { reference: e.target.value })
}
/>
</label>
<label>
Data da substituição ou prevista
<input
type="date"
value={r.date}
onChange={(e) =>
change(r.id, { date: e.target.value })
}
/>
</label>
<label>
Motivo da substituição
<textarea
value={r.reason}
onChange={(e) =>
change(r.id, { reason: e.target.value })
}
/>
</label>
<label>
Situação da substituição
<select
value={r.stage}
onChange={(e) =>
change(r.id, {
stage: e.target
.value as SubstitutionInput['stage'],
})
}
>
<option value="preparation">
Em preparação — sem submissão
</option>
<option value="presented">
Apresentada fora do InvesteMoz — registo
declarado
</option>
</select>
</label>
<label>
Juros adicionais23
<select
value={r.interest}
onChange={(e) =>
change(r.id, {
interest: e.target
.value as SubstitutionInput['interest'],
})
}
>
<option value="">Por identificar</option>
<option value="none">
Sem juros adicionais — motivo identificado
</option>
<option value="documented">
Montante adicional identificado
</option>
</select>
</label>
{r.interest === 'documented' && (
<label>
Montante dos juros adicionais (MZN)
<input
value={r.interestAmount}
onChange={(e) =>
change(r.id, {
interestAmount: e.target.value,
})
}
/>
</label>
)}
<label>
Fonte ou motivo dos juros adicionais
<input
value={r.interestRef}
onChange={(e) =>
change(r.id, { interestRef: e.target.value })
}
/>
</label>
<h5>Pagamentos adicionais desta substituição</h5>
{r.payments.map((p) => (
<fieldset key={p.id}>
<legend>Pagamento adicional</legend>
{(['reference', 'date', 'amount'] as const).map(
(k) => (
<label key={k}>
{k === 'reference'
? 'Referência do pagamento adicional'
: k === 'date'
? 'Data do pagamento adicional'
: 'Montante adicional pago (MZN)'}
<input
type={
k === 'date' ? 'date' : 'text'
}
value={p[k]}
onChange={(e) =>
change(r.id, {
payments: r.payments.map(
(v) =>
v.id === p.id
? {
...v,
[k]: e
.target
.value,
}
: v,
),
})
}
/>
</label>
),
)}
<button
type="button"
onClick={() =>
change(r.id, {
payments: r.payments.filter(
(v) => v.id !== p.id,
),
})
}
>
Remover pagamento desta preparação
</button>
</fieldset>
))}
<button
type="button"
onClick={() =>
change(r.id, {
payments: [
...r.payments,
{
id: crypto.randomUUID(),
reference: '',
date: '',
amount: '',
},
],
})
}
>
Registar pagamento adicional
</button>
<button
type="button"
onClick={() =>
save({
...b,
rows: b.rows.filter((v) => v.id !== r.id),
})
}
>
Remover substituição desta preparação
</button>
</fieldset>
))}
{b.rows.length > 0 && (
<button
type="button"
onClick={() => {
try {
save(recordSubstitution(draft, b));
} catch (e) {
setError(String(e));
}
}}
>
Calcular e guardar substituições C
</button>
)}
{substitutionReportRows(draft).map((r, i) => (
<p key={i}>
<strong>{r[1]}:</strong> {r[2]}
<br />
{r[3]}
</p>
))}
</details>
);
}