432 lines
20 KiB
TypeScript
432 lines
20 KiB
TypeScript
import {
|
|
prepareCreditCarry,
|
|
type AnnualBook,
|
|
} from '@/lib/irpc-annual-settlement';
|
|
import {
|
|
carrySource,
|
|
newCarryLot,
|
|
newCarryUse,
|
|
type CarryLot,
|
|
type CreditCarry,
|
|
} from '@/lib/irpc-credit-carry';
|
|
import CountrySelect, { type Country } from '@/components/country-select';
|
|
import { countryIsoCodes } from '@/lib/country-iso-codes';
|
|
|
|
const pt = new Intl.DisplayNames(['pt'], { type: 'region' });
|
|
const en = new Intl.DisplayNames(['en'], { type: 'region' });
|
|
const countries: Country[] = countryIsoCodes
|
|
.filter((code) => code !== 'MZ')
|
|
.map((code) => ({
|
|
code,
|
|
name: pt.of(code) ?? code,
|
|
name_en: en.of(code) ?? code,
|
|
}))
|
|
.sort((a, b) => a.name.localeCompare(b.name, 'pt'));
|
|
|
|
export function SimulationIrpcCarry({
|
|
book,
|
|
save,
|
|
}: {
|
|
book: AnnualBook;
|
|
save: (book: AnnualBook) => void;
|
|
}) {
|
|
const c = book.creditCarry;
|
|
if (!c)
|
|
return (
|
|
<fieldset className="rounded border p-3">
|
|
<legend>Saldos de exercícios anteriores</legend>
|
|
<p>
|
|
Se dispõe de remanescentes documentados de crédito
|
|
internacional ou PEC, prepare a origem e as utilizações
|
|
efectivas. Os resultados anteriores ficam conservados.
|
|
</p>
|
|
<button
|
|
type="button"
|
|
onClick={() => save(prepareCreditCarry(book))}
|
|
>
|
|
Preparar saldos anteriores
|
|
</button>
|
|
</fieldset>
|
|
);
|
|
const update = (value: CreditCarry) =>
|
|
save({ ...book, creditCarry: value });
|
|
const lotChange = (r: CarryLot) =>
|
|
update({ ...c, lots: c.lots.map((x) => (x.id === r.id ? r : x)) });
|
|
return (
|
|
<fieldset className="space-y-3 rounded border p-3">
|
|
<legend>Crédito internacional e PEC — exercícios anteriores</legend>
|
|
<p>{carrySource}</p>
|
|
<label className="block">
|
|
Inventário de saldos anteriores
|
|
<select
|
|
aria-label="Estado dos saldos anteriores L8"
|
|
value={c.state}
|
|
onChange={(e) =>
|
|
update({
|
|
...c,
|
|
state: e.target.value as CreditCarry['state'],
|
|
})
|
|
}
|
|
>
|
|
<option value="">Informação em falta</option>
|
|
<option value="known">Saldos conhecidos</option>
|
|
<option value="absent">Ausência documentada</option>
|
|
</select>
|
|
</label>
|
|
<label className="block">
|
|
Suporte do inventário
|
|
<input
|
|
aria-label="Suporte dos saldos anteriores L8"
|
|
value={c.reference}
|
|
onChange={(e) =>
|
|
update({ ...c, reference: e.target.value })
|
|
}
|
|
/>
|
|
</label>
|
|
{(c.state === 'known' || c.lots.length > 0) && (
|
|
<>
|
|
<p>
|
|
Consolide uma origem por natureza, país e exercício.
|
|
Transcreva o montante elegível apurado e o deduzido na
|
|
origem, com o suporte da regra desse exercício. Imposto
|
|
estrangeiro acima dos limites de elegibilidade não é
|
|
remanescente por insuficiência de colecta. Só se aplica
|
|
aqui o prazo ordinário a exercícios anuais; situações
|
|
transitórias ou especiais exigem correspondência
|
|
própria.
|
|
</p>
|
|
<button
|
|
type="button"
|
|
onClick={() =>
|
|
update({
|
|
...c,
|
|
lots: [
|
|
...c.lots,
|
|
{
|
|
...newCarryLot(crypto.randomUUID()),
|
|
subjectNuit: book.subjectNuit,
|
|
},
|
|
],
|
|
})
|
|
}
|
|
>
|
|
Registar origem de saldo anterior
|
|
</button>
|
|
</>
|
|
)}
|
|
{c.lots.map((r, i) => {
|
|
const patch = (v: Partial<CarryLot>) =>
|
|
lotChange({ ...r, ...v });
|
|
const prefix = 'Saldo anterior ' + (i + 1) + ' — ';
|
|
return (
|
|
<fieldset
|
|
key={r.id}
|
|
className="space-y-2 rounded border p-3"
|
|
>
|
|
<legend>Origem {i + 1}</legend>
|
|
<label className="block">
|
|
Natureza
|
|
<select
|
|
aria-label={prefix + 'Natureza'}
|
|
value={r.kind}
|
|
onChange={(e) =>
|
|
patch({
|
|
kind: e.target
|
|
.value as CarryLot['kind'],
|
|
})
|
|
}
|
|
>
|
|
<option value="">Por identificar</option>
|
|
<option value="international">
|
|
Crédito por dupla tributação internacional
|
|
</option>
|
|
<option value="pec">
|
|
PEC efectivamente pago
|
|
</option>
|
|
</select>
|
|
</label>
|
|
{(r.kind === 'international' || r.country) && (
|
|
<CountrySelect
|
|
countries={countries}
|
|
label={prefix + 'País estrangeiro'}
|
|
value={r.country}
|
|
onChange={(country) => patch({ country })}
|
|
allowLegacyValue
|
|
/>
|
|
)}
|
|
{r.kind === 'pec' && (
|
|
<p>
|
|
O PEC não tem país estrangeiro. A escolha da
|
|
natureza conserva os valores introduzidos;
|
|
reconcilie qualquer país anterior.
|
|
</p>
|
|
)}
|
|
{(
|
|
[
|
|
['subjectNuit', 'NUIT titular'],
|
|
['year', 'Exercício de origem'],
|
|
['currency', 'Moeda comprovada'],
|
|
[
|
|
'originDocument',
|
|
'Declaração/apuramento de origem',
|
|
],
|
|
[
|
|
'originReference',
|
|
'Suporte da origem e insuficiência de colecta',
|
|
],
|
|
[
|
|
'ruleReference',
|
|
'Regra e período aplicáveis na origem',
|
|
],
|
|
[
|
|
'eligible',
|
|
'Montante elegível na origem — MZN',
|
|
],
|
|
['originDeducted', 'Deduzido na origem — MZN'],
|
|
[
|
|
'selected',
|
|
'Parcela escolhida para este fecho — MZN',
|
|
],
|
|
] as const
|
|
).map(([key, label]) => (
|
|
<label key={key} className="block">
|
|
{label}
|
|
<input
|
|
aria-label={prefix + label}
|
|
value={r[key]}
|
|
onChange={(e) =>
|
|
patch({ [key]: e.target.value })
|
|
}
|
|
/>
|
|
</label>
|
|
))}
|
|
<label className="block">
|
|
Exercício de origem anual ordinário
|
|
<select
|
|
aria-label={prefix + 'Período ordinário'}
|
|
value={r.ordinary}
|
|
onChange={(e) =>
|
|
patch({
|
|
ordinary: e.target
|
|
.value as CarryLot['ordinary'],
|
|
})
|
|
}
|
|
>
|
|
<option value="">Informação em falta</option>
|
|
<option value="yes">Sim — documentado</option>
|
|
<option value="no">
|
|
Não — regra específica por corresponder
|
|
</option>
|
|
</select>
|
|
</label>
|
|
<label className="block">
|
|
Remanescente por insuficiência de colecta
|
|
<select
|
|
aria-label={prefix + 'Insuficiência de colecta'}
|
|
value={r.insufficient}
|
|
onChange={(e) =>
|
|
patch({
|
|
insufficient: e.target
|
|
.value as CarryLot['insufficient'],
|
|
})
|
|
}
|
|
>
|
|
<option value="">Informação em falta</option>
|
|
<option value="yes">Sim — documentado</option>
|
|
<option value="no">
|
|
Não — não é saldo elegível neste percurso
|
|
</option>
|
|
</select>
|
|
</label>
|
|
<label className="block">
|
|
Utilizações efectivas posteriores à origem
|
|
<select
|
|
aria-label={prefix + 'Estado dos usos'}
|
|
value={r.usesState}
|
|
onChange={(e) =>
|
|
patch({
|
|
usesState: e.target
|
|
.value as CarryLot['usesState'],
|
|
})
|
|
}
|
|
>
|
|
<option value="">Informação em falta</option>
|
|
<option value="known">
|
|
Utilizações conhecidas
|
|
</option>
|
|
<option value="absent">
|
|
Ausência documentada
|
|
</option>
|
|
</select>
|
|
</label>
|
|
<label className="block">
|
|
Suporte do inventário de utilizações
|
|
<input
|
|
aria-label={prefix + 'Suporte dos usos'}
|
|
value={r.usesReference}
|
|
onChange={(e) =>
|
|
patch({ usesReference: e.target.value })
|
|
}
|
|
/>
|
|
</label>
|
|
<p>
|
|
Inclua utilizações efectivas já ocorridas neste
|
|
exercício antes desta parcela. Não registe
|
|
simulações históricas como utilizações fiscais.
|
|
Crédito internacional admite fracção exacta n/d; não
|
|
é aplicado arredondamento fiscal.
|
|
</p>
|
|
{r.usesState === 'known' && (
|
|
<button
|
|
type="button"
|
|
onClick={() =>
|
|
patch({
|
|
uses: [
|
|
...r.uses,
|
|
newCarryUse(crypto.randomUUID()),
|
|
],
|
|
})
|
|
}
|
|
>
|
|
Registar utilização efectiva da origem {i + 1}
|
|
</button>
|
|
)}
|
|
{r.uses.map((u, j) => (
|
|
<fieldset key={u.id} className="rounded border p-2">
|
|
<legend>
|
|
Utilização {i + 1}.{j + 1}
|
|
</legend>
|
|
{(
|
|
[
|
|
['year', 'Exercício'],
|
|
['amount', 'Montante MZN'],
|
|
['document', 'Documento de utilização'],
|
|
['reference', 'Suporte'],
|
|
] as const
|
|
).map(([key, label]) => (
|
|
<label key={key} className="block">
|
|
{label}
|
|
<input
|
|
aria-label={
|
|
prefix +
|
|
'Uso ' +
|
|
(j + 1) +
|
|
' — ' +
|
|
label
|
|
}
|
|
value={u[key]}
|
|
onChange={(e) =>
|
|
patch({
|
|
uses: r.uses.map((x) =>
|
|
x.id === u.id
|
|
? {
|
|
...x,
|
|
[key]: e
|
|
.target
|
|
.value,
|
|
}
|
|
: x,
|
|
),
|
|
})
|
|
}
|
|
/>
|
|
</label>
|
|
))}
|
|
<button
|
|
type="button"
|
|
onClick={() =>
|
|
patch({
|
|
uses: r.uses.filter(
|
|
(x) => x.id !== u.id,
|
|
),
|
|
})
|
|
}
|
|
>
|
|
Retirar utilização {i + 1}.{j + 1}
|
|
</button>
|
|
</fieldset>
|
|
))}
|
|
<button
|
|
type="button"
|
|
onClick={() =>
|
|
update({
|
|
...c,
|
|
lots: c.lots.filter((x) => x.id !== r.id),
|
|
})
|
|
}
|
|
>
|
|
Retirar origem {i + 1}
|
|
</button>
|
|
</fieldset>
|
|
);
|
|
})}
|
|
{(c.state === 'known' ||
|
|
c.currentInternational ||
|
|
c.currentPec ||
|
|
c.allocationReference) && (
|
|
<fieldset className="space-y-2 rounded border p-3">
|
|
<legend>Escolha das parcelas entre exercícios</legend>
|
|
<p>
|
|
Escolha quanto deduzir dos créditos correntes e de cada
|
|
origem anterior. Os créditos internacionais precedem o
|
|
PEC; a soma fica limitada à colecta. Não se presume
|
|
prioridade entre exercícios e nenhuma parcela é reduzida
|
|
automaticamente. Valores não escolhidos ficam
|
|
identificados, sem criação de transporte oficial.
|
|
</p>
|
|
<label className="block">
|
|
Crédito internacional corrente a deduzir — MZN
|
|
<input
|
|
aria-label="Parcela corrente internacional L8"
|
|
value={c.currentInternational}
|
|
onChange={(e) =>
|
|
update({
|
|
...c,
|
|
currentInternational: e.target.value,
|
|
})
|
|
}
|
|
/>
|
|
</label>
|
|
<label className="block">
|
|
PEC corrente a deduzir — MZN
|
|
<input
|
|
aria-label="Parcela corrente PEC L8"
|
|
value={c.currentPec}
|
|
onChange={(e) =>
|
|
update({ ...c, currentPec: e.target.value })
|
|
}
|
|
/>
|
|
</label>
|
|
<label className="block">
|
|
Suporte da escolha e seus impactos
|
|
<input
|
|
aria-label="Suporte da afectação entre exercícios L8"
|
|
value={c.allocationReference}
|
|
onChange={(e) =>
|
|
update({
|
|
...c,
|
|
allocationReference: e.target.value,
|
|
})
|
|
}
|
|
/>
|
|
</label>
|
|
<button
|
|
type="button"
|
|
onClick={() =>
|
|
update({ ...c, assessmentKey: book.assessmentKey })
|
|
}
|
|
>
|
|
Confirmar afectação à colecta seleccionada
|
|
</button>
|
|
<p>
|
|
{c.assessmentKey &&
|
|
c.assessmentKey === book.assessmentKey
|
|
? 'Escolha vinculada à colecta seleccionada; o cálculo verifica disponibilidade e limites.'
|
|
: 'Falta confirmar a escolha na colecta seleccionada. Valores conservados.'}
|
|
</p>
|
|
</fieldset>
|
|
)}
|
|
</fieldset>
|
|
);
|
|
}
|