255 lines
9.4 KiB
JavaScript
255 lines
9.4 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import {
|
|
newGoodsAllocation,
|
|
switchAllocationTreatment,
|
|
switchAllocationValuation,
|
|
goodsAllocationConditions,
|
|
allocationSpecificConditions,
|
|
goodsAllocationTreatments,
|
|
evaluateGoodsAllocation,
|
|
recordGoodsAllocations,
|
|
saveGoodsAllocationBook,
|
|
goodsAllocationBook,
|
|
goodsAllocationCurrent,
|
|
goodsAllocationReportRows,
|
|
} from '../../resources/js/lib/iva-goods-allocation.ts';
|
|
const frozen = JSON.parse(
|
|
fs.readFileSync(
|
|
'tests/frontend/fixtures/iva-goods-allocation-preserved.json',
|
|
'utf8',
|
|
),
|
|
);
|
|
const draft = {
|
|
...frozen,
|
|
currency: 'MZN',
|
|
context_opportunity: 'opportunity:353',
|
|
project_title: 'SYNTHETIC QA — Afectação de bens / Manica',
|
|
};
|
|
function row(t) {
|
|
const cost = ['free_goods_transfer', 'fixed_asset_20a'].includes(t);
|
|
return {
|
|
...newGoodsAllocation(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',
|
|
goodsRef: 'SYNTHETIC QA conjunto de bens/elementos',
|
|
deductionRef:
|
|
'SYNTHETIC QA dedução histórica parcial,bens/elementos identificados',
|
|
destinationRef: 'SYNTHETIC QA destino/finalidade/sector/activo eâmbito',
|
|
exceptionRef:
|
|
'SYNTHETIC QA factos fora da excepção de amostras/ofertas,não limiar inventado',
|
|
asset20aRef: 'SYNTHETIC QA bens20(1)a/activoimobilizado',
|
|
valuationMode: cost ? 'cost' : 'acquisition',
|
|
acquisitionPrice: '1000.25',
|
|
acquisitionRef: 'SYNTHETIC QA preço aquisição reportado ao momento',
|
|
currentCost: '800.15',
|
|
costRef: 'SYNTHETIC QA custo reportado ao momento',
|
|
valuationDate: '2026-12-12',
|
|
valuationScope:
|
|
'SYNTHETIC QA conjunto identificado/preço ao momento,semIVA',
|
|
evidence: 'SYNTHETIC QA pressupostos expressos',
|
|
conditions: {
|
|
...Object.fromEntries(
|
|
[
|
|
...goodsAllocationConditions,
|
|
...allocationSpecificConditions(t),
|
|
].map(([key]) => [key, 'yes']),
|
|
),
|
|
valuationConfirmed: 'yes',
|
|
acquisitionUnavailable: 'yes',
|
|
},
|
|
};
|
|
}
|
|
for (const rule of goodsAllocationTreatments)
|
|
void test(
|
|
'P124/P088 ' + rule.id + ' ownfacts,historicaldeduction and base',
|
|
() => {
|
|
const r = row(rule.id),
|
|
result = evaluateGoodsAllocation(r, 'MZN');
|
|
assert.equal(result.status, 'determined');
|
|
assert.equal(result.classification, 'assimilated_goods');
|
|
assert.equal(
|
|
result.baseAmount,
|
|
r.valuationMode === 'cost' ? '800.15' : '1000.25',
|
|
);
|
|
assert.equal(result.vat, null);
|
|
assert.equal(result.rate, null);
|
|
for (const [key] of [
|
|
...goodsAllocationConditions,
|
|
...allocationSpecificConditions(rule.id),
|
|
]) {
|
|
const missing = structuredClone(r);
|
|
delete missing.conditions[key];
|
|
assert.equal(
|
|
evaluateGoodsAllocation(missing, 'MZN').classification,
|
|
'indeterminate',
|
|
);
|
|
missing.conditions[key] = 'no';
|
|
assert.equal(
|
|
evaluateGoodsAllocation(missing, 'MZN').status,
|
|
'not_applicable',
|
|
);
|
|
assert.equal(
|
|
evaluateGoodsAllocation(missing, 'MZN').baseAmount,
|
|
null,
|
|
);
|
|
}
|
|
for (const field of [
|
|
'description',
|
|
'operationRef',
|
|
'documentRef',
|
|
'goodsRef',
|
|
'deductionRef',
|
|
'destinationRef',
|
|
'evidence',
|
|
...(rule.clause === 'e' ? ['exceptionRef'] : []),
|
|
...(rule.id === 'fixed_asset_20a' ? ['asset20aRef'] : []),
|
|
])
|
|
assert.equal(
|
|
evaluateGoodsAllocation({ ...r, [field]: '' }, 'MZN')
|
|
.classification,
|
|
'indeterminate',
|
|
field,
|
|
);
|
|
for (const patch of [
|
|
{ operationDate: '2026-11-12' },
|
|
{ documentDate: '2026-02-30' },
|
|
{ period: '2025-12' },
|
|
])
|
|
assert.equal(
|
|
evaluateGoodsAllocation({ ...r, ...patch }, 'MZN').status,
|
|
'indeterminate',
|
|
);
|
|
},
|
|
);
|
|
void test('P124classification independentof missingP088base;no newdeduction and no temporaryservice substitution', () => {
|
|
const r = row('permanent_private_goods');
|
|
for (const patch of [
|
|
{ valuationMode: '' },
|
|
{ acquisitionPrice: '' },
|
|
{ valuationDate: '2026-12-11' },
|
|
{ valuationScope: '' },
|
|
{ currency: 'USD' },
|
|
{ acquisitionRef: '' },
|
|
]) {
|
|
const result = evaluateGoodsAllocation({ ...r, ...patch }, 'MZN');
|
|
assert.equal(result.status, 'indeterminate');
|
|
assert.equal(result.classification, 'assimilated_goods');
|
|
assert.equal(result.baseAmount, null);
|
|
}
|
|
assert.match(evaluateGoodsAllocation(r, 'MZN').reason, /não nova dedução/);
|
|
assert.match(evaluateGoodsAllocation(r, 'MZN').reason, /P089/);
|
|
const noPrior = structuredClone(r);
|
|
noPrior.conditions.priorDeduction = 'no';
|
|
assert.equal(
|
|
evaluateGoodsAllocation(noPrior, 'MZN').classification,
|
|
'not_applicable',
|
|
);
|
|
assert.equal(evaluateGoodsAllocation(noPrior, 'MZN').baseAmount, null);
|
|
});
|
|
void test('P088costfallback explicit,currentmoment,hidden retention and exact amounts', () => {
|
|
const r = row('free_goods_transfer');
|
|
for (const flag of ['', 'no']) {
|
|
const result = evaluateGoodsAllocation(
|
|
{
|
|
...r,
|
|
conditions: { ...r.conditions, acquisitionUnavailable: flag },
|
|
},
|
|
'MZN',
|
|
);
|
|
assert.equal(result.classification, 'assimilated_goods');
|
|
assert.equal(result.baseAmount, null);
|
|
}
|
|
assert.equal(
|
|
evaluateGoodsAllocation({ ...r, acquisitionPrice: '999999' }, 'MZN')
|
|
.baseAmount,
|
|
'800.15',
|
|
);
|
|
assert.equal(
|
|
evaluateGoodsAllocation({ ...r, currentCost: '0' }, 'MZN').baseAmount,
|
|
'0.00',
|
|
);
|
|
assert.equal(
|
|
evaluateGoodsAllocation({ ...r, currentCost: '0.000001' }, 'MZN')
|
|
.baseAmount,
|
|
'0.000001',
|
|
);
|
|
for (const v of ['', '-1', '1e3', '1,5'])
|
|
assert.equal(
|
|
evaluateGoodsAllocation({ ...r, currentCost: v }, 'MZN').baseAmount,
|
|
null,
|
|
);
|
|
const acquisition = switchAllocationValuation(r, 'acquisition');
|
|
assert.equal(acquisition.currentCost, r.currentCost);
|
|
assert.equal(acquisition.acquisitionPrice, r.acquisitionPrice);
|
|
assert.equal(acquisition.conditions.valuationConfirmed, '');
|
|
assert.equal(evaluateGoodsAllocation(acquisition, 'MZN').baseAmount, null);
|
|
acquisition.conditions.valuationConfirmed = 'yes';
|
|
assert.equal(
|
|
evaluateGoodsAllocation(acquisition, 'MZN').baseAmount,
|
|
'1000.25',
|
|
);
|
|
const changed = switchAllocationTreatment(r, 'fixed_asset_20a');
|
|
assert.equal(changed.exceptionRef, r.exceptionRef);
|
|
assert.equal(changed.asset20aRef, r.asset20aRef);
|
|
assert.equal(changed.conditions.fixedAsset, undefined);
|
|
assert.equal(
|
|
evaluateGoodsAllocation(changed, 'MZN').classification,
|
|
'indeterminate',
|
|
);
|
|
});
|
|
void test('Sharedoperation under2requirements persists once;all previous books/history/report preserved', () => {
|
|
const rows = goodsAllocationTreatments.map((t) => row(t.id));
|
|
let book = recordGoodsAllocations(draft, { rows });
|
|
book = recordGoodsAllocations(draft, book);
|
|
assert.equal(book.history.length, 1);
|
|
assert.notEqual(book.result.inputs, book.rows);
|
|
const next = {
|
|
...draft,
|
|
modelo_a_snapshot: saveGoodsAllocationBook(draft, book),
|
|
};
|
|
assert.deepEqual(goodsAllocationBook(next), book);
|
|
assert.ok(goodsAllocationCurrent(next, book));
|
|
assert.equal(
|
|
goodsAllocationCurrent({ ...next, currency: 'USD' }, book),
|
|
false,
|
|
);
|
|
assert.equal(
|
|
goodsAllocationCurrent({ ...next, context_opportunity: 'other' }, book),
|
|
false,
|
|
);
|
|
assert.equal(goodsAllocationReportRows(next).length, 4);
|
|
assert.ok(
|
|
goodsAllocationReportRows(next).every((r) =>
|
|
r[1].includes('P124/baseP088'),
|
|
),
|
|
);
|
|
const store = JSON.parse(next.modelo_a_snapshot);
|
|
delete store.regulatedGoodsAllocations;
|
|
assert.deepEqual(store, JSON.parse(frozen.modelo_a_snapshot));
|
|
assert.throws(() =>
|
|
recordGoodsAllocations(next, { rows: [rows[0], rows[0]] }),
|
|
);
|
|
assert.throws(() =>
|
|
goodsAllocationBook({
|
|
...next,
|
|
modelo_a_snapshot: JSON.stringify({
|
|
...JSON.parse(next.modelo_a_snapshot),
|
|
regulatedGoodsAllocations: [],
|
|
}),
|
|
}),
|
|
);
|
|
if (process.env.IVA_ALLOCATION_FIXTURE)
|
|
fs.writeFileSync(
|
|
process.env.IVA_ALLOCATION_FIXTURE,
|
|
JSON.stringify(next, null, 2) + '\n',
|
|
);
|
|
});
|