import { inputText } from './input-text.ts'; import { calculateEconomicInclusions, validateEconomicInclusions, type EconomicInclusions, } from './irpc-normal-economic.ts'; import { irpcSpecialCombinationsVersion } from './irpc-special-combinations.ts'; import { irpcSpecialVersion, irpcSpecialMonthlyVersion, irpcSpecialStartPeriodVersion, irpcSpecialCessationPriorVersion, specialCalculationVersion, validateSpecial, calculateSpecial, specialReportRows, specialDependency, type SpecialInput, type SpecialModel, } from './irpc-special.ts'; import { normalFormPreparationVersion, validateNormalFormPreparation, prepareNormalForms, normalFormPreparationReport, type NormalFormPreparationInput, type NormalFormPreparationModel, } from './irpc-normal-form-preparation.ts'; import { normalPeriodFxVersion, validateNormalPeriodFx, mapNormalPeriodFx, normalPeriodFxDependency, normalPeriodFxReport, type NormalPeriodFxInput, type NormalPeriodFxModel, } from './irpc-normal-period-fx.ts'; import { normalDepreciationVersion, validateNormalDepreciation, calculateNormalDepreciation, normalDepreciationReport, type NormalDepreciationInput, type NormalDepreciationModel, } from './irpc-normal-depreciation.ts'; import { normalCostsVersion, validateNormalCosts, calculateNormalCosts, normalCostReport, type NormalCostsInput, type NormalCostsModel, } from './irpc-normal-costs.ts'; import { normalFormFields } from './irpc-normal-forms.ts'; import { normalSchedulesVersion, normalSchedulePeriod, validateSchedules, calculateSchedules, type NormalSchedules, type SchedulesModel, } from './irpc-normal-schedules.ts'; import { normalAssessmentVersion, validateNormalAssessment, calculateNormalAssessment, type NormalAssessmentInput, type NormalAssessmentModel, } from './irpc-normal-assessment.ts'; import { normalBaseVersion, validateNormalBase, calculateNormalBase, type NormalBaseInput, type NormalBaseModel, } from './irpc-normal-base.ts'; import { normalProfitVersion, validateNormalProfit, closeNormalProfit, type NormalProfitInput, type NormalProfitModel, } from './irpc-normal-profit.ts'; import { forecastBusinessPlan, restoreBusinessPlanSnapshot, } from './business-financial-plan.ts'; import { buildFinancialFiscalBridge } from './financial-fiscal-bridge.ts'; import { acquisitionScope } from './iva-acquisitions.ts'; import { addFractions, decimalFraction, fraction, fractionPresentation, type FiscalFraction, } from './fiscal-rational.ts'; export const irpcNormalVersion = 'IRPC-normal-adjustments/1'; export const irpcNormalSource = 'CIRPC Lei34/2007 art17(1),PDF6/p768(60):resultado e correcções;art36(1)c,PDF10/p768(64):multas e encargos por infracções sem origem contratual,incluindo juros compensatórios. Lei4/2012 PDF2 altera17(6)/acrescenta36(1)o;Lei19/2013 PDF2 mantém36(1)a–o e acrescenta p/q. Lei12/2025 altera3/5/20/62/67/75,adita61-A e revoga39/47/75(2)/76;sem alteração deste recorte36(1)c. Não é apuramento integral nem Modelo20/22.'; export type NormalAdjustment = { id: string; sourceCode: string; document: string; line: string; amount: string; rule: '' | 'noncontractual_fine' | 'compensatory_interest' | 'other'; included: '' | 'yes' | 'no'; reference: string; }; export type NormalBook = { economicInclusions?: EconomicInclusions; version: 1; year: string; mappingReference: string; scope: '' | 'resident_general' | 'ep_general' | 'other'; scopeReference: string; rows: NormalAdjustment[]; special?: SpecialInput; profit?: NormalProfitInput; base?: NormalBaseInput; assessment?: NormalAssessmentInput; schedules?: NormalSchedules; costs?: NormalCostsInput; depreciation?: NormalDepreciationInput; forms?: NormalFormPreparationInput; periodsFx?: NormalPeriodFxInput; result?: NormalRecord; history?: NormalRecord[]; }; export type NormalModel = { economicInclusions?: ReturnType; special?: SpecialModel; formPreparation?: NormalFormPreparationModel; periodsFx?: NormalPeriodFxModel; depreciation?: NormalDepreciationModel; costs?: NormalCostsModel; fiscalPeriod?: { kind: string; from: string; to: string; reference: string; pending: string[]; }; schedules?: SchedulesModel; currentLoss?: FiscalFraction | null; assessment?: NormalAssessmentModel; colecta?: FiscalFraction | null; base?: NormalBaseModel; profit?: NormalProfitModel; version: string; year: string; currency: string; origin: 'projected'; accountingProfit: FiscalFraction | null; additions: FiscalFraction; partialAdjustedResult: FiscalFraction | null; taxableProfit: FiscalFraction | null; taxableBase: FiscalFraction | null; tax: null; rows: Array< NormalAdjustment & { status: 'calculated' | 'indeterminate' | 'outside_scope'; adjustment: FiscalFraction | null; reason: string; } >; pending: string[]; source: string; }; type NormalRecord = { key: string; createdAt: string; model: NormalModel }; export const newNormalBook = (): NormalBook => ({ version: 1, year: '', mappingReference: '', scope: '', scopeReference: '', rows: [], }); export const newNormalAdjustment = (id: string): NormalAdjustment => ({ id, sourceCode: '', document: '', line: '', amount: '', rule: '', included: '', reference: '', }); export function rawNormalFinancialSource(d: Record) { const p = restoreBusinessPlanSnapshot( inputText(d.business_plan_snapshot ?? ''), ); if (!p) return null; const forecast = forecastBusinessPlan(p), bridge = buildFinancialFiscalBridge(forecast); return { ...bridge, periods: forecast.rows.length, origin: 'projected' as const, scope: acquisitionScope(d), }; } export function normalFinancialSource(d: Record) { const raw = rawNormalFinancialSource(d), input = normalBook(d).periodsFx; return input ? mapNormalPeriodFx(d, input, raw).source : raw; } function store(d: Record) { const s = JSON.parse( inputText(d.modelo_a_snapshot || '{"version":1,"books":{}}'), ); if ( s.version !== 1 || !s.books || typeof s.books !== 'object' || Array.isArray(s.books) ) throw Error('Livros fiscais não recuperáveis;originais preservados.'); return s; } export function validateNormal(b: NormalBook) { if (b?.economicInclusions !== undefined) validateEconomicInclusions(b.economicInclusions); if (b?.special !== undefined) validateSpecial(b.special); if (b?.forms !== undefined) validateNormalFormPreparation(b.forms); if (b?.periodsFx !== undefined) validateNormalPeriodFx(b.periodsFx); if (b?.depreciation !== undefined) validateNormalDepreciation(b.depreciation); if (b?.costs !== undefined) validateNormalCosts(b.costs); if (b?.schedules !== undefined) validateSchedules(b.schedules); if (b?.assessment !== undefined) validateNormalAssessment(b.assessment); if (b?.base !== undefined) validateNormalBase(b.base); if (b?.profit !== undefined) validateNormalProfit(b.profit); if ( !b || b.version !== 1 || ['year', 'mappingReference', 'scopeReference'].some( (k) => typeof b[k as 'year'] !== 'string', ) || !['', 'resident_general', 'ep_general', 'other'].includes(b.scope) || !Array.isArray(b.rows) || (b.history !== undefined && !Array.isArray(b.history)) ) throw Error('Preparação IRPC normal não recuperável.'); for (const r of b.rows) if ( !r || Object.keys(newNormalAdjustment('')).some( (k) => typeof r[k as keyof NormalAdjustment] !== 'string', ) || ![ '', 'noncontractual_fine', 'compensatory_interest', 'other', ].includes(r.rule) || !['', 'yes', 'no'].includes(r.included) ) throw Error( 'Ajustamento IRPC não recuperável;original conservado.', ); if (new Set(b.rows.map((r) => r.id)).size !== b.rows.length) throw Error('Identificador de ajustamento duplicado.'); for (const r of [b.result, ...(b.history ?? [])]) if ( r && (!r.model || ![ irpcSpecialCombinationsVersion, irpcSpecialStartPeriodVersion, irpcSpecialCessationPriorVersion, irpcSpecialMonthlyVersion, irpcSpecialVersion, irpcNormalVersion, normalProfitVersion, normalBaseVersion, normalAssessmentVersion, normalSchedulesVersion, normalCostsVersion, normalDepreciationVersion, normalPeriodFxVersion, normalFormPreparationVersion, ].includes(r.model.version) || typeof r.key !== 'string' || typeof r.createdAt !== 'string' || !Array.isArray(r.model.rows) || !Array.isArray(r.model.pending)) ) throw Error('Histórico IRPC normal não recuperável.'); } export function normalBook(d: Record): NormalBook { const m = store(d).irpcNormal; if (m === undefined) return newNormalBook(); if (!m || typeof m !== 'object' || Array.isArray(m)) throw Error('Mapa IRPC normal não recuperável.'); const b = m[acquisitionScope(d)] ?? newNormalBook(); validateNormal(b); return b; } export function saveNormalBook(d: Record, b: NormalBook) { validateNormal(b); const s = store(d); if ( s.irpcNormal !== undefined && (!s.irpcNormal || typeof s.irpcNormal !== 'object' || Array.isArray(s.irpcNormal)) ) throw Error('Mapa IRPC normal não recuperável.'); return JSON.stringify({ ...s, irpcNormal: { ...s.irpcNormal, [acquisitionScope(d)]: b }, }); } function key(d: Record, b: NormalBook) { return JSON.stringify([ b.special ? specialCalculationVersion(b.special) : b.forms ? normalFormPreparationVersion : b.periodsFx ? normalPeriodFxVersion : b.depreciation ? normalDepreciationVersion : b.costs ? normalCostsVersion : b.schedules ? normalSchedulesVersion : b.assessment ? normalAssessmentVersion : b.base ? normalBaseVersion : b.profit ? normalProfitVersion : irpcNormalVersion, acquisitionScope(d), d.business_plan_snapshot ?? '', b.year, b.mappingReference, b.scope, b.scopeReference, b.rows, ...(b.profit ? [b.profit] : []), ...(b.economicInclusions ? [b.economicInclusions] : []), ...(b.base ? [b.base] : []), ...(b.assessment ? [b.assessment] : []), ...(b.schedules ? [b.schedules] : []), ...(b.costs ? [b.costs] : []), ...(b.depreciation ? [b.depreciation] : []), ...(b.periodsFx ? [b.periodsFx, normalPeriodFxDependency(d, b.periodsFx)] : []), ...(b.forms ? [b.forms] : []), ...(b.special ? [b.special, specialDependency(d)] : []), ]); } export function normalCurrent(d: Record, b: NormalBook) { return !!b.result && b.result.key === key(d, b); } const zero = () => fraction(0n, 1n); const cents = (s: string) => { if (!/^-?\d+(?:\.\d{1,2})?$/.test(s)) throw Error('Montante MZN inválido;máximo dois decimais.'); const v = decimalFraction(s.startsWith('-') ? s.slice(1) : s); return s.startsWith('-') ? fraction(-BigInt(v.numerator), BigInt(v.denominator)) : v; }; const integerCents = (v: FiscalFraction) => (BigInt(v.numerator) * 100n) / BigInt(v.denominator); export function calculateNormal( d: Record, b: NormalBook, ): NormalModel { validateNormal(b); const pending: string[] = [], raw = rawNormalFinancialSource(d), periodsFx = b.periodsFx ? mapNormalPeriodFx(d, b.periodsFx, raw) : undefined, source = periodsFx ? periodsFx.source : raw, fiscalPeriod = periodsFx ? { kind: b.periodsFx!.kind, from: b.periodsFx!.from, to: b.periodsFx!.to, reference: b.periodsFx!.reference, pending: periodsFx.pending, } : b.schedules ? normalSchedulePeriod(b.schedules, b.scope) : undefined; if ( periodsFx && b.periodsFx!.kind === 'annual' && b.periodsFx!.from !== '2026-01-01' ) { const formal = b.schedules ? normalSchedulePeriod(b.schedules, b.scope) : null; if ( !formal || formal.pending.length || formal.from !== b.periodsFx!.from || formal.to !== b.periodsFx!.to ) fiscalPeriod!.pending.push( 'Ano diferente do civil exige o período autorizado/comunicado já documentado nos mapas,com as mesmas datas.', ); } if (fiscalPeriod) pending.push(...fiscalPeriod.pending); if (!source) pending.push('Falta plano financeiro reconciliado.'); if (source?.currency !== 'MZN') pending.push( 'Ponte em MZN necessária;sem conversão fiscal anual presumida.', ); if ( !/^2026$/.test(b.year) || !b.mappingReference.trim() || source?.periods !== (periodsFx?.months ?? 12) ) pending.push( 'Documente correspondência dos 12 meses do plano ao exercício2026;outros exercícios/horizontes permanecem separados.', ); if ( !( b.scope === 'resident_general' || (b.schedules && b.scope === 'ep_general') ) || !b.scopeReference.trim() ) pending.push( 'Identifique sujeito residente no regime geral,fora de transparência/regimes especiais;EP e outros âmbitos não abrangidos.', ); const rows: NormalModel['rows'] = b.rows.map((r) => ({ ...structuredClone(r), status: 'indeterminate', adjustment: null, reason: '', })); const amounts = new Map(), groups = new Map(); for (const r of rows) { const line = source?.accountingLines.find( (l) => l.code === r.sourceCode, ); const issue: string[] = []; if (!r.document.trim() || !r.line.trim() || !r.reference.trim()) issue.push( 'Identifique documento,linha e suporte da qualificação.', ); if (!line || line.accountingEffect !== 'expense') issue.push('Seleccione linha de custo existente na ponte.'); if (r.included !== 'yes') issue.push( 'Custo incluído no resultado de partida por demonstrar;sem acréscimo duplicado de montante já excluído.', ); try { const a = cents(r.amount); if (integerCents(a) <= 0n) throw Error(); amounts.set(r.id, a); } catch { issue.push( 'Montante positivo MZN,até dois decimais,por identificar.', ); } if (r.rule === 'other') { r.status = 'outside_scope'; issue.push( 'Outro ajustamento por corresponder;sem regra inventada.', ); } else if (!r.rule) issue.push( 'Seleccione natureza da multa não contratual ou dos juros compensatórios;juros comerciais/penalidades contratuais não equiparados.', ); if ( rows.some( (q) => q !== r && q.document.trim() === r.document.trim() && q.line.trim() === r.line.trim() && r.document.trim() && r.line.trim(), ) ) issue.push( 'Documento/linha repetidos;reconcilie sem duplicar o encargo.', ); if (line) { const g = groups.get(line.code) ?? []; g.push(r); groups.set(line.code, g); } r.reason = issue.join(' '); } for (const [code, group] of groups) { const line = source!.accountingLines.find((l) => l.code === code)!; const requested = addFractions( group.map((r) => amounts.get(r.id) ?? zero()), ); if (integerCents(requested) > integerCents(cents(line.total))) for (const r of group) r.reason += ' Parcelas excedem o custo da linha de origem;sem dupla contagem.'; } for (const r of rows) { if (!r.reason) { r.status = 'calculated'; r.adjustment = amounts.get(r.id)!; r.reason = 'Acréscimo integral de custo incluído no resultado de partida,qualificado pelo utilizador como ' + (r.rule === 'noncontractual_fine' ? 'multa/encargo de infracção sem origem contratual' : 'juros compensatórios') + ';CIRPC36(1)c. Projecção pelos pressupostos do plano.'; } else pending.push(r.document + ': ' + r.reason); } const additions = addFractions( rows.flatMap((r) => (r.adjustment ? [r.adjustment] : [])), ); const accountingProfit = source?.currency === 'MZN' ? cents(source.accountingProfitBeforeTax) : null; const contextReady = (!fiscalPeriod || fiscalPeriod.pending.length === 0) && !!source && source.currency === 'MZN' && source.periods === (periodsFx?.months ?? 12) && b.year === '2026' && !!b.mappingReference.trim() && (b.scope === 'resident_general' || (!!b.schedules && b.scope === 'ep_general')) && !!b.scopeReference.trim(); if (rows.length === 0 && !b.profit) pending.push( 'Sem ajustamentos documentados;ausência de registos não prova inexistência de correcções.', ); if (!b.profit) pending.push( 'L6 parcial:resultado ajustado apenas destes acréscimos;restantes custos/variações patrimoniais,amortizações,perdas,deduções e colecta por tratar. Lucro tributável,matéria colectável e imposto indeterminados.', ); const depreciation = b.depreciation ? calculateNormalDepreciation( b.depreciation, source, fiscalPeriod ?? { from: b.year + '-01-01', to: b.year + '-12-31', }, [ ...b.rows, ...(b.schedules?.rows ?? []), ...(b.costs?.rows ?? []), ], !!periodsFx?.short, ) : undefined; const schedules = b.schedules ? calculateSchedules( b.schedules, source, rows, depreciation ? { value: depreciation.adjustment, pending: depreciation.pending, } : undefined, ) : undefined; const costs = b.costs ? calculateNormalCosts( b.costs, source, [...b.rows, ...(b.schedules?.rows ?? [])], fiscalPeriod ?? { from: '2026-01-01', to: b.year + '-12-31' }, !!periodsFx?.short, ) : undefined; const economicInclusions = b.economicInclusions ? calculateEconomicInclusions( b.economicInclusions, b.base?.subjectNuit ?? '', b.year, ) : undefined; if ( economicInclusions && (b.special || b.periodsFx || (b.schedules && b.schedules.period.kind !== 'calendar') || b.scope !== 'resident_general') ) economicInclusions.pending.push( 'Inclusão económica automática delimitada à base ordinária residente/ano civil2026/MZN; este âmbito exige correspondência própria.', ); const profit = b.profit ? closeNormalProfit( b.profit, source, rows, accountingProfit, additions, contextReady, schedules, costs, depreciation, economicInclusions, ) : undefined; if (profit && periodsFx) profit.source = 'CIRPC17/18/20/21/22/24/36;' + periodsFx.rule + ' L7 mantém benefícios/casos especiais;L8 mantém saldo final.'; if (profit) pending.push( ...profit.pending, ...(!b.base ? [ 'Matéria colectável,perdas reportadas,deduções e imposto não apurados neste percurso.', ] : []), ); const base = b.base ? calculateNormalBase( b.base, profit?.taxableProfit ?? null, b.year, b.schedules?.currentLossReference, !!periodsFx?.short, ) : undefined; if (base) pending.push( ...base.pending, ...(!b.assessment ? [ 'Colecta/imposto a pagar,créditos anuais eM20/M22 não determinados;matéria colectável não é imposto.', ] : []), ); const assessment = b.assessment ? calculateNormalAssessment(b.assessment, base?.taxableBase ?? null, { year: b.year, scope: b.scope, extended: !!b.schedules || !!periodsFx, short: !!periodsFx?.short, currency: source?.currency ?? '', ...(fiscalPeriod ? { period: { from: fiscalPeriod.from, to: fiscalPeriod.to } } : {}), }) : undefined; if (assessment) pending.push(...assessment.pending, assessment.finalBalance.reason); const currentLoss = b.schedules && profit?.taxableProfit && BigInt(profit.taxableProfit.numerator) < 0n ? fraction( -BigInt(profit.taxableProfit.numerator), BigInt(profit.taxableProfit.denominator), ) : null; const model: NormalModel = { ...(periodsFx ? { periodsFx } : {}), ...(depreciation ? { depreciation } : {}), ...(costs ? { costs } : {}), ...(schedules ? { schedules, currentLoss, fiscalPeriod } : {}), ...(assessment ? { assessment, colecta: assessment.colecta } : {}), ...(base ? { base } : {}), ...(profit ? { profit } : {}), version: b.special ? specialCalculationVersion(b.special) : b.forms ? normalFormPreparationVersion : b.periodsFx ? normalPeriodFxVersion : b.depreciation ? normalDepreciationVersion : b.costs ? normalCostsVersion : b.schedules ? normalSchedulesVersion : b.assessment ? normalAssessmentVersion : b.base ? normalBaseVersion : b.profit ? normalProfitVersion : irpcNormalVersion, year: b.year, currency: source?.currency ?? '', origin: 'projected', accountingProfit, additions, partialAdjustedResult: contextReady && rows.length > 0 && rows.every((r) => r.status === 'calculated') && accountingProfit ? addFractions([accountingProfit, additions]) : null, taxableProfit: profit?.taxableProfit ?? null, ...(economicInclusions ? { economicInclusions } : {}), taxableBase: base?.taxableBase ?? null, tax: null, rows, pending, source: irpcNormalSource + (periodsFx ? ' ' + periodsFx.rule : b.schedules ? ' Mapas complementares: CIRPC18/21/22/24/36; Decreto72/2013 art3–7. EP: CIRPC15(1)c, apenas resultado imputável ao estabelecimento no regime geral, anual2026/MZN.' : ''), }; if (b.special) model.special = calculateSpecial(d, b, model, source); if (b.forms) { model.formPreparation = prepareNormalForms(b.forms, model, source); model.pending.push( ...model.formPreparation.pending.map( (p) => 'Preparação M20/M22: ' + p, ), ); } return model; } export function recordNormal( d: Record, b: NormalBook, now = new Date().toISOString(), ): NormalBook { return { ...b, history: b.result ? [...(b.history ?? []), structuredClone(b.result)] : (b.history ?? []), result: { key: key(d, b), createdAt: now, model: calculateNormal(d, b), }, }; } const money = (v: FiscalFraction | null) => v ? fractionPresentation(v).decimal + ' MZN' : 'Indeterminado'; function normalBaseReport( m: NormalBaseModel, ): Array<[string, string, string, string]> { return [ [ 'IRPC normal — L6', 'Matéria colectável — deduções/perdas', `Lucro tributável:${money(m.taxableProfit)};perdas propostas elegíveis:${money(m.requestedLosses)};perdas deduzidas nesta simulação:${money(m.deductedLosses)};benefícios deduzidos:${money(m.benefitDeduction)};matéria colectável simulada:${money(m.taxableBase)};imposto a pagar:Indeterminado. ${m.pending.join(' | ')}`, `NUIT:${m.input.subjectNuit};${m.input.identityReference};método:${m.input.method === 'direct' ? 'Declarativo/directo' : m.input.method === 'indirect' ? 'Indirecto' : 'Por identificar'};${m.input.methodReference};perdas:${m.input.lossesState === 'absent' ? 'Ausência declarada' : m.input.lossesState === 'known' ? 'Conhecidas' : 'Informação em falta'};${m.input.lossesReference};objecto:${m.input.unchangedObject === 'yes' ? 'Sem alteração declarada' : m.input.unchangedObject === 'no' ? 'Alteração declarada' : 'Por esclarecer'};${m.input.objectReference};${m.source}`, ], [ 'IRPC normal — L6', 'Benefícios à matéria colectável', `Estado:${m.input.benefits.state === 'known' ? 'Valor conhecido' : m.input.benefits.state === 'absent' ? 'Ausência declarada' : 'Informação em falta'};montante original:${m.input.benefits.amount || 'Não preenchido'};dedução:${money(m.benefitDeduction)};não inclui créditos à colecta ou retenções sofridas.`, m.input.benefits.reference, ], ...m.losses.map( (l) => [ 'IRPC normal — L6', 'Perda fiscal de ' + l.year, `Estado:${l.status === 'eligible' ? 'Elegível neste recorte' : l.status === 'excluded' ? 'Excluída neste exercício' : 'Indeterminada'};apurada:${l.assessed};utilizada antes:${l.previouslyUsed};saldo:${money(l.available)};utilização proposta:${l.requested};dedução nesta simulação:${money(l.deducted)};saldo aritmético posterior:${money(l.remaining)};último exercício ordinário:${l.lastOrdinaryYear ?? 'Por determinar'};${l.reason}`, `NUIT:${l.subjectNuit};${l.reference};sem consumo do saldo oficial nem reporte automático.`, ] as [string, string, string, string], ), ]; } function normalAssessmentReport( m: NormalAssessmentModel, ): Array<[string, string, string, string]> { return [ [ 'IRPC normal — L6', 'Colecta — regime normal', `Matéria colectável:${money(m.taxableBase)};taxa:${m.rate ? m.rate.numerator + '%' : 'Por identificar'};colecta simulada:${money(m.colecta)};${m.classification};${m.centExact === false ? 'Valor racional exacto com fracção de cêntimo;sem arredondamento fiscal aplicado.' : ''}${m.pending.join(' | ')}`, `Exercício:${m.period.year};período:${m.period.from}–${m.period.to};${m.input.reference};${m.rule};${m.source}`, ], [ 'IRPC normal — L6', 'Saldo final — âmbito L8', 'A pagar:Indeterminado;a recuperar:Indeterminado. ' + m.finalBalance.reason, 'Sequência:lucro tributável → matéria colectável → colecta → saldo final;retenções,pagamentos e créditos anuais reservados aL8.', ], ]; } export function normalReportRows( d: Record, ): Array<[string, string, string, string]> { try { const b = normalBook(d), r = b.result; if (!r) return []; const m = r.model; return [ ...(m.economicInclusions?.effects.map( (e) => [ 'IRPC normal — L6', 'Inclusão/correcção económica — ' + e.id, 'Parcela reconciliada: ' + money(e.amount) + '; efeito com sinal: ' + money(e.effect), e.reason, ] as [string, string, string, string], ) ?? []), [ 'IRPC normal — L6', 'Ajustamentos ao resultado financeiro', `Exercício:${m.year};resultado contabilístico antes de imposto:${money(m.accountingProfit)};acréscimos demonstrados:${money(m.additions)};resultado parcial ajustado:${money(m.partialAdjustedResult)};${m.profit ? 'lucro tributável simulado:' + money(m.taxableProfit) + (m.base ? ';matéria colectável simulada:' + money(m.taxableBase) + (m.assessment ? ';colecta simulada:' + money(m.colecta ?? null) + ';saldo final a pagar ou recuperar:Indeterminado (L8).' : ';imposto a pagar:Indeterminado.') : ';matéria colectável/imposto:Indeterminados.') : 'lucro tributável/matéria colectável/imposto:Indeterminados.'} Projecção com pressupostos;${m.pending.join(' | ')}`, `${normalCurrent(d, b) ? 'Entradas correspondentes' : 'Histórico;origem ou entradas alteradas'};${r.createdAt};${m.source}`, ], ...(m.special ? specialReportRows(m.special) : []), ...(m.formPreparation ? normalFormPreparationReport(m.formPreparation) : []), ...(m.periodsFx ? normalPeriodFxReport(m.periodsFx) : []), ...(m.depreciation ? normalDepreciationReport(m.depreciation) : []), ...(m.costs ? normalCostReport(m.costs) : []), ...(m.base ? normalBaseReport(m.base) : []), ...(m.assessment ? normalAssessmentReport(m.assessment) : []), ...(m.schedules ? [ [ 'IRPC normal — L6', 'Mapas complementares', `Efeito líquido adicional:${money(m.schedules.net)};perda fiscal do próprio exercício:${money(m.currentLoss ?? null)};sem reporte ou consumo automático. ${m.schedules.pending.join(' | ')}`, m.schedules.source, ] as [string, string, string, string], ...m.schedules.effects.map((e) => { const i = m.schedules!.input.rows.find( (r) => r.id === e.id, )!; return [ 'IRPC normal — L6', i.document + '/' + i.line, `Montante:${money(e.amount)};efeito:${money(e.effect)};${e.reason}`, `${i.reference};exercício de imputação:${i.fiscalYear || 'Não aplicável'}`, ] as [string, string, string, string]; }), ...m.schedules.assets.map( (a) => [ 'IRPC normal — L6', a.description, `Depreciação contabilística:${money(a.accountingValue)};quota fiscal:${money(a.fiscal)};acréscimo:${money(a.adjustment)};${a.reason}`, `${a.tableReference};${a.reference}`, ] as [string, string, string, string], ), [ 'IRPC normal — L6', 'Juros financeiros ordinários', `Remanescente aceite no recorte:${money(m.schedules.interest)};não corresponde a juros de mora/compensatórios L5.`, m.schedules.input.interest.reference, ] as [string, string, string, string], ] : []), ...(m.schedules && !m.formPreparation ? [ ...normalFormFields(m).fields.map( (f) => [ 'IRPC normal — L6', 'M22 campo ' + f.code + ' — ' + f.label, `${f.display};exacto:${f.value ? f.value.numerator + '/' + f.value.denominator : 'Indeterminado'};transcrição:${f.transcriptionReady ? 'Preparação parcial com valor exacto ao cêntimo' : 'Pendente ou sem aplicação'};${f.reason}`, normalFormFields(m).source, ] as [string, string, string, string], ), ...normalFormFields(m).m20.fields.map( (f) => [ 'IRPC normal — L6', 'M20 A.1 — conta ' + f.account + ' — ' + f.label, `${f.display};representação em meticais inteiros:${f.transcriptionReady ? 'Exacta' : 'Pendente'};${f.reason}`, `${f.reference};${normalFormFields(m).m20.source}`, ] as [string, string, string, string], ), [ 'IRPC normal — L6', 'M20 — correspondência', normalFormFields(m).m20.reason, normalFormFields(m).m20.source, ] as [string, string, string, string], ] : []), ...m.rows.map( (r) => [ 'IRPC normal — L6', r.document + '/' + r.line, `${{ calculated: 'Calculado neste recorte', indeterminate: 'Indeterminado', outside_scope: 'Fora deste recorte' }[r.status]};montante:${r.amount};acréscimo:${money(r.adjustment)};${r.reason}`, `Origem:${({ revenue: 'Receitas', cost_of_sales: 'Custo das vendas', operating_expenses: 'Despesas operacionais', personnel: 'Pessoal', depreciation: 'Depreciação contabilística', interest_expense: 'Juros reconhecidos' } as Record)[r.sourceCode] ?? 'Por corresponder'};incluído:${r.included === 'yes' ? 'Sim' : r.included === 'no' ? 'Não' : 'Por demonstrar'};suporte:${r.reference}`, ] as [string, string, string, string], ), ...(m.profit ? [ ...m.profit.lines.map( (l) => [ 'IRPC normal — L6', 'Conferência: ' + l.label, `Estado:${l.state === 'known' ? 'Valor conhecido' : l.state === 'absent' ? 'Ausência declarada' : 'Informação em falta'};origem:${l.amount};acréscimos:${money(l.addedBack)};remanescente:${money(l.remainder)};${l.reason}`, l.reference, ] as [string, string, string, string], ), ...m.profit.topics.map( (t) => [ 'IRPC normal — L6', t.label, `Estado:${t.state === 'known' ? 'Valor conhecido' : t.state === 'absent' ? 'Ausência declarada' : 'Informação em falta'};montante:${money(t.value)};${t.reason}`, t.reference, ] as [string, string, string, string], ), [ 'IRPC normal — L6', 'Âmbito da conferência', m.profit.input.completenessReference, m.profit.source, ] as [string, string, string, string], ] : []), ]; } catch (e) { return [ [ 'IRPC normal — L6', 'Não recuperável', String(e), 'Originais conservados.', ], ]; } } export function normalHtml(d: Record) { const b = normalBook(d); if (!normalCurrent(d, b)) throw Error('Calcule e guarde as entradas actuais antes de exportar.'); const esc = (s: string) => s.replace( /[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', })[c]!, ); return ( 'IRPC normal — ajustamentos

IRPC — L6 de referência e tratamentos L7 quando qualificados

Projecção pelos pressupostos do plano. Não é demonstração contabilística,liquidação ou Modelo20/22.

' + normalReportRows(d) .map( (r) => '

' + esc(r[1]) + '

' + esc(r[2]) + '

' + esc(r[3]) + '
', ) .join('') + '' ); }