Files
Investidor-V2/resources/js/components/simulation-irpc-economic.tsx
T

417 lines
19 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import {
prepareEconomicBook,
type AnnualBook,
} from '@/lib/irpc-annual-settlement';
import {
economicSource,
newEconomicPosition,
type EconomicPosition,
} from '@/lib/irpc-economic-credit';
export function SimulationIrpcEconomic({
book,
save,
residentConfirmed,
}: {
book: AnnualBook;
save: (book: AnnualBook) => void;
residentConfirmed: boolean;
}) {
const c = book.economicCredits;
if (!c)
return (
<fieldset className="rounded border p-3">
<legend>Crédito por dupla tributação económica</legend>
<p>
Para lucros distribuídos por entidade moçambicana sujeita e
não isenta de IRPC, distinga o imposto correspondente aos
lucros da retenção sofrida e do imposto estrangeiro.
Reutiliza a colecta e conserva o resumo de créditos
anterior.
</p>
<button
type="button"
onClick={() => save(prepareEconomicBook(book))}
>
Preparar crédito económico
</button>
</fieldset>
);
const update = (next: typeof c) => save({ ...book, economicCredits: next });
const rowChange = (r: EconomicPosition) =>
update({ ...c, rows: c.rows.map((x) => (x.id === r.id ? r : x)) });
return (
<fieldset className="space-y-3 rounded border p-3">
<legend>Crédito económico — lucros distribuídos</legend>
<p>{economicSource}</p>
<p>
O detalhe substitui nesta execução o resumo económico anterior,
que permanece conservado:{' '}
{book.internationalCredits?.economic.state ||
'informação em falta'}
; montante{' '}
{book.internationalCredits?.economic.amount || 'não preenchido'}
. Os benefícios e o crédito internacional mantêm os seus estados
próprios.
</p>
<label className="block">
Estado do crédito económico
<select
aria-label="Estado do crédito económico L8"
value={c.state}
onChange={(e) =>
update({
...c,
state: e.target.value as typeof c.state,
})
}
>
<option value="">Informação em falta</option>
<option value="known">Lucros e IRPC conhecidos</option>
<option value="absent">Ausência documentada</option>
</select>
</label>
<label className="block">
Suporte do inventário económico
<input
aria-label="Suporte económico L8"
value={c.reference}
onChange={(e) =>
update({ ...c, reference: e.target.value })
}
/>
</label>
{(c.state === 'known' || c.rows.length > 0) && (
<button
type="button"
onClick={() =>
update({
...c,
state: 'known',
rows: [
...c.rows,
{
...newEconomicPosition(crypto.randomUUID()),
subjectNuit: book.subjectNuit,
year: book.year,
recipientResident: residentConfirmed
? 'yes'
: '',
},
],
})
}
>
Registar lucros distribuídos
</button>
)}
{c.rows.map((r, i) => {
const patch = (v: Partial<EconomicPosition>) =>
rowChange({ ...r, ...v });
const prefix = 'Crédito económico ' + (i + 1) + ' — ';
const field = (
key: Exclude<keyof EconomicPosition, 'previousUse'>,
label: string,
) => (
<label key={key} className="block">
{label}
<input
aria-label={prefix + label}
value={r[key]}
onChange={(e) => patch({ [key]: e.target.value })}
/>
</label>
);
const choice = (
key:
| 'recipientResident'
| 'payerResident'
| 'payerIrpc'
| 'payerExempt'
| 'article40'
| 'incomeIncluded',
label: string,
) => (
<label key={key} className="block">
{label}
<select
aria-label={prefix + label}
value={r[key]}
onChange={(e) => patch({ [key]: e.target.value })}
>
<option value="">Informação em falta</option>
<option value="yes">Sim — documentado</option>
<option value="no">Não — documentado</option>
</select>
</label>
);
const outside =
(!!r.kind && r.kind !== 'ordinary_profit') ||
r.article40 === 'yes';
const detail = (
<>
{(
[
[
'distributionDocument',
'Documento da distribuição',
],
[
'distributionParcel',
'Parcela dos lucros no documento',
],
[
'distributionDate',
'Data da distribuição (AAAA-MM-DD)',
],
[
'income',
'Lucros incluídos antes do crédito — MZN, ilíquidos de retenção',
],
[
'incomeReference',
'Suporte da inclusão e do valor ilíquido na base corrente',
],
[
'taxIssuer',
'Emitente do comprovativo de IRPC correspondente',
],
[
'taxDocument',
'Documento do IRPC correspondente aos lucros',
],
[
'taxParcel',
'Parcela desse IRPC no documento',
],
[
'parcelReference',
'Suporte da identificação da parcela de imposto',
],
['currency', 'Moeda comprovada'],
[
'proofAmount',
'IRPC comprovado nesta parcela — MZN',
],
[
'taxAmount',
'IRPC correspondente aos lucros seleccionados — MZN',
],
[
'attributionReference',
'Suporte da correspondência entre imposto e lucros',
],
[
'previouslyAttributed',
'IRPC desta parcela já atribuído a outros lucros — MZN',
],
[
'priorAttributionReference',
'Suporte das atribuições anteriores, incluindo zero conhecido',
],
[
'grossUpIncluded',
'Crédito nominal já acrescido à base (art.53) — MZN',
],
[
'grossUpReference',
'Suporte do acréscimo art.53 já incluído em L6',
],
[
'nationalLimit',
'Quota proporcional da colecta após acréscimo — MZN',
],
[
'nationalLimitReference',
'Suporte da quota proporcional da colecta',
],
] as const
).map(([key, label]) => field(key, label))}
{choice(
'incomeIncluded',
'Lucros ilíquidos e acréscimo incluídos na base corrente',
)}
<p>
O cálculo confere 60% do IRPC correspondente com o
acréscimo já incluído em L6 e a quota proporcional
documentada. Se a base estiver incompleta,
reconcilie-a no percurso existente. Esta acção
conserva os valores e não altera a colecta
automaticamente.
</p>
<button
type="button"
onClick={() =>
patch({ assessmentKey: book.assessmentKey })
}
>
Confirmar inclusão e quota na colecta seleccionada
</button>
<label className="block">
Utilização anterior efectiva do crédito
<select
aria-label={prefix + 'Estado do uso anterior'}
value={r.previousUse.state}
onChange={(e) =>
patch({
previousUse: {
...r.previousUse,
state: e.target
.value as EconomicPosition['previousUse']['state'],
},
})
}
>
<option value="">Informação em falta</option>
<option value="known">
Montante conhecido
</option>
<option value="absent">
Ausência documentada
</option>
</select>
</label>
{(r.previousUse.state === 'known' ||
r.previousUse.amount) && (
<label className="block">
Crédito já utilizado — MZN
<input
aria-label={prefix + 'Uso anterior MZN'}
value={r.previousUse.amount}
onChange={(e) =>
patch({
previousUse: {
...r.previousUse,
amount: e.target.value,
},
})
}
/>
</label>
)}
<label className="block">
Suporte do uso anterior ou da sua ausência
<input
aria-label={prefix + 'Suporte do uso anterior'}
value={r.previousUse.reference}
onChange={(e) =>
patch({
previousUse: {
...r.previousUse,
reference: e.target.value,
},
})
}
/>
</label>
</>
);
return (
<fieldset
key={r.id}
className="space-y-2 rounded border p-3"
>
<legend>Lucros {i + 1}</legend>
{field('subjectNuit', 'NUIT beneficiário')}
{field('payerNuit', 'NUIT distribuidor')}
{field('year', 'Exercício')}
<label className="block">
Natureza
<select
aria-label={prefix + 'Natureza'}
value={r.kind}
onChange={(e) =>
patch({
kind: e.target
.value as EconomicPosition['kind'],
})
}
>
<option value="">Por identificar</option>
<option value="ordinary_profit">
Lucros ordinários — art.64(1–2)
</option>
<option value="liquidation">
Partilha em liquidação — art.64(3)
</option>
<option value="association">
Associação — art.64(4)
</option>
</select>
</label>
{residentConfirmed && r.recipientResident === 'yes' ? (
<p>
Residência do beneficiário reutilizada da
colecta corrente no regime geral. NUIT e
exercício conservados.
</p>
) : (
choice(
'recipientResident',
'Beneficiário com sede/direcção efectiva em Moçambique',
)
)}
{choice(
'payerResident',
'Distribuidor com sede/direcção efectiva em Moçambique',
)}
{choice('payerIrpc', 'Distribuidor sujeito a IRPC')}
{choice('payerExempt', 'Distribuidor isento de IRPC')}
{field(
'qualificationReference',
'Suporte da qualificação das entidades e dos lucros',
)}
{choice(
'article40',
'Esta parcela está abrangida pela exclusão do art.40(1)',
)}
{field(
'article40Reference',
'Suporte da correspondência do art.40(1) e seu período',
)}
<p>
O art.40(1), na redacção de 2009, contempla as
entidades e condições próprias, incluindo
participação directa de pelo menos20% e prazo de
dois anos, nos termos da fonte. Uma parcela excluída
da base não recebe também o crédito64. A simples
percentagem de participação não decide todas as
condições.
</p>
{outside ? (
<>
<p>
Esta parcela exige o tratamento específico
identificado. O crédito ordinário não é
aplicado; os valores introduzidos ficam
conservados.
</p>
<details>
<summary>
Consultar ou reconciliar dados
conservados
</summary>
{detail}
</details>
</>
) : (
detail
)}
<button
type="button"
onClick={() =>
update({
...c,
rows: c.rows.filter((x) => x.id !== r.id),
})
}
>
Retirar lucros {i + 1}
</button>
</fieldset>
);
})}
</fieldset>
);
}