32 lines
4.0 KiB
TypeScript
32 lines
4.0 KiB
TypeScript
import {selectFx,fxSelectionKey,type FxSelection} from './iva-fx-selection.ts';
|
|
import {currencyEventBook,newCurrencyEvent} from './iva-currency-events.ts';
|
|
import {convertCents} from './business-financial-plan.ts';
|
|
import {decimalFraction,fraction,type FiscalFraction} from './fiscal-rational.ts';
|
|
import {closureFingerprint} from './iva-closure.ts';
|
|
|
|
export type IrpcFxLink={id:string;fingerprint:string};
|
|
export type IrpcFxAmount={currency:string;fxSelection?:FxSelection;fxLink?:IrpcFxLink};
|
|
export type IrpcFxEvidence={originalAmount:string;originalCurrency:string;convertedCurrency:'MZN';column:string;rate:string;date:string;direction:string;source:string;selectionKey:string;rounding:string};
|
|
export function irpcFxSources(d:Record<string,unknown>){return currencyEventBook(d).rows.filter(r=>r.fxSelection).map(r=>({id:r.id,label:`${r.description||r.id} — ${r.sourceCurrency} — ${r.fxSelection?.applied?.rate??'por aplicar'}`,fingerprint:closureFingerprint(r),selection:structuredClone(r.fxSelection!)}));}
|
|
export function irpcFxDependency(d:Record<string,unknown>,input:IrpcFxAmount):string{
|
|
if(input.currency==='MZN'||!input.fxLink)return'';
|
|
const source=irpcFxSources(d).find(s=>s.id===input.fxLink!.id);
|
|
return !source?'Selecção cambial de origem indisponível;vínculo e histórico conservados.':source.fingerprint!==input.fxLink.fingerprint?'Selecção cambial de origem alterada;compare a nova selecção antes de a aplicar às retenções.':'';
|
|
}
|
|
export function irpcFxRow(id:string,input:IrpcFxAmount){const s=input.fxSelection,a=s?.applied;return{...newCurrencyEvent(id,'MZN'),sourceCurrency:input.currency,fxSelection:s,fxDirection:s?.direction??'',fxRate:a?.rate??'',fxDate:a?.date??'',fxSource:a?.source??''};}
|
|
export function convertIrpcAmount(d:Record<string,unknown>,amount:string,input:IrpcFxAmount):{value:FiscalFraction;evidence:IrpcFxEvidence|null}{
|
|
const original=decimalFraction(amount);if(input.currency==='MZN')return{value:original,evidence:null};
|
|
const issue=irpcFxDependency(d,input);if(issue)throw Error(issue);
|
|
const s=input.fxSelection;if(!s)throw Error('Reutilize ou aplique a selecção cambial já entregue:moeda original,boletim,coluna,data e sentido;sem taxa actual por defeito.');
|
|
const selected=selectFx(s),a=s.applied;
|
|
if(!a||a.selectionKey!==fxSelectionKey(s)||a.rate!==selected.rate||a.date!==selected.date||a.source!==selected.source||a.column!==selected.column||s.sourceCurrency!==input.currency)throw Error('Selecção cambial alterada,não confirmada ou de outra moeda;use a conferência e confirmação existentes,sem recalcular históricos.');
|
|
const [whole,places='']=selected.rate.split('.'),rate=BigInt(whole+places.padEnd(8,'0')),scale=100000000n;
|
|
const micro=BigInt(original.numerator)*1000000n/BigInt(original.denominator);
|
|
const converted=s.direction==='mzn_per_unit'?convertCents(micro,rate,scale):convertCents(micro,scale,rate);
|
|
return{value:fraction(converted,1000000n),evidence:{originalAmount:amount,originalCurrency:input.currency,convertedCurrency:'MZN',column:selected.column,rate:selected.rate,date:selected.date,direction:s.direction,source:selected.source,selectionKey:a.selectionKey,rounding:'Mesmo convertCents entregue;conversão à unidade de0,000001MZN;apresentação separada,não regra nova de arredondamento do imposto.'}};
|
|
}
|
|
|
|
export function irpcFxEvidenceLabel(e:IrpcFxEvidence):string{const column=({buy:'Compra',sell:'Venda',mean:'Média de referência BM',exception:'Excepção fiscal identificada'} as Record<string,string>)[e.column]??e.column;const direction=e.direction==='mzn_per_unit'?'MZN por '+e.originalCurrency:e.originalCurrency+' por MZN';return`Original: ${e.originalAmount} ${e.originalCurrency}; coluna: ${column}; taxa: ${e.rate}; cotação: ${direction}; data cambial: ${e.date}; fonte: ${e.source.split(';perspectiva:')[0]}. Montante e selecção originais conservados.`;}
|
|
|
|
export function updateIrpcFx(input:IrpcFxAmount,selection:FxSelection):Partial<IrpcFxAmount>{return{fxSelection:selection,fxLink:input.fxSelection&&fxSelectionKey(input.fxSelection)===fxSelectionKey(selection)?input.fxLink:undefined};}
|