import { inputText } from '../lib/input-text.ts'; import { SimulationIrpcMrfta } from '@/components/simulation-irpc-mrfta'; import { irpcReliefRules, newIrpcReliefPreparation, prepareIrpcRelief, } from '@/lib/irpc-relief-preparation'; import { SimulationFxSelection } from '@/components/simulation-fx-selection'; import { irpcFxSources, irpcFxRow, updateIrpcFx, type IrpcFxAmount, } from '@/lib/irpc-fx'; import { useState } from 'react'; import { irpcBook, saveIrpcBook, reuseIrpcSources, irpcSources, newIrpcRow, recordIrpc, irpcKinds, emptyIrpcOrdinaryFacts, irpcReportRows, irpcPreparationHtml, type IrpcBook, type IrpcRow, type IrpcEvent, type IrpcPayment, type IrpcOrdinaryFacts, } from '@/lib/irpc-withholdings'; export function SimulationIrpcWithholdings({ draft, onChange, }: { draft: Record; onChange: (field: string, value: string) => void; }) { const [error, setError] = useState(''); if ( draft.fiscal_component !== 'irpc' && draft.fiscal_component !== 'withholding' ) return null; try { const book = irpcBook(draft), sources = irpcSources(draft), save = (b: IrpcBook) => { try { onChange('modelo_a_snapshot', saveIrpcBook(draft, b)); setError(''); } catch (e) { setError(String(e)); } }, change = (id: string, p: Partial) => save({ ...book, rows: book.rows.map((r) => r.id === id ? { ...r, ...p } : r, ), }), uid = () => crypto.randomUUID(); const field = ( r: IrpcRow, k: keyof IrpcRow, label: string, type = 'text', ) => ( ); const select = ( r: IrpcRow, k: keyof IrpcRow, label: string, options: Array<[string, string]>, ) => ( ); const yn: Array<[string, string]> = [ ['', 'Por esclarecer'], ['yes', 'Sim'], ['no', 'Não'], ]; const eventLabels: Array<[IrpcEvent['kind'], string]> = [ ['cost', 'Reconhecimento do custo'], ['payment', 'Pagamento do rendimento'], ['maturity', 'Vencimento, ainda que presumido'], ['availability', 'Colocação à disposição'], ['liquidation', 'Liquidação'], ['quantification', 'Apuramento do quantitativo'], ]; const fxSources = irpcFxSources(draft); const renderFx = ( id: string, input: IrpcFxAmount, apply: (patch: Partial) => void, ) => (

A base/comprovativo conserva a moeda e o montante originais. A conversão reutiliza o selector e a aritmética entregues; a data cambial fica distinta do evento da retenção.

{fxSources.map((src) => (
{src.label};data: {src.selection.applied?.date || 'por confirmar'} ;selecção actual: {input.fxSelection?.applied?.rate || 'nenhuma'}.
))} { if (patch.fxSelection) apply(updateIrpcFx(input, patch.fxSelection)); }} />
); const ordinarySelect = ( r: IrpcRow, k: keyof IrpcOrdinaryFacts, label: string, options: Array<[string, string]>, ) => ( ); const paymentChange = (id: string, p: Partial) => save({ ...book, payments: book.payments.map((v) => v.id === id ? { ...v, ...p } : v, ), }); return (

IRPC — retenções e preparação documental

Reutilize documentos e contrapartes existentes. Classifique o rendimento e o evento aplicável:facturar,pagar e reconhecer o custo são factos distintos. O conjunto calcula os casos digitais de 10%, as taxas normais de 20% e as taxas especiais do artigo 62(4) nos âmbitos identificados. Dispensas dos artigos 68(d/f) e limite convencional de royalties Portugal–Moçambique são calculados nos casos documentados. Informação insuficiente condiciona apenas o respectivo rendimento.

{error &&

{error}

} {book.rows.map((r) => (
{r.description || r.documentRef || 'Rendimento por identificar'} {r.source && (

Origem:{r.source.id}. Entradas deliberadamente editadas são conservadas.{' '} {sources.find((s) => s.id === r.source!.id) ?.key !== r.source.key && ( <> Origem alterada ou indisponível. {sources.find( (s) => s.id === r.source!.id, ) && ( <> {' '} Base actual:{r.base};base na origem: {sources.find( (s) => s.id === r.source!.id, )?.amount || 'desconhecida'} ;documento actual: {r.documentRef};origem: {sources.find( (s) => s.id === r.source!.id, )?.documentRef || 'desconhecido'} . )} )}

)}
{select(r, 'direction', 'Perspectiva', [ [ 'paid', 'Rendimento pago — retenção a entregar', ], [ 'received', 'Rendimento recebido — retenção sofrida', ], ])} {field(r, 'counterparty', 'Contraparte')} {field( r, 'documentRef', 'Documento / operação / linha distinta', )} {field(r, 'description', 'Descrição')} {select(r, 'origin', 'Origem', [ ['projected', 'Projecção com pressupostos'], ['effective', 'Factos documentados'], ])} {select( r, 'kind', 'Natureza do rendimento', irpcKinds, )} {field( r, 'base', 'Base do rendimento, separada do IVA', )} {field(r, 'currency', 'Moeda da base')} {select( r, 'recipientIrpc', 'Titular sujeito a IRPC neste rendimento', yn, )} {select( r, 'payerIrpc', 'Devedor sujeito a IRPC', yn, )} {select(r, 'residence', 'Residência do titular', [ ['', 'Por esclarecer'], ['resident', 'Residente'], ['nonresident', 'Não residente'], ])} {r.residence === 'nonresident' && ( <> {select( r, 'permanentEstablishment', 'Existe estabelecimento estável em Moçambique', yn, )} {r.permanentEstablishment === 'yes' && select( r, 'attributable', 'Rendimento imputável ao estabelecimento', yn, )} )} {select( r, 'mozambique', 'Rendimento obtido em Moçambique, conforme qualificação legal', yn, )} {field( r, 'qualificationRef', 'Suporte / pressuposto da qualificação, residência e incidência', )} {select( r, 'specialRelief', 'Convenção, isenção ou dispensa aplicável', [ ['', 'Por esclarecer'], [ 'none', 'Não invocada neste caso qualificado', ], [ 'claimed', 'Invocada — identificar regra e condições', ], ], )} {r.specialRelief === 'claimed' && field( r, 'reliefRef', 'Artigo, condições, período e prova invocados', )}
{r.specialRelief === 'claimed' && (
Preparação de dispensa ou convenção

Reutiliza contraparte, residência, estabelecimento estável e documento deste rendimento.

{( [ 'instrument', 'article', 'country', 'residenceDocument', 'validFrom', 'validTo', 'conditions', 'evidence', ] as const ) .filter( (k) => ![ 'country', 'residenceDocument', ].includes(k) || r.reliefPreparation?.kind === 'treaty', ) .map((k) => ( ))} {(r.reliefPreparation?.rule === 'ptMz12' ? ([ 'treatyResident', 'beneficialOwner', 'payerResidentMz', 'armLength', 'royaltyDefinition', ] as const) : r.reliefPreparation?.rule === 'cirpc68f' ? ([ 'subjectToIrpc', 'notTransparent', ] as const) : r.reliefPreparation?.rule === 'cirpc68d' ? (['subjectToIrpc'] as const) : [] ).map((k) => ( ))}

{prepareIrpcRelief( r, r.reliefPreparation ?? newIrpcReliefPreparation(), ).missing.length ? 'Faltam elementos documentais; confira artigo, período e provas.' : 'Documentos registados; calcule para conferir as condições da regra seleccionada.'}

)} {r.currency !== 'MZN' && renderFx(r.id, r, (patch) => change(r.id, patch))} {r.kind && ![ 'digital_goods', 'digital_services', 'emoney_commission', 'other', ].includes(r.kind) && (
Factos específicos do rendimento {ordinarySelect( r, 'nonDigital', 'Rendimento fora do âmbito digital/moeda electrónica do artigo62(5)', yn, )} {r.kind === 'capital' && ordinarySelect( r, 'capitalType', 'Categoria dos capitais', [ ['', 'Por esclarecer'], [ 'unlisted_interest', 'Juros não cotados, fora das permutas de liquidez', ], [ 'unlisted_dividend', 'Dividendos não cotados', ], [ 'listed_debt_mz', 'Juros de bilhetes do Tesouro/títulos de dívida cotados em bolsa —62(3)a', ], [ 'listed_other_mz', 'Outros títulos cotados na Bolsa de Valores de Moçambique —62(4)d', ], [ 'liquidity_repo', 'Juros de permutas de liquidez em bancos,com ou sem garantia —62(3)b', ], ['other', 'Outra categoria'], ], )} {r.kind === 'capital' && r.residence === 'resident' && [ 'listed_debt_mz', 'liquidity_repo', ].includes( r.ordinary?.capitalType ?? '', ) && ordinarySelect( r, 'capitalSeatAndManagement', 'Titular com sede e direcção efectiva nacionais —62(3)', yn, )} {r.kind === 'rent' && ordinarySelect( r, 'rentOwnPropertyManagement', 'Titular é sociedade com objecto de gestão de imóveis próprios', yn, )} {([ 'telecom', 'international_transport', 'connected_installation', 'aircraft_maintenance_charter', 'rural_public_electrification', 'maritime_fishing_cabotage', ].includes(r.kind) || (r.kind === 'capital' && r.ordinary?.capitalType === 'listed_other_mz')) && ordinarySelect( r, 'specialFacts', 'Factos exactos da categoria62(4) identificados no suporte — não apenas actividade semelhante', yn, )}
)}
Eventos aplicáveis — artigo67(5), versão2026
{r.events.map((e, n) => (
))} {select( r, 'eventsComplete', 'Todos os eventos aplicáveis identificados, incluindo custo quando aplicável', yn, )} {select( r, 'settlement', 'Reconciliação da entrega / prova de retenção sofrida', [ ['', 'Por esclarecer'], ['none', 'Ainda sem entrega / prova'], [ 'complete', 'Pagamentos / provas associados completos', ], ], )} {field( r, 'settlementRef', 'Referência da situação declarada', )} {r.allocations.map((a, n) => (
))} {r.settlement === 'complete' && ( )}
))}
Pagamentos e comprovativos reutilizáveis neste livro

Uma referência é registada uma vez e repartida entre rendimentos sem exceder o montante. Pagamento ao fornecedor não é entrega à AT; pagamentos de IVA conservam o seu vínculo anterior e não são automaticamente usados em IRPC.

{book.payments.map((p) => (
{( ['reference', 'date', 'amount', 'currency'] as const ).map((k) => ( ))} {p.currency !== 'MZN' && (p.kind === 'withholding_proof' ? ( renderFx(p.id, p, (patch) => paymentChange(p.id, patch), ) ) : (

A entrega IRPC à AT requer o montante MZN do comprovativo; converter o pagamento ao beneficiário não prova entrega do imposto.

))}
))}
{irpcReportRows(draft) .slice(0, book.result?.rows.length ?? 0) .map((r, n) => (

{r[1]}: {r[2]} {r[3]}

))}

Formalidades externas:MRFTA19/39 e guia M/39 preservados. CER/rubricas digitais e remissão67(7)→62(6) por corresponder. Exportar não submete nem obtém aceitação AT. L5 continua aberto;ISPC e IRPS conservam os seus limites anteriores.

); } catch (e) { return

{String(e)}

; } }