Files
Investidor-V2/resources/js/components/simulation-irpc-investment.tsx
T
2026-11-10 16:17:48 +02:00

659 lines
30 KiB
TypeScript

import { QualificationFields } from './simulation-irpc-special';
import { FiscalProofParcelFields } from './fiscal-proof-parcel';
import {
prepareInvestmentCredit,
prepareInvestmentCarry,
type AnnualBook,
} from '@/lib/irpc-annual-settlement';
import {
investmentCreditSource,
newInvestmentAsset,
type InvestmentCredit,
type InvestmentAsset,
} from '@/lib/irpc-investment-credit';
export function SimulationIrpcInvestment({
draft,
book,
save,
}: {
draft: Record<string, unknown>;
book: AnnualBook;
save: (b: AnnualBook) => void;
}) {
const c = book.investmentCredit;
if (!c)
return (
<fieldset className="rounded border p-3">
<legend>Crédito fiscal por investimento — CBF15</legend>
<p>
Prepare o detalhe do investimento efectivo e da qualificação
do projecto. Substitui neste cálculo o resumo anterior dos
benefícios, que fica conservado. O recorte cobre
investimento efectivo de2026 dentro dos cinco exercícios do
prazo,com colecta integral ou quota residual documental do
projecto, sem outros benefícios próprios ou transportes
anteriores.
</p>
<button
type="button"
onClick={() => save(prepareInvestmentCredit(draft, book))}
>
Preparar crédito por investimento
</button>
</fieldset>
);
const patch = (v: Partial<InvestmentCredit>) =>
save({ ...book, investmentCredit: { ...c, ...v } });
const text = (
key:
| 'projectReference'
| 'locationReference'
| 'startDate'
| 'startReference'
| 'recognitionReference'
| 'noOtherBenefitsReference'
| 'soleProjectReference',
label: string,
) => (
<label className="block">
{label}
<input
aria-label={'CBF15 — ' + label}
value={c[key]}
onChange={(e) => patch({ [key]: e.target.value })}
/>
</label>
);
const choice = (
key:
| 'cbfRequirements'
| 'genericOnly'
| 'noOtherBenefits'
| 'soleProject',
label: string,
) => (
<label className="block">
{label}
<select
aria-label={'CBF15 — ' + label}
value={c[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>
);
return (
<fieldset className="space-y-3 rounded border p-3">
<legend>Crédito fiscal por investimento — CBF15</legend>
<p>{investmentCreditSource}</p>
<p>
Beneficiário {c.subjectNuit}, exercício {c.year}. Resumo
anterior conservado:{' '}
{book.internationalCredits?.benefits.state ||
'informação em falta'}
; montante{' '}
{book.internationalCredits?.benefits.amount || 'não preenchido'}
. O detalhe aplica apenas o benefício aqui qualificado; não
volta a deduzir o resumo.
</p>
<button
type="button"
onClick={() =>
patch({
subjectNuit: book.subjectNuit,
year: book.year,
assessmentKey: book.assessmentKey,
})
}
>
Vincular CBF15 à colecta seleccionada
</button>
<QualificationFields
label="CBF15"
value={c.qualification}
onChange={(qualification) => patch({ qualification })}
/>
{choice(
'noOtherBenefits',
'Sem outros benefícios próprios/cumulações neste recorte',
)}
{text(
'noOtherBenefitsReference',
'Reconciliação dos outros benefícios e tratamentos L6/L7',
)}
{c.qualification.state === 'known' && (
<>
{text(
'projectReference',
'Despacho/termos ou instrumento legal do projecto',
)}
{choice(
'cbfRequirements',
'Condições CBF3/9 e âmbito do projecto documentados',
)}
{choice(
'genericOnly',
'Investimento no regime genérico, fora dos benefícios específicos — art.13',
)}
<label className="block">
Reconhecimento — art.10
<select
aria-label="CBF15 — Reconhecimento"
value={c.recognition}
onChange={(e) =>
patch({
recognition: e.target
.value as InvestmentCredit['recognition'],
})
}
>
<option value="">Por corresponder</option>
<option value="documented">
Reconhecimento documentado
</option>
<option value="not_required">
Excepção com norma e suporte
</option>
</select>
</label>
{text(
'recognitionReference',
'Suporte do reconhecimento ou norma de excepção',
)}
{choice(
'soleProject',
'Toda a colecta corrente respeita à actividade do projecto',
)}
{c.soleProject === 'yes'
? text(
'soleProjectReference',
'Suporte da correspondência integral da colecta ao projecto',
)
: c.soleProject === 'no' && (
<fieldset className="rounded border p-3">
<legend>
Parte da colecta respeitante ao projecto
</legend>
<p>
Indique a colecta residual do projecto
depois dos créditos
económicos/internacionais que lhe
respeitam. Documente a atribuição entre
actividades; a quota não pode exceder a
colecta global remanescente. O portal não
reparte rendimentos, custos ou perdas por
presunção.
</p>
<label className="block">
Quota residual do projecto — MZN ou n/d
<input
aria-label="CBF15 — Quota residual do projecto"
value={c.projectQuota?.amount ?? ''}
onChange={(e) =>
patch({
projectQuota: {
amount: e.target.value,
reference:
c.projectQuota
?.reference ?? '',
},
})
}
/>
</label>
<label className="block">
Suporte da quota e atribuição dos créditos
anteriores
<input
aria-label="CBF15 — Suporte da quota do projecto"
value={
c.projectQuota?.reference ?? ''
}
onChange={(e) =>
patch({
projectQuota: {
amount:
c.projectQuota
?.amount ?? '',
reference: e.target.value,
},
})
}
/>
</label>
</fieldset>
)}
<label className="block">
Local efectivo do investimento
<select
aria-label="CBF15 — Local do investimento"
value={c.location}
onChange={(e) =>
patch({
location: e.target
.value as InvestmentCredit['location'],
})
}
>
<option value="">Por documentar</option>
<option value="maputo_city">
Cidade de Maputo —5%
</option>
<option value="other">
Restantes províncias —10%, incluindo Província
de Maputo
</option>
</select>
</label>
{text(
'locationReference',
'Suporte da localização do investimento',
)}
<label className="block">
Início do prazo do crédito
<select
aria-label="CBF15 — Início"
value={c.startKind}
onChange={(e) =>
patch({
startKind: e.target
.value as InvestmentCredit['startKind'],
})
}
>
<option value="">Por documentar</option>
<option value="new">
Projecto novo — início de exploração
</option>
<option value="existing">
Projecto em funcionamento — início dos
procedimentos após aprovação
</option>
</select>
</label>
{text('startDate', 'Data do início aplicável — AAAA-MM-DD')}
{text(
'startReference',
'Suporte do início e contagem dos cinco exercícios',
)}
<p>
Indique investimento efectivamente realizado,
documentado em MZN, adquirido novo e afecto à exploração
nacional. Art15(6) exclui edifícios, viaturas ligeiras,
mobiliário/conforto/decoração, equipamentos sociais,
tecnologia de ponta e outros bens não directa e
imprescindivelmente ligados à actividade produtiva. Não
copiar investimento projectado como realização.
</p>
<button
type="button"
onClick={() =>
patch({
assets: [
...c.assets,
newInvestmentAsset(crypto.randomUUID()),
],
})
}
>
Acrescentar activo/documento
</button>
</>
)}
{!c.carryForward ? (
<button
type="button"
onClick={() => save(prepareInvestmentCarry(draft, book))}
>
Preparar transporte de crédito de anos anteriores
</button>
) : (
<fieldset className="space-y-2 rounded border p-3">
<legend>Transporte CBF15 do mesmo projecto</legend>
<p>
Declare activos de origem anterior, taxa e qualificação
históricas. Não copie saldos de simulações como uso
fiscal. O prazo original de cinco exercícios mantém-se;
investimentos correntes ficam separados.
</p>
<label className="block">
Correspondência histórica do crédito transportado
<input
aria-label="CBF15 transporte — Correspondência"
value={c.carryForward.reference}
onChange={(e) =>
patch({
carryForward: {
...c.carryForward!,
reference: e.target.value,
},
})
}
/>
</label>
<label className="block">
Investimento efectivo de 2026
<select
aria-label="CBF15 transporte — Investimento corrente"
value={c.carryForward.currentState}
onChange={(e) =>
patch({
carryForward: {
...c.carryForward!,
currentState: e.target.value as
| 'known'
| 'absent'
| '',
},
})
}
>
<option value="">Por documentar</option>
<option value="known">
Conhecido — activos correntes discriminados
</option>
<option value="absent">
Ausência documentada — apenas origens anteriores
</option>
</select>
</label>
<label className="block">
Suporte do investimento corrente/ausência
<input
aria-label="CBF15 transporte — Suporte corrente"
value={c.carryForward.currentReference}
onChange={(e) =>
patch({
carryForward: {
...c.carryForward!,
currentReference: e.target.value,
},
})
}
/>
</label>
<label className="block">
Uso efectivo de todo o crédito reconciliado
<select
aria-label="CBF15 transporte — Reconciliação do uso"
value={c.carryForward.useReconciled}
onChange={(e) =>
patch({
carryForward: {
...c.carryForward!,
useReconciled: e.target.value as
| ''
| 'yes'
| 'no',
},
})
}
>
<option value="">Por documentar</option>
<option value="yes">
Sim — inclui as origens anteriores e o crédito
corrente
</option>
<option value="no">Não reconciliado</option>
</select>
</label>
<button
type="button"
onClick={() =>
patch({
assets: [
...c.assets,
{
...newInvestmentAsset(
crypto.randomUUID(),
),
originYear: '',
originNuit: '',
originProject: '',
originReference: '',
originRate: '',
},
],
})
}
>
Acrescentar activo de origem anterior
</button>
</fieldset>
)}
{c.assets.map((r, index) => {
const change = (v: Partial<InvestmentAsset>) =>
patch({
assets: c.assets.map((x) =>
x.id === r.id ? { ...r, ...v } : x,
),
});
return (
<fieldset
key={r.id}
className="space-y-2 rounded border p-3"
>
<legend>Investimento {index + 1}</legend>
{r.originYear !== undefined && (
<>
<p>
Origem anterior — mantém a data real de
aquisição e o prazo original do projecto.
</p>
{(
[
[
'originYear',
'Exercício que originou o crédito',
],
[
'originNuit',
'NUIT do contribuinte de origem',
],
[
'originProject',
'Referência do projecto de origem',
],
[
'originRate',
'Taxa original documentada — 5 ou 10',
],
[
'originReference',
'Suporte da qualificação e crédito de origem,mesmo projecto/local/regime',
],
] as const
).map(([key, label]) => (
<label key={key} className="block">
{label}
<input
aria-label={
'CBF15 origem ' +
(index + 1) +
' — ' +
label
}
value={r[key] ?? ''}
onChange={(e) =>
change({
[key]: e.target.value,
})
}
/>
</label>
))}
</>
)}
{(
[
['issuer', 'Fornecedor/emitente'],
['document', 'Factura/documento'],
[
'date',
'Data do investimento efectivo — AAAA-MM-DD',
],
[
'proofAmount',
'Montante total do documento — MZN',
],
[
'amount',
'Investimento atribuído a este activo/projecto — MZN',
],
['currency', 'Moeda documental (recorte MZN)'],
[
'reference',
'Suporte do activo, valor e realização',
],
] as const
).map(([key, label]) => (
<label key={key} className="block">
{label}
<input
aria-label={
'CBF15 activo ' +
(index + 1) +
' — ' +
label
}
value={r[key]}
onChange={(e) =>
change({ [key]: e.target.value })
}
/>
</label>
))}
<FiscalProofParcelFields
label={'InvestimentoCBF15 ' + (index + 1)}
value={r}
change={change}
/>
{(
[
[
'realised',
'Investimento efectivamente realizado',
],
[
'newTangible',
'Activo imobilizado corpóreo adquirido em estado novo',
],
[
'domesticUse',
'Afecto à exploração do projecto em território nacional',
],
[
'directProductive',
'Directa e imprescindivelmente ligado à actividade produtiva',
],
[
'excluded15_6',
'Abrangido por alguma exclusão15(6) (para deduzir exige Não)',
],
] as const
).map(([key, label]) => (
<label key={key} className="block">
{label}
<select
aria-label={
'CBF15 activo ' +
(index + 1) +
' — ' +
label
}
value={r[key]}
onChange={(e) =>
change({ [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>
))}
<button
type="button"
onClick={() =>
patch({
assets: c.assets.filter(
(x) => x.id !== r.id,
),
})
}
>
Retirar activo {index + 1}
</button>
</fieldset>
);
})}
<fieldset className="space-y-2 rounded border p-2">
<legend>Uso anterior efectivo do crédito</legend>
<select
aria-label="CBF15 — Uso anterior"
value={c.previousUse.state}
onChange={(e) =>
patch({
previousUse: {
...c.previousUse,
state: e.target
.value as InvestmentCredit['previousUse']['state'],
},
})
}
>
<option value="">Informação em falta</option>
<option value="known">Montante conhecido</option>
<option value="absent">Ausência documentada</option>
</select>
<label className="block">
Montante anteriormente utilizado em todo o crédito
discriminado
<input
aria-label="CBF15 — Montante de uso anterior"
value={c.previousUse.amount}
onChange={(e) =>
patch({
previousUse: {
...c.previousUse,
amount: e.target.value,
},
})
}
/>
</label>
<label className="block">
Suporte do uso/ausência
<input
aria-label="CBF15 — Suporte do uso anterior"
value={c.previousUse.reference}
onChange={(e) =>
patch({
previousUse: {
...c.previousUse,
reference: e.target.value,
},
})
}
/>
</label>
</fieldset>
<p>
Calcule e guarde pelo percurso L8 existente. O crédito não
utilizado fica discriminado; transporte para outros exercícios e
formalidades oficiais conservam percurso próprio.
</p>
</fieldset>
);
}