340 lines
15 KiB
TypeScript
340 lines
15 KiB
TypeScript
import { useState } from 'react';
|
||
import {
|
||
newRegularization,
|
||
regularizationBook,
|
||
regularizationSources,
|
||
saveRegularizationBook,
|
||
recordRegularizations,
|
||
regularizationReportRows,
|
||
frozenAssetRows,
|
||
type RegularizationEvent,
|
||
type RegularizationBook,
|
||
} from '@/lib/iva-regularizations';
|
||
export function AcquisitionRegularizations({
|
||
draft,
|
||
onChange,
|
||
}: {
|
||
draft: Record<string, unknown>;
|
||
onChange: (field: string, value: string) => void;
|
||
}) {
|
||
const [error, setError] = useState('');
|
||
let b: RegularizationBook;
|
||
try {
|
||
b = regularizationBook(draft);
|
||
} catch (cause) {
|
||
return <p role="alert">{String(cause)}</p>;
|
||
}
|
||
const save = (next: RegularizationBook) => {
|
||
try {
|
||
onChange('modelo_a_snapshot', saveRegularizationBook(draft, next));
|
||
setError('');
|
||
} catch (cause) {
|
||
setError(String(cause));
|
||
}
|
||
};
|
||
const change = (id: string, patch: Partial<RegularizationEvent>) =>
|
||
save({
|
||
...b,
|
||
events: b.events.map((e) => (e.id === id ? { ...e, ...patch } : e)),
|
||
});
|
||
const sources = regularizationSources(draft);
|
||
const add = (kind: RegularizationEvent['kind']) =>
|
||
save({
|
||
...b,
|
||
events: [
|
||
...b.events,
|
||
newRegularization(
|
||
crypto.randomUUID(),
|
||
kind,
|
||
sources.at(-1) ?? null,
|
||
),
|
||
],
|
||
});
|
||
const labels = {
|
||
scope: 'As parcelas e o âmbito correspondem ao evento,sem estender o direito a outros bens/operações',
|
||
recorded:
|
||
'O valor original foi deduzido ou é pressuposto explicitamente assumido nesta simulação',
|
||
annual: 'Bases finais do ano completo22(4),semIVA,incluindo operações fora de campo',
|
||
exclusions:
|
||
'Excluí vendas de activo usado e operações imobiliárias/financeiras acessórias22(5)',
|
||
supplierCorrection:
|
||
'O fornecedor anulou/reduziu a operação e emitiu o documento rectificativo',
|
||
rightUnchanged:
|
||
'O documento conserva o âmbito e as condições de dedução;sem alteração de direito',
|
||
};
|
||
return (
|
||
<details className="rounded border p-3">
|
||
<summary>E — activos e regularizações por evento</summary>
|
||
<p>
|
||
Reutiliza resultados congelados de D e documentos existentes. O
|
||
acerto anual usa o IVA elegível antes de D, já limitado por C, e
|
||
compara com o deduzido. Uma rectificação da compra tem documento
|
||
e prazo próprios. Guardar E não altera as deduções originais;
|
||
depois actualize expressamente o apuramento parcial para
|
||
conferir F.
|
||
</p>
|
||
<p>
|
||
18.º(6) com afectação mista continua pendente. Não existe
|
||
regularização plurianual genérica de activos,correcção de erros
|
||
ou incobráveis automatizada neste percurso. Submissão fiscal
|
||
fora do InvesteMoz;19–27/restituição em L3.
|
||
</p>
|
||
{error && <p role="alert">{error}</p>}
|
||
<button type="button" onClick={() => add('annual')}>
|
||
Preparar acerto anual do pro rata
|
||
</button>{' '}
|
||
<button type="button" onClick={() => add('purchase_correction')}>
|
||
Preparar rectificação da dedução pelo adquirente
|
||
</button>
|
||
{b.events.map((e, index) => (
|
||
<fieldset className="rounded border p-3" key={e.id}>
|
||
<legend>
|
||
{index + 1}.{' '}
|
||
{e.kind === 'annual'
|
||
? 'Acerto anual22(6)'
|
||
: 'Rectificação51(4)'}
|
||
</legend>
|
||
<label>
|
||
Resultado original congelado
|
||
<select
|
||
value={sources.findIndex(
|
||
(s) =>
|
||
JSON.stringify(s) ===
|
||
JSON.stringify(e.source),
|
||
)}
|
||
onChange={(event) => {
|
||
const source =
|
||
sources[Number(event.target.value)];
|
||
if (source)
|
||
change(e.id, {
|
||
source: structuredClone(source),
|
||
rowIds: source.inputs
|
||
.filter(
|
||
(r) =>
|
||
e.kind !== 'annual' ||
|
||
r.d?.method === 'pro_rata',
|
||
)
|
||
.map((r) => r.id),
|
||
});
|
||
}}
|
||
>
|
||
<option value={-1}>Seleccione uma versão</option>
|
||
{sources.map((s, i) => (
|
||
<option key={i} value={i}>
|
||
Revisão{s.revision} — {s.calculatedAt}
|
||
</option>
|
||
))}
|
||
</select>
|
||
</label>
|
||
<p>
|
||
Escolher outra versão adopta expressamente as parcelas
|
||
dessa versão,conservando os outros dados do evento.
|
||
Desmarque parcelas fora do âmbito;não pode regularizar a
|
||
mesma aquisição duas vezes.
|
||
</p>
|
||
{e.source?.inputs.map((r) => (
|
||
<label className="block" key={r.id}>
|
||
<input
|
||
type="checkbox"
|
||
checked={e.rowIds.includes(r.id)}
|
||
onChange={(event) =>
|
||
change(e.id, {
|
||
rowIds: event.target.checked
|
||
? [...e.rowIds, r.id]
|
||
: e.rowIds.filter(
|
||
(id) => id !== r.id,
|
||
),
|
||
})
|
||
}
|
||
/>
|
||
{r.description || r.id} — {r.documentRef} —{' '}
|
||
{r.period} —{' '}
|
||
{r.origin === 'projected'
|
||
? 'Pressuposto de simulação'
|
||
: 'Efectivo declarado'}
|
||
</label>
|
||
))}
|
||
<label>
|
||
Período da regularização
|
||
<input
|
||
type="month"
|
||
value={e.period}
|
||
min="2026-01"
|
||
max="2026-12"
|
||
onChange={(v) =>
|
||
change(e.id, { period: v.target.value })
|
||
}
|
||
/>
|
||
</label>
|
||
<label>
|
||
Documento/fonte do evento
|
||
<input
|
||
value={e.reference}
|
||
onChange={(v) =>
|
||
change(e.id, { reference: v.target.value })
|
||
}
|
||
/>
|
||
</label>
|
||
<label>
|
||
Motivo e âmbito do evento
|
||
<input
|
||
value={e.reason}
|
||
onChange={(v) =>
|
||
change(e.id, { reason: v.target.value })
|
||
}
|
||
/>
|
||
</label>
|
||
{e.kind === 'annual' ? (
|
||
<>
|
||
<label>
|
||
Operações anuais finais com direito — sem IVA
|
||
(MZN)
|
||
<input
|
||
value={e.numerator}
|
||
onChange={(v) =>
|
||
change(e.id, {
|
||
numerator: v.target.value,
|
||
})
|
||
}
|
||
/>
|
||
</label>
|
||
<label>
|
||
Total anual final22(4–5) — sem IVA (MZN)
|
||
<input
|
||
value={e.denominator}
|
||
onChange={(v) =>
|
||
change(e.id, {
|
||
denominator: v.target.value,
|
||
})
|
||
}
|
||
/>
|
||
</label>
|
||
</>
|
||
) : (
|
||
<>
|
||
<label>
|
||
Data de recepção do documento rectificativo
|
||
<input
|
||
type="date"
|
||
value={e.receivedDate}
|
||
onChange={(v) =>
|
||
change(e.id, {
|
||
receivedDate: v.target.value,
|
||
})
|
||
}
|
||
/>
|
||
</label>
|
||
<label>
|
||
Dedução corrigida documentada (MZN)
|
||
<input
|
||
value={e.correctedDeductible}
|
||
onChange={(v) =>
|
||
change(e.id, {
|
||
correctedDeductible: v.target.value,
|
||
})
|
||
}
|
||
/>
|
||
</label>
|
||
</>
|
||
)}
|
||
{Object.entries(labels)
|
||
.filter(([k]) =>
|
||
[
|
||
'scope',
|
||
'recorded',
|
||
...(e.kind === 'annual'
|
||
? ['annual', 'exclusions']
|
||
: ['supplierCorrection', 'rightUnchanged']),
|
||
].includes(k),
|
||
)
|
||
.map(([k, label]) => (
|
||
<label className="block" key={k}>
|
||
{label}
|
||
<select
|
||
value={e.facts[k] ?? ''}
|
||
onChange={(v) =>
|
||
change(e.id, {
|
||
facts: {
|
||
...e.facts,
|
||
[k]: v.target.value as
|
||
| ''
|
||
| 'yes'
|
||
| 'no',
|
||
},
|
||
})
|
||
}
|
||
>
|
||
<option value="">Por confirmar</option>
|
||
<option value="yes">Sim</option>
|
||
<option value="no">Não</option>
|
||
</select>
|
||
</label>
|
||
))}
|
||
<button
|
||
type="button"
|
||
onClick={() =>
|
||
save({
|
||
...b,
|
||
events: b.events.filter(
|
||
(row) => row.id !== e.id,
|
||
),
|
||
})
|
||
}
|
||
>
|
||
Remover evento em preparação (histórico conservado)
|
||
</button>
|
||
</fieldset>
|
||
))}
|
||
{[...new Set(b.events.map((e) => e.period))]
|
||
.filter(Boolean)
|
||
.map((period) => (
|
||
<label className="block" key={period}>
|
||
<input
|
||
type="checkbox"
|
||
checked={b.completePeriods.includes(period)}
|
||
onChange={(e) =>
|
||
save({
|
||
...b,
|
||
completePeriods: e.target.checked
|
||
? [...b.completePeriods, period]
|
||
: b.completePeriods.filter(
|
||
(p) => p !== period,
|
||
),
|
||
})
|
||
}
|
||
/>
|
||
Reconciliei todas as regularizações deste período —{' '}
|
||
{period};os outros motivos não automatizados foram
|
||
conferidos.
|
||
</label>
|
||
))}
|
||
<p>
|
||
Quando existirem eventos E, retire as declarações «Sem
|
||
operações» nos campos12/13 desse período. Uma cobertura
|
||
incompleta mantém os totais afectados indeterminados.
|
||
</p>
|
||
<button
|
||
type="button"
|
||
onClick={() => {
|
||
try {
|
||
save(recordRegularizations(draft, b));
|
||
} catch (cause) {
|
||
setError(String(cause));
|
||
}
|
||
}}
|
||
>
|
||
Calcular e guardar regularizações E
|
||
</button>
|
||
{[
|
||
...regularizationReportRows(draft),
|
||
...frozenAssetRows(draft),
|
||
].map(([, label, value, source], i) => (
|
||
<p key={i}>
|
||
<strong>{label}</strong>: {value}
|
||
<small className="block">{source}</small>
|
||
</p>
|
||
))}
|
||
</details>
|
||
);
|
||
}
|