110 lines
25 KiB
TypeScript
110 lines
25 KiB
TypeScript
import {irpcSpecialVersion,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={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={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<string,unknown>){
|
||
const p=restoreBusinessPlanSnapshot(String(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<string,unknown>){const raw=rawNormalFinancialSource(d),input=normalBook(d).periodsFx;return input?mapNormalPeriodFx(d,input,raw).source:raw;}
|
||
function store(d:Record<string,unknown>){const s=JSON.parse(String(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?.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||![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<string,unknown>):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<string,unknown>,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<string,unknown>,b:NormalBook){return JSON.stringify([b.special?irpcSpecialVersion: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.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<string,unknown>,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<string,unknown>,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<string,FiscalFraction>(),groups=new Map<string,NormalModel['rows']>();
|
||
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 profit=b.profit?closeNormalProfit(b.profit,source,rows,accountingProfit,additions,contextReady,schedules,costs,depreciation):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?irpcSpecialVersion: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,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<string,unknown>,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<string,unknown>):Array<[string,string,string,string]>{try{const b=normalBook(d),r=b.result;if(!r)return[];const m=r.model;return[['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<string,string>)[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<string,unknown>){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'<!doctype html><html lang="pt"><meta charset="utf-8"><title>IRPC normal — ajustamentos</title><h1>IRPC — L6 de referência e tratamentos L7 quando qualificados</h1><p>Projecção pelos pressupostos do plano. Não é demonstração contabilística,liquidação ou Modelo20/22.</p>'+normalReportRows(d).map(r=>'<section><h2>'+esc(r[1])+'</h2><p>'+esc(r[2])+'</p><small>'+esc(r[3])+'</small></section>').join('')+'</html>';}
|