import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import { newSecondhand, switchSecondhandTreatment, switchSecondhandCategory, secondhandConditions, secondhandCategoryConditions, secondhandTreatments, secondhandCategories, evaluateSecondhand, recordSecondhands, saveSecondhandBook, secondhandBook, secondhandCurrent, secondhandReportRows, } from '../../resources/js/lib/iva-secondhand.ts'; const frozen = JSON.parse( fs.readFileSync( 'tests/frontend/fixtures/iva-secondhand-preserved.json', 'utf8', ), ); const draft = { ...frozen, currency: 'MZN', context_opportunity: 'opportunity:354', project_title: 'SYNTHETIC QA — Segunda mão / Manica', }; function row(t, c) { return { ...newSecondhand(t + '-' + c, 'MZN'), treatment: t, category: c, description: 'SYNTHETIC QA ' + t + '/' + c, period: '2026-12', origin: 'projected', operationDate: '2026-12-12', operationRef: 'SYNTHETIC QA operação', documentRef: 'SYNTHETIC QA documento/pressuposto', documentDate: '2026-12-12', goodsRef: 'SYNTHETIC QA bem individual ' + t + c, operatorRef: 'SYNTHETIC QA estatuto operador', categoryRef: 'SYNTHETIC QA características/definição3(6)', artSubtype: 'print', editionCount: '200', ceramicUniqueSigned: 'yes', collectionSubtype: 'postal', postalNotCirculating: 'yes', ageYears: '101', sourceType: 'private', sourceRef: 'SYNTHETIC QA origem particular nacional', domesticPurchase: 'yes', regimeChoice: 'margin', domesticTaxable: 'yes', grossSale: '1160', purchasePrice: '1000', purchaseRef: 'SYNTHETIC QA preço compra justificado', grossInvoice: '1300', hammerPrice: '1000', principalCommission: '100', auctionRef: 'SYNTHETIC QA organizador emnome próprio/contrato comissão/leilão', principalRef: 'SYNTHETIC QA comitente/líquido', ownVat: '55.17', ownVatRef: 'SYNTHETIC QA IVApróprio transmissão identificado', scope: 'SYNTHETIC QA bem/partes/operação/período', evidence: 'SYNTHETIC QA pressupostos expressos', conditions: { ...Object.fromEntries( [ ...secondhandConditions, ...secondhandCategoryConditions(c), ].map(([k]) => [k, 'yes']), ), regimeConfirmed: 'yes', compositionConfirmed: 'yes', }, }; } for (const t of secondhandTreatments) for (const c of secondhandCategories) void test( t.branch + '/P125 ' + c.id + ' ownfacts,eligibleorigin and specificformula', () => { const r = row(t.id, c.id), result = evaluateSecondhand(r, 'MZN'); assert.equal(result.status, 'determined'); assert.equal(result.classification, 'qualified_goods'); assert.equal( result.baseAmount, t.id === 'reseller_margin' ? '138.00' : '344.83', ); assert.equal(result.vat, null); assert.equal(result.rate, null); for (const [k] of [ ...secondhandConditions, ...secondhandCategoryConditions(c.id), ]) { const missing = structuredClone(r); delete missing.conditions[k]; assert.equal( evaluateSecondhand(missing, 'MZN').classification, 'indeterminate', ); missing.conditions[k] = 'no'; assert.equal( evaluateSecondhand(missing, 'MZN').classification, 'not_applicable', ); assert.equal( evaluateSecondhand(missing, 'MZN').baseAmount, null, ); } for (const field of [ 'description', 'operationRef', 'documentRef', 'goodsRef', 'operatorRef', 'categoryRef', 'scope', 'evidence', ]) assert.equal( evaluateSecondhand({ ...r, [field]: '' }, 'MZN') .classification, 'indeterminate', field, ); for (const patch of [ { period: '2025-12' }, { operationDate: '2026-11-12' }, { documentDate: '2026-02-30' }, { currency: 'USD' }, { sourceType: 'legacy_35_42' }, { sourceType: 'other' }, { sourceRef: '' }, { domesticPurchase: '' }, { domesticTaxable: '' }, ]) assert.equal( evaluateSecondhand({ ...r, ...patch }, 'MZN') .baseAmount, null, ); assert.equal( evaluateSecondhand( { ...r, sourceType: 'margin_reseller' }, 'MZN', ).status, 'determined', ); assert.equal( evaluateSecondhand( { ...r, sourceType: 'exempt_9_12_e' }, 'MZN', ).status, 'determined', ); if (t.id === 'reseller_margin') { assert.equal( evaluateSecondhand( { ...r, regimeChoice: 'general' }, 'MZN', ).baseAmount, null, ); for (const field of [ 'grossSale', 'purchasePrice', 'purchaseRef', ]) assert.equal( evaluateSecondhand({ ...r, [field]: '' }, 'MZN') .baseAmount, null, ); } else { for (const field of [ 'grossInvoice', 'hammerPrice', 'principalCommission', 'ownVat', 'ownVatRef', 'auctionRef', 'principalRef', ]) assert.equal( evaluateSecondhand({ ...r, [field]: '' }, 'MZN') .baseAmount, null, ); assert.equal( evaluateSecondhand( { ...r, principalCommission: '1001' }, 'MZN', ).baseAmount, null, ); assert.equal( evaluateSecondhand({ ...r, ownVat: '401' }, 'MZN') .baseAmount, null, ); } }, ); void test('P092 rounding towholeunit,zero vsunknown and negative individualmargin no compensation', () => { const r = row('reseller_margin', 'used_goods'); assert.equal( evaluateSecondhand({ ...r, grossSale: '1000.58' }, 'MZN').baseAmount, '1.00', ); assert.equal( evaluateSecondhand({ ...r, grossSale: '1000.579999' }, 'MZN') .baseAmount, '0.00', ); assert.equal( evaluateSecondhand({ ...r, grossSale: '1000' }, 'MZN').baseAmount, '0.00', ); const loss = evaluateSecondhand({ ...r, grossSale: '900' }, 'MZN'); assert.equal(loss.grossMargin, '-100.00'); assert.equal(loss.baseAmount, '0.00'); assert.match(loss.reason, /sem compensar/); assert.equal( evaluateSecondhand({ ...r, purchasePrice: '' }, 'MZN').baseAmount, null, ); const positive = evaluateSecondhand(r, 'MZN'); assert.equal(positive.baseAmount, '138.00'); assert.equal(positive.baseDivisorRate, '16%'); assert.equal(positive.vat, null); }); void test('P125 boundaries and both switches preserve hidden values withoutautomaticregime', () => { const art = row('reseller_margin', 'art'); assert.equal( evaluateSecondhand({ ...art, editionCount: '201' }, 'MZN') .classification, 'not_applicable', ); assert.equal( evaluateSecondhand({ ...art, editionCount: '' }, 'MZN').classification, 'indeterminate', ); assert.equal( evaluateSecondhand( { ...art, artSubtype: 'ceramic', ceramicUniqueSigned: '' }, 'MZN', ).baseAmount, null, ); const antique = row('reseller_margin', 'antique'); assert.equal( evaluateSecondhand({ ...antique, ageYears: '100' }, 'MZN') .classification, 'not_applicable', ); assert.equal( evaluateSecondhand({ ...antique, ageYears: '100.1' }, 'MZN') .classification, 'qualified_goods', ); const changed = switchSecondhandTreatment(art, 'auction_margin'); assert.equal(changed.grossSale, art.grossSale); assert.equal(changed.ownVat, art.ownVat); assert.equal(changed.conditions.compositionConfirmed, ''); assert.equal(evaluateSecondhand(changed, 'MZN').baseAmount, null); const cat = switchSecondhandCategory(art, 'collection'); assert.equal(cat.editionCount, '200'); assert.equal(cat.conditions.collectionNature, undefined); }); void test('All8individualgoods persistwithhistory/report;all priorbooks preserved,noaggregateoffset', () => { const rows = secondhandTreatments.flatMap((t) => secondhandCategories.map((c) => row(t.id, c.id)), ); let book = recordSecondhands(draft, { rows }); book = recordSecondhands(draft, book); assert.equal(book.history.length, 1); const next = { ...draft, modelo_a_snapshot: saveSecondhandBook(draft, book), }; assert.deepEqual(secondhandBook(next), book); assert.ok(secondhandCurrent(next, book)); assert.equal(secondhandCurrent({ ...next, currency: 'USD' }, book), false); assert.equal(secondhandReportRows(next).length, 8); const store = JSON.parse(next.modelo_a_snapshot); delete store.regulatedSecondhands; assert.deepEqual(store, JSON.parse(frozen.modelo_a_snapshot)); assert.throws(() => recordSecondhands(next, { rows: [rows[0], rows[0]] })); assert.throws(() => secondhandBook({ ...next, modelo_a_snapshot: JSON.stringify({ ...JSON.parse(next.modelo_a_snapshot), regulatedSecondhands: [], }), }), ); if (process.env.IVA_SECONDHAND_FIXTURE) fs.writeFileSync( process.env.IVA_SECONDHAND_FIXTURE, JSON.stringify(next, null, 2) + '\n', ); });