172 lines
6.1 KiB
JavaScript
172 lines
6.1 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import {
|
|
emptyFxSelection,
|
|
selectFx,
|
|
fxTempoSource,
|
|
} from '../../resources/js/lib/iva-fx-selection.ts';
|
|
import {
|
|
newCurrencyEvent,
|
|
evaluateCurrencyEvent,
|
|
recordCurrencyEvents,
|
|
saveCurrencyEventBook,
|
|
currencyEventBook,
|
|
currencyEventCurrent,
|
|
} from '../../resources/js/lib/iva-currency-events.ts';
|
|
const selection = () => ({
|
|
...emptyFxSelection(),
|
|
nature: 'commercial',
|
|
submissionDate: '2026-10-23',
|
|
bulletinDate: '2026-10-23',
|
|
bulletinRef: 'SYNTHETIC QA boletimBM de referência,23/10/2026',
|
|
source: 'https://www.bancomoc.mz/pt/areas-de-actuacao/mercados/mercado-cambial/',
|
|
sourceCurrency: 'USD',
|
|
direction: 'mzn_per_unit',
|
|
bulletinPerspective: 'SYNTHETIC QA bancos perante público',
|
|
relation: 'same',
|
|
buy: '63.27',
|
|
sell: '64.54',
|
|
mean: '63.91',
|
|
});
|
|
for (const [nature, relation, column, rate] of [
|
|
['purchase', 'same', 'buy', '63.27'],
|
|
['sale', 'same', 'sell', '64.54'],
|
|
['purchase', 'opposite', 'sell', '64.54'],
|
|
['sale', 'opposite', 'buy', '63.27'],
|
|
['commercial', '', 'mean', '63.91'],
|
|
])
|
|
void test(`P104 ${nature}/${relation || 'médiaBM'} escolhe ${column} sem inverter cliente`, () => {
|
|
const result = selectFx({ ...selection(), nature, relation });
|
|
assert.equal(result.column, column);
|
|
assert.equal(result.rate, rate);
|
|
assert.equal(result.date, '2026-10-23');
|
|
});
|
|
void test('P104 falta de boletim/data/perspectiva não autoriza taxa actual,média própria ou coluna oposta presumida', () => {
|
|
for (const patch of [
|
|
{ bulletinDate: '2026-10-22' },
|
|
{ mean: '' },
|
|
{ source: 'https://example.com/cambio' },
|
|
{ submissionDate: '2026-02-30' },
|
|
{ nature: '' },
|
|
{ nature: 'purchase', relation: '' },
|
|
{ nature: 'sale', bulletinPerspective: '' },
|
|
])
|
|
assert.throws(() => selectFx({ ...selection(), ...patch }));
|
|
assert.equal(
|
|
selectFx({
|
|
...selection(),
|
|
source: 'SYNTHETIC QA exemplar oficial arquivado',
|
|
issuer: 'Banco de Moçambique',
|
|
}).column,
|
|
'mean',
|
|
);
|
|
const exception = {
|
|
...selection(),
|
|
mode: 'exception',
|
|
exceptionRef: 'SYNTHETIC QA disposição fiscal específica',
|
|
exceptionScope: 'SYNTHETIC QA apenas operaçãoA',
|
|
exceptionDate: '2026-10-20',
|
|
exceptionRate: '62.12345678',
|
|
};
|
|
assert.equal(selectFx(exception).rate, '62.12345678');
|
|
assert.equal(selectFx(exception).date, '2026-10-20');
|
|
assert.throws(() => selectFx({ ...exception, exceptionRef: '' }));
|
|
});
|
|
const frozen = JSON.parse(
|
|
fs.readFileSync(
|
|
'tests/frontend/fixtures/iva-offer-workflow.json',
|
|
'utf8',
|
|
),
|
|
),
|
|
prior = JSON.parse(frozen.modelo_a_snapshot),
|
|
draft = {
|
|
...frozen,
|
|
context_opportunity: 'opportunity:365',
|
|
project_title: 'SYNTHETIC QA — P104 operacional / Manica',
|
|
};
|
|
function row() {
|
|
const s = selection(),
|
|
applied = selectFx(s);
|
|
return {
|
|
...newCurrencyEvent('commercial', 'MZN'),
|
|
treatment: 'foreign_scenario',
|
|
description: 'SYNTHETIC QA operação comercial',
|
|
period: '2026-10',
|
|
origin: 'effective',
|
|
operationRef: 'SYNTHETIC QA operação já declarada',
|
|
documentRef: 'SYNTHETIC QA factura',
|
|
documentDate: '2026-10-23',
|
|
scope: 'SYNTHETIC QA operaçãoA',
|
|
evidence: 'SYNTHETIC QA factos',
|
|
factsConfirmed: 'yes',
|
|
amount: '100.25',
|
|
sourceCurrency: 'USD',
|
|
fxRate: applied.rate,
|
|
fxDate: applied.date,
|
|
fxSource: applied.source,
|
|
fxRuleRef: fxTempoSource,
|
|
fxDirection: s.direction,
|
|
fxSelection: { ...s, applied },
|
|
};
|
|
}
|
|
void test('P104 original/convertido,direcção e taxa guardados;semIVA,nova dedução ou alteração automática', () => {
|
|
const r = row(),
|
|
v = evaluateCurrencyEvent(r);
|
|
assert.equal(v.status, 'determined');
|
|
assert.equal(v.scenarioAmount, '6406.9775');
|
|
assert.equal(v.baseAmount, null);
|
|
assert.equal(v.vat, null);
|
|
const reverse = {
|
|
...r,
|
|
fxDirection: 'units_per_mzn',
|
|
fxSelection: { ...r.fxSelection, direction: 'units_per_mzn' },
|
|
};
|
|
const applied = selectFx(reverse.fxSelection);
|
|
reverse.fxSelection.applied = applied;
|
|
reverse.fxSource = applied.source;
|
|
assert.equal(evaluateCurrencyEvent(reverse).scenarioAmount, '1.568612');
|
|
assert.equal(
|
|
evaluateCurrencyEvent({ ...r, fxRate: '999' }).status,
|
|
'indeterminate',
|
|
);
|
|
assert.equal(
|
|
evaluateCurrencyEvent({
|
|
...r,
|
|
fxSelection: { ...r.fxSelection, submissionDate: '2026-10-24' },
|
|
}).status,
|
|
'indeterminate',
|
|
);
|
|
});
|
|
void test('P104 versões antigas permanecem condicionadas e127fechos/P058/ofertas permanecem iguais', () => {
|
|
const r = row(),
|
|
legacy = { ...r };
|
|
delete legacy.fxSelection;
|
|
assert.equal(evaluateCurrencyEvent(legacy).status, 'conditional');
|
|
let b = recordCurrencyEvents(draft, { rows: [r] });
|
|
const saved = structuredClone(b.result);
|
|
const changed = { ...b, rows: [{ ...r, amount: '200' }] };
|
|
assert.equal(currencyEventCurrent(draft, changed), false);
|
|
assert.deepEqual(b.result, saved);
|
|
b = recordCurrencyEvents(draft, changed);
|
|
assert.deepEqual(b.history[0], saved);
|
|
draft.modelo_a_snapshot = saveCurrencyEventBook(draft, b);
|
|
assert.deepEqual(currencyEventBook(draft), b);
|
|
const next = JSON.parse(draft.modelo_a_snapshot);
|
|
assert.deepEqual(
|
|
Object.fromEntries(
|
|
Object.entries(prior).filter(([k]) => k !== 'currencyEvents'),
|
|
),
|
|
Object.fromEntries(
|
|
Object.entries(next).filter(([k]) => k !== 'currencyEvents'),
|
|
),
|
|
);
|
|
for (const [k, v] of Object.entries(prior.currencyEvents ?? {}))
|
|
assert.deepEqual(next.currencyEvents[k], v);
|
|
if (process.env.IVA_FX_SELECTION_FIXTURE)
|
|
fs.writeFileSync(
|
|
'tests/frontend/fixtures/iva-fx-selection.json',
|
|
JSON.stringify(draft),
|
|
);
|
|
});
|