Files
Investidor-V2/tests/frontend/iva-secondhand.test.mjs
T

314 lines
11 KiB
JavaScript

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',
);
});