import { fraction, fractionPresentation, type FiscalFraction, } from './fiscal-rational.ts'; export const normalFormSource = 'M/22, exemplar inventariado IRPC M-22- DECLARACAO DE RENDIMENTOS.pdf, SHA256 4b61bd83d1fbcfd3b7b5c5523cea5923f7d0f65dc4c004eff58e5db38f763c88, quadros10/11/12, três páginas conferidas. Correspondência parcial de preparação, sem ficheiro oficial ou submissão.'; export function normalFormFields(m: { profit?: { lines: Array<{ code: string; amount: string; reference: string }>; }; taxableProfit: FiscalFraction | null; taxableBase: FiscalFraction | null; colecta?: FiscalFraction | null; base?: { deductedLosses: FiscalFraction | null; benefitDeduction: FiscalFraction | null; }; }) { const p = m.taxableProfit, negative = p !== null && BigInt(p.numerator) < 0n; const values: Array<[string, string, FiscalFraction | null, string]> = [ [ '259', 'Prejuízo fiscal', negative ? fraction(-BigInt(p!.numerator), BigInt(p!.denominator)) : null, 'Quadro10; apenas quando há perda conhecida.', ], [ '260', 'Lucro tributável', p && !negative ? p : null, 'Quadro10; não preencher o campo de lucro com uma perda.', ], [ '269', 'Prejuízo fiscal — regime geral', negative ? fraction(-BigInt(p!.numerator), BigInt(p!.denominator)) : null, 'Quadro11; não é uma perda anterior dedutível.', ], [ '270', 'Lucro tributável — regime geral', p && !negative ? p : null, 'Quadro11; manter separados regimes reduzido/isento.', ], [ '276', 'Prejuízos fiscais deduzidos', m.base?.deductedLosses ?? null, 'Quadro11; utilização simulada expressa.', ], [ '277', 'Benefícios fiscais a deduzir', m.base?.benefitDeduction ?? null, 'Quadro11; benefícios positivos pertencem aL7.', ], [ '278', 'Matéria colectável — regime geral', m.taxableBase, 'Quadro11; não é colecta.', ], [ '300', 'Imposto à taxa normal', m.colecta ?? null, 'Quadro12; não é saldo final.', ], [ '200', 'Resultado do exercício', null, 'Resultado financeiro antes de imposto não preenche automaticamente o resultado líquido do formulário; falta reconciliação própria.', ], [ '302', 'Colecta total', null, 'Inclui eventual parcela de taxa reduzida; não presumir a ausência deL7.', ], [ '309–316', 'Liquidação e saldo final', null, 'ÂmbitoL8; retenções, pagamentos, créditos e juros não preenchidos porL6.', ], ]; const m20Fields = ( [ [ 'cost_of_sales', '6.1.1', 'Custo dos inventários vendidos/consumidos', ], ['personnel', '6.2', 'Gastos com pessoal'], ['depreciation', '6.5', 'Amortizações do período'], [ 'interest_expense', '6.9', 'Gastos financeiros — apenas juros da origem', ], ] as const ).map(([key, account, label]) => { const l = m.profit?.lines.find((l) => l.code === key); let value: FiscalFraction | null = null; if (l && /^\d+(?:\.\d{1,2})?$/.test(l.amount)) { const [n, f = ''] = l.amount.split('.'); value = fraction(BigInt(n) * 100n + BigInt(f.padEnd(2, '0')), 100n); } return { account, label, value, display: value ? fractionPresentation(value).decimal + ' MZN' : 'Não preenchido', transcriptionReady: value !== null && BigInt(value.numerator) % BigInt(value.denominator) === 0n, reference: l?.reference ?? '', reason: 'M20 A.1 quadro4, código contabilístico do exemplar, sem inventar número de campo. Valor contabilístico da origem; não é dedução fiscal. Exemplar exige meticais sem centavos; fracções conservadas, sem arredondamento presumido.', }; }); return { source: normalFormSource, m20: { status: 'partial', fields: m20Fields, source: 'Cópia de IRPS-IMOP.xlsx, SHA256 1ceca6f4c6d77853c1b2b445876599b42093175fa89eecf40590180956d83a9e, folhas M,20 e M20A.1; rótulos reutilizados, sem valores de terceiros ou execução das fórmulas do exemplar.', reason: 'M20 A.1: correspondência parcial dos custos contabilísticos. Vendas/serviços, fornecimentos/outros gastos, imposto sobre o rendimento e resultado líquido exigem discriminação própria. Divergência concreta: M20A.1 remete resultado para M22 campo201/quadro8; PDF M22 inventariado usa campo200/quadro10. Sem transporte automático entre essas edições; restantes campos independentes conservados.', }, fields: values.map(([code, label, value, reason]) => { const centExact = value ? (BigInt(value.numerator) * 100n) % BigInt(value.denominator) === 0n : null; return { code, label, value, centExact, display: value ? fractionPresentation(value).decimal + ' MZN' : 'Não preenchido', transcriptionReady: value !== null && centExact === true, reason: reason + (centExact === false ? ' Fracção de cêntimo conservada exactamente; regra de representação fiscal por corresponder, sem arredondamento presumido.' : ''), }; }), }; }