620 lines
30 KiB
TypeScript
620 lines
30 KiB
TypeScript
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' | 'liquidation' | 'association'
|
||
>,
|
||
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.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
|
||
)
|
||
.filter(
|
||
([key]) =>
|
||
r.nationalMethod !== 'proportional' ||
|
||
(key !== 'nationalLimit' &&
|
||
key !== 'nationalLimitReference'),
|
||
)
|
||
.map(([key, label]) => field(key, label))}
|
||
<label className="block">
|
||
Quota proporcional
|
||
<select
|
||
aria-label={prefix + 'Método da quota'}
|
||
value={r.nationalMethod ?? 'documented'}
|
||
onChange={(e) =>
|
||
patch({
|
||
nationalMethod: e.target
|
||
.value as EconomicPosition['nationalMethod'],
|
||
})
|
||
}
|
||
>
|
||
<option value="documented">
|
||
Limite documentado
|
||
</option>
|
||
<option value="proportional">
|
||
Derivar da colecta L6 ordinária
|
||
</option>
|
||
</select>
|
||
</label>
|
||
{r.nationalMethod === 'proportional' && (
|
||
<label className="block">
|
||
Correspondência dos lucros mais acréscimo53 na
|
||
base corrente
|
||
<textarea
|
||
aria-label={
|
||
prefix + 'Correspondência da quota'
|
||
}
|
||
value={r.nationalBasisReference ?? ''}
|
||
onChange={(e) =>
|
||
patch({
|
||
nationalBasisReference:
|
||
e.target.value,
|
||
})
|
||
}
|
||
/>
|
||
<span className="block">
|
||
Quota = colecta L6 × (lucros incluídos +
|
||
crédito nominal acrescido) / matéria
|
||
colectável L6. Não altera a base nem
|
||
acrescenta o crédito novamente. O limite
|
||
manual anterior fica conservado, mas não é
|
||
usado neste modo.
|
||
</span>
|
||
</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>
|
||
{r.kind === 'association' && (
|
||
<fieldset className="space-y-2 rounded border p-3">
|
||
<legend>Associação — art.64(4)</legend>
|
||
<label className="block">
|
||
Modalidade
|
||
<select
|
||
aria-label={
|
||
prefix + 'Modalidade da associação'
|
||
}
|
||
value={r.association?.kind ?? ''}
|
||
onChange={(e) =>
|
||
patch({
|
||
association: {
|
||
kind: e.target.value as
|
||
| ''
|
||
| 'participation'
|
||
| 'quota',
|
||
effectivelyTaxed:
|
||
r.association
|
||
?.effectivelyTaxed ??
|
||
'',
|
||
reference:
|
||
r.association
|
||
?.reference ?? '',
|
||
},
|
||
})
|
||
}
|
||
>
|
||
<option value="">
|
||
Escolha a modalidade
|
||
</option>
|
||
<option value="participation">
|
||
Em participação
|
||
</option>
|
||
<option value="quota">À quota</option>
|
||
</select>
|
||
</label>
|
||
{r.association?.kind === 'participation' && (
|
||
<label className="block">
|
||
Rendimentos distribuídos efectivamente
|
||
tributados
|
||
<select
|
||
aria-label={
|
||
prefix +
|
||
'Tributação efectiva da participação'
|
||
}
|
||
value={
|
||
r.association.effectivelyTaxed
|
||
}
|
||
onChange={(e) =>
|
||
patch({
|
||
association: {
|
||
...r.association!,
|
||
effectivelyTaxed: e
|
||
.target.value as
|
||
| ''
|
||
| 'yes'
|
||
| 'no',
|
||
},
|
||
})
|
||
}
|
||
>
|
||
<option value="">
|
||
Informação em falta
|
||
</option>
|
||
<option value="yes">
|
||
Sim — documentado
|
||
</option>
|
||
<option value="no">
|
||
Não — documentado
|
||
</option>
|
||
</select>
|
||
</label>
|
||
)}
|
||
<label className="block">
|
||
Suporte do contrato, rendimentos e adaptação
|
||
do crédito
|
||
<input
|
||
aria-label={
|
||
prefix + 'Suporte da associação'
|
||
}
|
||
value={r.association?.reference ?? ''}
|
||
onChange={(e) =>
|
||
patch({
|
||
association: {
|
||
kind: '',
|
||
effectivelyTaxed: '',
|
||
...r.association,
|
||
reference: e.target.value,
|
||
},
|
||
})
|
||
}
|
||
/>
|
||
</label>
|
||
</fieldset>
|
||
)}
|
||
{r.kind === 'liquidation' && (
|
||
<fieldset className="space-y-2 rounded border p-3">
|
||
<legend>
|
||
Partilha em liquidação — art.64(3)
|
||
</legend>
|
||
<p>
|
||
No rendimento abaixo indique a diferença
|
||
positiva classificada documentalmente como
|
||
aplicação de capitais. O crédito limita-se
|
||
também ao valor atribuído menos entradas
|
||
efectivas para capital. O excesso mantém
|
||
tratamento próprio de mais-valia; não
|
||
integra a quota económica. Confirme a
|
||
inclusão L6 e o IRPC correspondente apenas à
|
||
parcela elegível.
|
||
</p>
|
||
{(
|
||
[
|
||
[
|
||
'attributed',
|
||
'Valor atribuído na partilha — MZN',
|
||
],
|
||
[
|
||
'paidInCapital',
|
||
'Entradas efectivas para capital na contabilidade da liquidada — MZN',
|
||
],
|
||
[
|
||
'reference',
|
||
'Suporte da partilha e classificação do rendimento',
|
||
],
|
||
] as const
|
||
).map(([key, label]) => (
|
||
<label key={key} className="block">
|
||
{label}
|
||
<input
|
||
aria-label={prefix + label}
|
||
value={r.liquidation?.[key] ?? ''}
|
||
onChange={(e) =>
|
||
patch({
|
||
liquidation: {
|
||
attributed: '',
|
||
paidInCapital: '',
|
||
reference: '',
|
||
...r.liquidation,
|
||
[key]: e.target.value,
|
||
},
|
||
})
|
||
}
|
||
/>
|
||
</label>
|
||
))}
|
||
</fieldset>
|
||
)}
|
||
{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>
|
||
);
|
||
}
|