Files
Investidor-V2/resources/js/lib/iva-fx-selection.ts
T

149 lines
5.3 KiB
TypeScript

export const fxTempoSource =
'Orientação operacional expressa da Tempo,25/10/2026:compra de moeda→taxa de compra;venda→taxa de venda;operações comerciais→taxa média de referência do Banco de Moçambique na data de submissão indicada. Interpretar compra/venda pela perspectiva do boletim. Excepções fiscais específicas conservam disposição,data,taxa e âmbito;sem recálculo automático dos históricos.';
export type FxSelection = {
issuer?: string;
nature: '' | 'purchase' | 'sale' | 'commercial';
submissionDate: string;
bulletinDate: string;
bulletinRef: string;
source: string;
sourceCurrency: string;
direction: '' | 'mzn_per_unit' | 'units_per_mzn';
bulletinPerspective: string;
relation: '' | 'same' | 'opposite';
buy: string;
sell: string;
mean: string;
mode: 'general' | 'exception';
exceptionRef: string;
exceptionScope: string;
exceptionDate: string;
exceptionRate: string;
applied?: {
column: string;
rate: string;
date: string;
source: string;
selectedAt: string;
selectionKey: string;
};
};
export function emptyFxSelection(): FxSelection {
return {
nature: '',
submissionDate: '',
bulletinDate: '',
bulletinRef: '',
source: '',
sourceCurrency: '',
direction: '',
bulletinPerspective: '',
relation: '',
buy: '',
sell: '',
mean: '',
mode: 'general',
exceptionRef: '',
exceptionScope: '',
exceptionDate: '',
exceptionRate: '',
};
}
function date(s: string) {
const d = new Date(s + 'T00:00:00Z');
return (
/^\d{4}-\d{2}-\d{2}$/.test(s) &&
Number.isFinite(d.getTime()) &&
d.toISOString().slice(0, 10) === s
);
}
function positive(s: string) {
return /^\d{1,15}(\.\d{1,8})?$/.test(s) && Number(s) > 0;
}
export function fxSelectionKey(s: FxSelection): string {
const { applied, ...inputs } = s;
void applied;
return JSON.stringify(inputs);
}
export function selectFx(s: FxSelection): NonNullable<FxSelection['applied']> {
if (
!['purchase', 'sale', 'commercial'].includes(s.nature) ||
!date(s.submissionDate) ||
!s.bulletinRef.trim() ||
!s.source.trim() ||
!s.sourceCurrency.trim() ||
!['mzn_per_unit', 'units_per_mzn'].includes(s.direction)
)
throw Error(
'Indique natureza,data de submissão,moeda,direcção e boletim/fonte;sem usar taxa actual ouzero por defeito.',
);
let column = '',
rate = '',
day = '';
if (s.mode === 'exception') {
if (
!s.exceptionRef.trim() ||
!s.exceptionScope.trim() ||
!date(s.exceptionDate) ||
!positive(s.exceptionRate)
)
throw Error(
'Excepção exige disposição fiscal,âmbito,data e taxa identificados;os restantes percursos continuam disponíveis.',
);
column = 'exception';
rate = s.exceptionRate;
day = s.exceptionDate;
} else {
if (
s.mode !== 'general' ||
s.bulletinDate !== s.submissionDate ||
!date(s.bulletinDate)
)
throw Error(
'Use boletim da data de submissão indicada;ausência de boletim não autoriza usar a taxa mais recente.',
);
if (s.nature === 'commercial') {
let officialUrl = false;
try {
officialUrl = ['bancomoc.mz', 'www.bancomoc.mz'].includes(
new URL(s.source).hostname,
);
} catch {
/* An identified official local exemplar does not require another validation. */
}
if (!officialUrl && s.issuer?.trim() !== 'Banco de Moçambique')
throw Error(
'Operação comercial usa taxa média de referência do Banco de Moçambique;identifique a origemBM do boletim arquivado ou a suaURL,sem exigir nova validação da fonte aceite.',
);
column = 'mean';
rate = s.mean;
} else {
if (
!s.bulletinPerspective.trim() ||
!['same', 'opposite'].includes(s.relation)
)
throw Error(
'Identifique a perspectiva indicada no boletim e a relação com a natureza declarada;sem inverter a operação do cliente por presunção.',
);
column =
(s.nature === 'purchase') === (s.relation === 'same')
? 'buy'
: 'sell';
rate = column === 'buy' ? s.buy : s.sell;
}
day = s.submissionDate;
}
if (!positive(rate))
throw Error(
'Taxa da coluna aplicável ausente ou inválida;não calcular média a partir de compra/venda.',
);
return {
column,
rate,
date: day,
source: `${s.bulletinRef};${s.source};perspectiva:${s.bulletinPerspective || 'média de referênciaBM'};natureza:${s.nature};relação:${s.relation || 'não aplicável à média'};${s.mode === 'exception' ? s.exceptionRef + ';' + s.exceptionScope : fxTempoSource}`,
selectedAt: new Date().toISOString(),
selectionKey: fxSelectionKey(s),
};
}