417 lines
17 KiB
TypeScript
417 lines
17 KiB
TypeScript
import { useState } from 'react';
|
|
import {
|
|
refundBook,
|
|
refundSources,
|
|
newRefund,
|
|
saveRefundBook,
|
|
recordRefund,
|
|
refundReportRows,
|
|
type RefundBook,
|
|
type RefundInput,
|
|
} from '@/lib/iva-refund';
|
|
export function AcquisitionRefund({
|
|
draft,
|
|
onChange,
|
|
}: {
|
|
draft: Record<string, unknown>;
|
|
onChange: (field: string, value: string) => void;
|
|
}) {
|
|
const [error, setError] = useState('');
|
|
let b: RefundBook;
|
|
try {
|
|
b = refundBook(draft);
|
|
} catch (e) {
|
|
return <p role="alert">{String(e)}</p>;
|
|
}
|
|
const save = (book: RefundBook) => {
|
|
try {
|
|
onChange('modelo_a_snapshot', saveRefundBook(draft, book));
|
|
setError('');
|
|
} catch (e) {
|
|
setError(String(e));
|
|
}
|
|
};
|
|
const change = (id: string, p: Partial<RefundInput>) =>
|
|
save({
|
|
...b,
|
|
rows: b.rows.map((r) => (r.id === id ? { ...r, ...p } : r)),
|
|
}),
|
|
sources = refundSources(draft);
|
|
const choice = (
|
|
r: RefundInput,
|
|
k: 'factsConfirmed' | 'oldestFirst',
|
|
label: string,
|
|
) => (
|
|
<label>
|
|
{label}
|
|
<select
|
|
value={r[k]}
|
|
onChange={(e) => change(r.id, { [k]: e.target.value })}
|
|
>
|
|
<option value="">Por confirmar</option>
|
|
<option value="yes">
|
|
Sim — documentado ou pressuposto explícito
|
|
</option>
|
|
<option value="no">Não</option>
|
|
</select>
|
|
</label>
|
|
);
|
|
return (
|
|
<details className="rounded border p-3">
|
|
<summary>L3-D — pedido,decisão e recebimento do reembolso</summary>
|
|
<p>
|
|
Primeiro,no fecho do período,escolha a parcela destinada ao
|
|
pedido27 e calcule a afectação:25=26+27. Apenas26 continua em
|
|
reporte. Reutilize a afectação abaixo;crédito já transportado
|
|
não é uma nova parcela disponível.
|
|
</p>
|
|
<p>
|
|
Preparar ou apresentar o pedido não é deferimento;deferimento
|
|
não é recebimento. Submissão,formulários
|
|
regulamentares,suspensões e pagamentos bancários ocorrem fora
|
|
doInvesteMoz. O prazo legal não é previsão de caixa. Não se
|
|
aplica a antiga obrigação50%/12meses,revogada em2026.
|
|
</p>
|
|
{error && <p role="alert">{error}</p>}
|
|
<button
|
|
type="button"
|
|
onClick={() =>
|
|
save({
|
|
...b,
|
|
rows: [
|
|
...b.rows,
|
|
newRefund(
|
|
crypto.randomUUID(),
|
|
sources.length === 1 ? sources[0] : null,
|
|
),
|
|
],
|
|
})
|
|
}
|
|
>
|
|
Preparar pedido de reembolso geral
|
|
</button>
|
|
{b.rows.map((r) => (
|
|
<fieldset className="rounded border p-3" key={r.id}>
|
|
<legend>
|
|
Pedido de reembolso —{' '}
|
|
{r.source?.period || 'período por escolher'}
|
|
</legend>
|
|
<label>
|
|
Afectação congelada do crédito27
|
|
<select
|
|
value={sources.findIndex(
|
|
(s) =>
|
|
s.allocationKey === r.source?.allocationKey,
|
|
)}
|
|
onChange={(e) => {
|
|
const s = sources[Number(e.target.value)];
|
|
if (s)
|
|
change(r.id, {
|
|
source: structuredClone(s),
|
|
});
|
|
}}
|
|
>
|
|
<option value={-1}>Escolha a parcela actual</option>
|
|
{sources.map((s, i) => (
|
|
<option key={s.allocationKey} value={i}>
|
|
{s.period} — {s.entityRef} — fecho
|
|
{s.closureId}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</label>
|
|
<label>
|
|
Fundamento do pedido
|
|
<select
|
|
value={r.ground}
|
|
onChange={(e) =>
|
|
change(r.id, {
|
|
ground: e.target
|
|
.value as RefundInput['ground'],
|
|
})
|
|
}
|
|
>
|
|
<option value="">Por identificar</option>
|
|
<option value="four_months">
|
|
21(6) — quatro meses e crédito superior100000MZN
|
|
</option>
|
|
<option value="high_credit">
|
|
21(7a) — crédito superior500000MZN
|
|
</option>
|
|
<option value="cessation">
|
|
21(7b) — cessação de actividade
|
|
</option>
|
|
</select>
|
|
</label>
|
|
<label>
|
|
Período de início do excesso de crédito
|
|
<input
|
|
type="month"
|
|
value={r.originPeriod}
|
|
onChange={(e) =>
|
|
change(r.id, { originPeriod: e.target.value })
|
|
}
|
|
/>
|
|
</label>
|
|
<label>
|
|
Data de nascimento do direito à dedução
|
|
<input
|
|
type="date"
|
|
value={r.entitlementDate}
|
|
onChange={(e) =>
|
|
change(r.id, {
|
|
entitlementDate: e.target.value,
|
|
})
|
|
}
|
|
/>
|
|
</label>
|
|
{r.ground === 'cessation' && (
|
|
<label>
|
|
Data da cessação de actividade
|
|
<input
|
|
type="date"
|
|
value={r.cessationDate}
|
|
onChange={(e) =>
|
|
change(r.id, {
|
|
cessationDate: e.target.value,
|
|
})
|
|
}
|
|
/>
|
|
</label>
|
|
)}
|
|
<label>
|
|
Suporte dos factos e origem do crédito
|
|
<textarea
|
|
value={r.groundRef}
|
|
onChange={(e) =>
|
|
change(r.id, { groundRef: e.target.value })
|
|
}
|
|
/>
|
|
</label>
|
|
{choice(
|
|
r,
|
|
'factsConfirmed',
|
|
'Factos do fundamento identificados no suporte',
|
|
)}
|
|
{choice(
|
|
r,
|
|
'oldestFirst',
|
|
'Considerei sequencialmente os créditos mais antigos',
|
|
)}
|
|
<label>
|
|
Referência do pedido ou preparação
|
|
<input
|
|
value={r.requestReference}
|
|
onChange={(e) =>
|
|
change(r.id, {
|
|
requestReference: e.target.value,
|
|
})
|
|
}
|
|
/>
|
|
</label>
|
|
<label>
|
|
Data do pedido ou prevista
|
|
<input
|
|
type="date"
|
|
value={r.requestDate}
|
|
onChange={(e) =>
|
|
change(r.id, { requestDate: e.target.value })
|
|
}
|
|
/>
|
|
</label>
|
|
<label>
|
|
Situação do pedido
|
|
<select
|
|
value={r.stage}
|
|
onChange={(e) =>
|
|
change(r.id, {
|
|
stage: e.target
|
|
.value as RefundInput['stage'],
|
|
})
|
|
}
|
|
>
|
|
<option value="preparation">
|
|
Em preparação — sem submissão
|
|
</option>
|
|
<option value="submitted">
|
|
Apresentado fora doInvesteMoz — registo
|
|
declarado
|
|
</option>
|
|
</select>
|
|
</label>
|
|
<label>
|
|
Decisão recebida
|
|
<select
|
|
value={r.decision}
|
|
onChange={(e) =>
|
|
change(r.id, {
|
|
decision: e.target
|
|
.value as RefundInput['decision'],
|
|
})
|
|
}
|
|
>
|
|
<option value="pending">
|
|
Ainda não identificada
|
|
</option>
|
|
<option value="approved">
|
|
Deferimento integral
|
|
</option>
|
|
<option value="partial">Deferimento parcial</option>
|
|
<option value="rejected">Indeferimento</option>
|
|
</select>
|
|
</label>
|
|
{r.decision !== 'pending' && (
|
|
<>
|
|
<label>
|
|
Número ou referência da decisão
|
|
<input
|
|
value={r.decisionReference}
|
|
onChange={(e) =>
|
|
change(r.id, {
|
|
decisionReference: e.target.value,
|
|
})
|
|
}
|
|
/>
|
|
</label>
|
|
<label>
|
|
Data da decisão
|
|
<input
|
|
type="date"
|
|
value={r.decisionDate}
|
|
onChange={(e) =>
|
|
change(r.id, {
|
|
decisionDate: e.target.value,
|
|
})
|
|
}
|
|
/>
|
|
</label>
|
|
<label>
|
|
Entidade emitente da decisão
|
|
<input
|
|
value={r.decisionIssuer}
|
|
onChange={(e) =>
|
|
change(r.id, {
|
|
decisionIssuer: e.target.value,
|
|
})
|
|
}
|
|
/>
|
|
</label>
|
|
<label>
|
|
Montante reconhecido na decisão (MZN)
|
|
<input
|
|
value={r.approvedAmount}
|
|
onChange={(e) =>
|
|
change(r.id, {
|
|
approvedAmount: e.target.value,
|
|
})
|
|
}
|
|
/>
|
|
</label>
|
|
</>
|
|
)}
|
|
<h5>Recebimentos registados</h5>
|
|
{r.receipts.map((p) => (
|
|
<fieldset key={p.id}>
|
|
<legend>Recebimento de reembolso</legend>
|
|
{(['reference', 'date', 'amount'] as const).map(
|
|
(k) => (
|
|
<label key={k}>
|
|
{k === 'reference'
|
|
? 'Referência do recebimento'
|
|
: k === 'date'
|
|
? 'Data do recebimento'
|
|
: 'Montante recebido (MZN)'}
|
|
<input
|
|
type={
|
|
k === 'date' ? 'date' : 'text'
|
|
}
|
|
value={p[k]}
|
|
onChange={(e) =>
|
|
change(r.id, {
|
|
receipts: r.receipts.map(
|
|
(v) =>
|
|
v.id === p.id
|
|
? {
|
|
...v,
|
|
[k]: e
|
|
.target
|
|
.value,
|
|
}
|
|
: v,
|
|
),
|
|
})
|
|
}
|
|
/>
|
|
</label>
|
|
),
|
|
)}
|
|
<button
|
|
type="button"
|
|
onClick={() =>
|
|
change(r.id, {
|
|
receipts: r.receipts.filter(
|
|
(v) => v.id !== p.id,
|
|
),
|
|
})
|
|
}
|
|
>
|
|
Remover recebimento desta preparação
|
|
</button>
|
|
</fieldset>
|
|
))}
|
|
<button
|
|
type="button"
|
|
onClick={() =>
|
|
change(r.id, {
|
|
receipts: [
|
|
...r.receipts,
|
|
{
|
|
id: crypto.randomUUID(),
|
|
reference: '',
|
|
date: '',
|
|
amount: '',
|
|
},
|
|
],
|
|
})
|
|
}
|
|
>
|
|
Registar recebimento identificado
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={() =>
|
|
save({
|
|
...b,
|
|
rows: b.rows.filter((v) => v.id !== r.id),
|
|
})
|
|
}
|
|
>
|
|
Remover pedido desta preparação
|
|
</button>
|
|
</fieldset>
|
|
))}
|
|
{b.rows.length > 0 && (
|
|
<button
|
|
type="button"
|
|
onClick={() => {
|
|
try {
|
|
save(recordRefund(draft, b));
|
|
} catch (e) {
|
|
setError(String(e));
|
|
}
|
|
}}
|
|
>
|
|
Calcular e guardar acompanhamento D
|
|
</button>
|
|
)}
|
|
{refundReportRows(draft).map((r, i) => (
|
|
<p key={i}>
|
|
<strong>{r[1]}:</strong> {r[2]}
|
|
<br />
|
|
{r[3]}
|
|
</p>
|
|
))}
|
|
</details>
|
|
);
|
|
}
|