import { inputText } from '../lib/input-text.ts'; import { newIrpcInterestRule, type IrpcInterestRule, } from '@/lib/irpc-interest'; import { mrftaCases, mrftaCaseHtml } from '@/lib/irpc-mrfta-cases'; import { useState } from 'react'; import { irpcCurrent, type IrpcBook } from '@/lib/irpc-withholdings'; import { newMrftaInput, recordMrfta, mrftaHtml, mrftaReportRows, mrftaCurrent, type MrftaInput, } from '@/lib/irpc-mrfta'; export function SimulationIrpcMrfta({ draft, book, save, }: { draft: Record; book: IrpcBook; save: (book: IrpcBook) => void; }) { const [error, setError] = useState(''), i = book.mrfta ?? newMrftaInput(); const change = (patch: Partial) => save({ ...book, mrfta: { ...i, ...patch } }); const interestMode = i.interest.mode || (i.interest.calculation === 'automatic' ? 'automatic' : ''); const interest = (patch: Partial) => change({ interest: { ...i.interest, ...patch } }); const field = ( key: | 'period' | 'nuit' | 'name' | 'commercialName' | 'taxArea' | 'address' | 'contact' | 'completenessRef' | 'otherRubricsRef', label: string, type = 'text', ) => ( ); const select = ( key: 'declaration' | 'timing' | 'completeness' | 'otherRubrics', label: string, options: Array<[string, string]>, ) => ( ); const periods = [ ...new Set( book.result?.rows.map((r) => r.period).filter(Boolean) ?? [], ), ]; const identities = Object.entries( (JSON.parse(inputText(draft.modelo_a_snapshot || '{}')) .irpcWithholdings ?? {}) as Record, ).filter( ([, b]) => b.mrfta?.nuit && b.mrfta?.name && (!i.nuit.trim() || i.nuit === b.mrfta.nuit) && (!i.name.trim() || i.name === b.mrfta.name) && ( [ 'nuit', 'name', 'commercialName', 'taxArea', 'address', 'contact', ] as const ).some((key) => !i[key].trim() && b.mrfta![key].trim()), ); return (
MRFTA — preenchimento e juros compensatórios

Reutiliza o cálculo guardado, documentos e entregas IRPC. Um mês/ano por declaração. A retenção sofrida e as dispensas ficam separadas. Quadros 8/9 reservados à assinatura e aos serviços; não há submissão ou aceitação AT.

{identities.map(([scope, b]) => ( ))}
{field('period', 'Quadro 2 — mês/ano', 'month')} {select('declaration', 'Quadro 1 — retenção na fonte', [ ['', 'Por identificar'], ['initial', 'Inicial'], ['replacement', 'Substituição'], ])} {select('timing', 'Quadro 2 — situação do prazo', [ ['', 'Por esclarecer'], ['within', 'Dentro do prazo'], ['late', 'Fora do prazo'], ])} {field('nuit', 'Quadro 3 — NUIT')} {field('taxArea', 'Quadro 3 — área fiscal')} {field('name', 'Quadro 4 — nome do sujeito passivo')} {field('commercialName', 'Quadro 5 — nome comercial')} {field('address', 'Quadro 6 — domicílio fiscal')} {field('contact', 'Quadro 6 — contacto')} {select( 'completeness', 'Retenções do sujeito passivo neste período conferidas, incluindo demais livros', [ ['', 'Por esclarecer'], ['yes', 'Sim'], ['no', 'Ainda não'], ], )} {field( 'completenessRef', 'Referência da reconciliação do período', )} {select( 'otherRubrics', 'IRPS, tributação autónoma e outras rubricas fora deste recorte', [ ['', 'Por esclarecer'], [ 'none', 'Ausência de outros valores devidos documentada', ], ['pending', 'Existem ou estão por apurar'], ], )} {field( 'otherRubricsRef', 'Documento da situação das outras rubricas', )}
Quadro 7 — CER das rubricas correspondidas

Base, taxa e imposto vêm do cálculo guardado. O CER é transcrito da referência aplicável; os números de campos da guia antiga não são automaticamente códigos de receita. Códigos introduzidos não são validados por catálogo oficial; códigos provisórios ou de simulação não são correspondências oficiais.

{book.result?.inputs .filter( (r) => r.direction === 'paid' && book.result?.rows.some( (v) => v.id === r.id && v.period === i.period && v.status === 'calculated' && v.treatment !== 'dispensed', ), ) .map((r) => { const code = i.codes.find((c) => c.rowId === r.id) ?? { rowId: r.id, cer: '', reference: '', }; const update = (p: Partial) => change({ codes: [ ...i.codes.filter((c) => c.rowId !== r.id), { ...code, ...p }, ], }); return (
{r.documentRef} — {r.counterparty}
); })}
Juros compensatórios — separados do principal

Regulamento IRPC, artigos 25/35: a taxa refere MAIBOR de 12 meses + 2 pontos. O Banco de Moçambique documenta a descontinuidade da MAIBOR. O portal não adopta MIMO ou Prime como substituto nem calcula juros de mora.

{interestMode && ( <> )} {['assessment', 'automatic'].includes(interestMode) && ( <> {i.interest.mode === 'assessment' && ( )} )} {['timely', 'automatic'].includes(interestMode) && ( <> )} {interestMode === 'automatic' && (
Parâmetros documentados do cálculo — sem taxa fiscal de2026 pré-definida

Simulação pelos pressupostos introduzidos, sem confirmação fiscal dos juros. Preencha apenas a regra aplicável ao caso. Sem parâmetro completo, os juros continuam indeterminados. A taxa bancária actual não é adoptada por substituição. Imposto por conta não retido e juros de mora mantêm regra própria.

{( [ 'annualRate', 'rateSource', 'fiscalBasis', 'validFrom', 'validTo', 'dayBasisRef', 'roundingRef', 'occurrenceRef', ] as const ).map((k) => ( ))} {(['dayBasis', 'rounding', 'retention'] as const).map( (k) => ( ), )}
)} {book.mrftaResult && (
Casos de trabalho separados por período, rubrica, taxa e CER

Estas fichas conservam as pendências do período. Não são declarações autónomas, anexos oficiais ou uma autorização para fundir taxas.

{mrftaCases(book.mrftaResult.model).map((c) => (
{c.period}; {c.rubric}; {c.rate}%; CER: {c.cer || 'por corresponder'}; documentos: {c.documents.join('; ')}.
))}
)} {error &&

{error}

} {mrftaReportRows(book, irpcCurrent(draft, book)).map((r, n) => (

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

))}
); }