264 lines
11 KiB
TypeScript
264 lines
11 KiB
TypeScript
import { useState } from 'react';
|
|
import {
|
|
ispcEligibilityBook,
|
|
ispcEligibilityCurrent,
|
|
} from '@/lib/ispc-eligibility';
|
|
import {
|
|
ispcQuarterBook,
|
|
saveIspcQuarterBook,
|
|
recordIspcQuarter,
|
|
ispcQuarterReportRows,
|
|
type IspcQuarterInput,
|
|
type IspcQuarterBook,
|
|
} from '@/lib/ispc-quarter';
|
|
export function SimulationIspcQuarter({
|
|
draft,
|
|
onChange,
|
|
}: {
|
|
draft: Record<string, unknown>;
|
|
onChange: (field: string, value: string) => void;
|
|
}) {
|
|
const [error, setError] = useState('');
|
|
if (draft.fiscal_component !== 'ispc') return null;
|
|
try {
|
|
if (ispcEligibilityBook(draft).input.mode === 'occasional')
|
|
return (
|
|
<p>
|
|
Actividade habitual:campos trimestrais conservados no
|
|
histórico. Use o percurso de operações ocasionais próprias
|
|
abaixo.
|
|
</p>
|
|
);
|
|
} catch {
|
|
/* Preserve the existing explicit recovery error below. */
|
|
}
|
|
let book: IspcQuarterBook;
|
|
try {
|
|
book = ispcQuarterBook(draft);
|
|
} catch (e) {
|
|
return <p role="alert">{String(e)}</p>;
|
|
}
|
|
const i = book.input,
|
|
save = (b: IspcQuarterBook) => {
|
|
try {
|
|
onChange('modelo_a_snapshot', saveIspcQuarterBook(draft, b));
|
|
setError('');
|
|
} catch (e) {
|
|
setError(String(e));
|
|
}
|
|
},
|
|
change = (patch: Partial<IspcQuarterInput>) =>
|
|
save({ ...book, input: { ...i, ...patch } });
|
|
let service = false,
|
|
ready = false;
|
|
try {
|
|
const e = ispcEligibilityBook(draft);
|
|
ready =
|
|
ispcEligibilityCurrent(draft, e) &&
|
|
e.result?.status === 'compatible';
|
|
service = e.input.activities.some((a) =>
|
|
['services12', 'liberal15'].includes(a),
|
|
);
|
|
} catch {
|
|
/* Show explicit preparation issue below, never fabricate eligibility. */
|
|
}
|
|
return (
|
|
<section
|
|
aria-label="Apuramento trimestral ISPC"
|
|
className="space-y-3 rounded border p-4"
|
|
>
|
|
<h4>ISPC — apuramento trimestral</h4>
|
|
<p>
|
|
Reutiliza o enquadramento e a opção guardados. Declare o volume
|
|
de todas as actividades do sujeito passivo em MZN; custos e IVA
|
|
de aquisições não reduzem esta base. Dados em falta permanecem
|
|
indeterminados.
|
|
</p>
|
|
{!ready && (
|
|
<p>
|
|
Reveja e guarde primeiro a avaliação de enquadramento acima.
|
|
As entradas trimestrais e o histórico ficam conservados.
|
|
</p>
|
|
)}
|
|
{error && <p role="alert">{error}</p>}
|
|
<label className="block">
|
|
Ano
|
|
<input
|
|
className="block rounded border p-2"
|
|
value={i.year}
|
|
onChange={(e) => change({ year: e.target.value })}
|
|
/>
|
|
</label>
|
|
<label className="block">
|
|
Trimestre
|
|
<select
|
|
className="block rounded border p-2"
|
|
value={i.quarter}
|
|
onChange={(e) => change({ quarter: e.target.value })}
|
|
>
|
|
{['1', '2', '3', '4'].map((q) => (
|
|
<option key={q} value={q}>
|
|
{q} —{' '}
|
|
{
|
|
[
|
|
'Janeiro a Março',
|
|
'Abril a Junho',
|
|
'Julho a Setembro',
|
|
'Outubro a Dezembro',
|
|
][Number(q) - 1]
|
|
}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</label>
|
|
<label className="block">
|
|
Âmbito do volume
|
|
<select
|
|
className="block rounded border p-2"
|
|
value={i.coverage}
|
|
onChange={(e) =>
|
|
change({
|
|
coverage: e.target
|
|
.value as IspcQuarterInput['coverage'],
|
|
})
|
|
}
|
|
>
|
|
<option value="">Por confirmar</option>
|
|
<option value="all_taxpayer_activity">
|
|
Todas as actividades do sujeito passivo, sem repetir
|
|
oportunidades
|
|
</option>
|
|
</select>
|
|
</label>
|
|
<div className="grid gap-3 md:grid-cols-2">
|
|
{i.sales.map(
|
|
(v, n) =>
|
|
(n < Number(i.quarter) ||
|
|
(i.annualBandOrigin === 'closed_year' &&
|
|
!service)) && (
|
|
<fieldset
|
|
key={n}
|
|
className="space-y-2 rounded border p-2"
|
|
>
|
|
<legend>
|
|
Trimestre {n + 1}
|
|
{n + 1 === Number(i.quarter)
|
|
? ' — a apurar'
|
|
: ' — acumulado anterior'}
|
|
</legend>
|
|
<label className="block">
|
|
Volume T{n + 1} em MZN
|
|
<input
|
|
className="block rounded border p-2"
|
|
inputMode="decimal"
|
|
value={v}
|
|
onChange={(e) => {
|
|
const a = [
|
|
...i.sales,
|
|
] as IspcQuarterInput['sales'];
|
|
a[n] = e.target.value;
|
|
change({ sales: a });
|
|
}}
|
|
/>
|
|
</label>
|
|
<label className="block">
|
|
Suporte ou pressuposto T{n + 1}
|
|
<input
|
|
className="block rounded border p-2"
|
|
value={i.salesRefs[n]}
|
|
onChange={(e) => {
|
|
const a = [
|
|
...i.salesRefs,
|
|
] as IspcQuarterInput['salesRefs'];
|
|
a[n] = e.target.value;
|
|
change({ salesRefs: a });
|
|
}}
|
|
/>
|
|
</label>
|
|
</fieldset>
|
|
),
|
|
)}
|
|
</div>
|
|
{!service && (
|
|
<fieldset className="space-y-2 rounded border p-3">
|
|
<legend>
|
|
Escalão anual da actividade — distinto do volume de
|
|
acesso
|
|
</legend>
|
|
<p>
|
|
Os limites de 1.000.000 / 2.500.000 / 4.000.000 MZN são
|
|
anuais. A taxa do escalão é aplicada à base do
|
|
trimestre; não dividir os limites por quatro. O excesso
|
|
anual tem taxa própria de 20%.
|
|
</p>
|
|
<label className="block">
|
|
Volume anual 2026 para o escalão
|
|
<input
|
|
className="block rounded border p-2"
|
|
value={i.annualBandVolume}
|
|
onChange={(e) =>
|
|
change({ annualBandVolume: e.target.value })
|
|
}
|
|
/>
|
|
</label>
|
|
<label className="block">
|
|
Origem da referência anual
|
|
<select
|
|
className="block rounded border p-2"
|
|
value={i.annualBandOrigin}
|
|
onChange={(e) =>
|
|
change({
|
|
annualBandOrigin: e.target
|
|
.value as IspcQuarterInput['annualBandOrigin'],
|
|
})
|
|
}
|
|
>
|
|
<option value="">Por identificar</option>
|
|
<option value="forecast">
|
|
Previsão expressa — simulação
|
|
</option>
|
|
<option value="closed_year">
|
|
Ano encerrado — reconciliar quatro trimestres
|
|
</option>
|
|
</select>
|
|
</label>
|
|
<label className="block">
|
|
Suporte da referência anual
|
|
<input
|
|
className="block rounded border p-2"
|
|
value={i.annualBandRef}
|
|
onChange={(e) =>
|
|
change({ annualBandRef: e.target.value })
|
|
}
|
|
/>
|
|
</label>
|
|
</fieldset>
|
|
)}
|
|
<button
|
|
type="button"
|
|
className="rounded bg-emerald-800 p-2 text-white"
|
|
onClick={() => {
|
|
try {
|
|
save(recordIspcQuarter(draft, book));
|
|
} catch (e) {
|
|
setError(String(e));
|
|
}
|
|
}}
|
|
>
|
|
Calcular e guardar apuramento trimestral ISPC
|
|
</button>
|
|
{ispcQuarterReportRows(draft).map((r, n) => (
|
|
<p key={n}>
|
|
<strong>{r[1]}:</strong> {r[2]}
|
|
<small className="block">{r[3]}</small>
|
|
</p>
|
|
))}
|
|
<p>
|
|
Preparação para a declaração periódica dos artigos 10/11. Este
|
|
cálculo não submete formulários nem efectua pagamentos. Retenção
|
|
na fonte em aquisições ocasionais tem registo e base próprios.
|
|
</p>
|
|
</section>
|
|
);
|
|
}
|