import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import { newOwnName, switchOwnNameTreatment, ownNameConditions, ownNameSpecificConditions, ownNameTreatments, evaluateOwnName, recordOwnNames, saveOwnNameBook, ownNameBook, ownNameCurrent, ownNameReportRows, } from '../../resources/js/lib/iva-own-name.ts'; const frozen = JSON.parse( fs.readFileSync( 'tests/frontend/fixtures/iva-g10-own-name-preserved.json', 'utf8', ), ); const draft = { ...frozen, currency: 'MZN', context_opportunity: 'opportunity:352', project_title: 'SYNTHETIC QA — G10 nome próprio / Manica', }; function row(t) { return { ...newOwnName(t, 'MZN'), treatment: t, description: 'SYNTHETIC QA ' + t, period: '2026-12', origin: 'projected', operationDate: '2026-12-12', operationRef: 'SYNTHETIC QA operação', documentRef: 'SYNTHETIC QA documento/pressuposto', documentDate: '2026-12-12', principalRef: 'SYNTHETIC QA comitente/mandante A', agentRef: 'SYNTHETIC QA agente B', thirdPartyRef: 'SYNTHETIC QA terceiro C', contractRef: 'SYNTHETIC QA contrato/mandato', subjectRef: 'SYNTHETIC QA objecto da operação', firstAmount: '1000.25', secondAmount: '1100.75', firstLegRef: 'SYNTHETIC QA relação1', secondLegRef: 'SYNTHETIC QA relação2', existingBaseRef: 'SYNTHETIC QA referênciaP091preservada', scope: 'SYNTHETIC QA partes/objecto/operação/período', evidence: 'SYNTHETIC QA pressupostos expressos', conditions: Object.fromEntries( [...ownNameConditions, ...ownNameSpecificConditions(t)].map( ([key]) => [key, 'yes'], ), ), }; } for (const rule of ownNameTreatments) void test( rule.branch + '/' + rule.id + ' direction,own facts and original separate amounts', () => { const r = row(rule.id), result = evaluateOwnName(r, 'MZN'); assert.equal(result.status, 'determined'); assert.equal(result.legs.length, 2); assert.deepEqual( result.legs.map((l) => [l.supplier, l.acquirer]), rule.direction === 'sale' ? [ [r.principalRef, r.agentRef], [r.agentRef, r.thirdPartyRef], ] : [ [r.thirdPartyRef, r.agentRef], [r.agentRef, r.principalRef], ], ); assert.deepEqual( result.legs.map((l) => l.consideration), ['1000.25', '1100.75'], ); assert.deepEqual( ['baseAmount', 'vat', 'rate'].map((k) => result[k]), [null, null, null], ); for (const [key] of [ ...ownNameConditions, ...ownNameSpecificConditions(rule.id), ]) { const missing = structuredClone(r); delete missing.conditions[key]; assert.equal( evaluateOwnName(missing, 'MZN').status, 'indeterminate', ); missing.conditions[key] = 'no'; assert.equal( evaluateOwnName(missing, 'MZN').status, 'not_applicable', ); assert.equal(evaluateOwnName(missing, 'MZN').legs.length, 0); } for (const field of [ 'description', 'operationRef', 'documentRef', 'contractRef', 'subjectRef', 'principalRef', 'agentRef', 'thirdPartyRef', 'firstLegRef', 'secondLegRef', 'firstAmount', 'secondAmount', 'scope', 'evidence', ]) assert.equal( evaluateOwnName({ ...r, [field]: '' }, 'MZN').status, 'indeterminate', field, ); for (const patch of [ { operationDate: '2026-11-12' }, { documentDate: '2026-02-30' }, { period: '2025-12' }, { currency: 'USD' }, { firstAmount: '1e3' }, { secondAmount: '-1' }, ]) assert.equal( evaluateOwnName({ ...r, ...patch }, 'MZN').status, 'indeterminate', ); assert.equal( evaluateOwnName({ ...r, currency: 'USD' }, 'USD').legs[0] .currency, 'USD', ); assert.equal( evaluateOwnName({ ...r, firstAmount: '0' }, 'MZN').legs[0] .consideration, '0', ); assert.equal( evaluateOwnName({ ...r, existingBaseRef: '' }, 'MZN').status, 'determined', ); assert.match( result.reason, rule.kind === 'goods' ? /comissário considerado comprador|comitente considerado comprador/ : /adquirente eprestador/, ); }, ); void test('Changing direction/kind keeps references and values but requires own confirmations', () => { const original = row('goods_sale_commission'); const changed = switchOwnNameTreatment( original, 'service_purchase_mandate', ); assert.equal(changed.firstAmount, original.firstAmount); assert.equal(changed.secondAmount, original.secondAmount); assert.equal(changed.existingBaseRef, original.existingBaseRef); assert.equal(changed.principalRef, original.principalRef); assert.equal(changed.conditions.directionConfirmed, undefined); assert.equal(evaluateOwnName(changed, 'MZN').status, 'indeterminate'); changed.conditions = { ...changed.conditions, ...Object.fromEntries( ownNameSpecificConditions(changed.treatment).map(([k]) => [ k, 'yes', ]), ), }; const result = evaluateOwnName(changed, 'MZN'); assert.equal(result.status, 'determined'); assert.equal(result.legs[0].supplier, original.thirdPartyRef); assert.match(result.reason, /Não transformar/); }); void test('Four circuits save/recover/history/report;P091 and all prior books unchanged', () => { const rows = ownNameTreatments.map((t) => row(t.id)); let book = recordOwnNames(draft, { rows }); book = recordOwnNames(draft, book); assert.equal(book.history.length, 1); assert.notEqual(book.result.inputs, book.rows); const next = { ...draft, modelo_a_snapshot: saveOwnNameBook(draft, book) }; assert.deepEqual(ownNameBook(next), book); assert.ok(ownNameCurrent(next, book)); assert.equal(ownNameCurrent({ ...next, currency: 'USD' }, book), false); assert.equal( ownNameCurrent({ ...next, context_opportunity: 'other' }, book), false, ); assert.equal(ownNameReportRows(next).length, 4); assert.ok( ownNameReportRows(next).every((r) => r[2].includes('sem soma das duas relações'), ), ); const store = JSON.parse(next.modelo_a_snapshot); delete store.ownNames; assert.deepEqual(store, JSON.parse(frozen.modelo_a_snapshot)); assert.throws(() => recordOwnNames(next, { rows: [rows[0], rows[0]] })); assert.throws(() => ownNameBook({ ...next, modelo_a_snapshot: JSON.stringify({ ...JSON.parse(next.modelo_a_snapshot), ownNames: [], }), }), ); if (process.env.IVA_OWN_NAME_FIXTURE) fs.writeFileSync( process.env.IVA_OWN_NAME_FIXTURE, JSON.stringify(next, null, 2) + '\n', ); });