243 lines
8.4 KiB
JavaScript
243 lines
8.4 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import {
|
|
newGoodsPresumption,
|
|
presumptionConditions,
|
|
goodsPresumptionTreatments,
|
|
evaluateGoodsPresumption,
|
|
recordGoodsPresumptions,
|
|
saveGoodsPresumptionBook,
|
|
goodsPresumptionBook,
|
|
goodsPresumptionCurrent,
|
|
goodsPresumptionReportRows,
|
|
switchPresumptionTreatment,
|
|
} from '../../resources/js/lib/iva-goods-presumptions.ts';
|
|
const frozen = JSON.parse(
|
|
fs.readFileSync(
|
|
'tests/frontend/fixtures/iva-goods-presumptions-preserved.json',
|
|
'utf8',
|
|
),
|
|
);
|
|
const draft = {
|
|
...frozen,
|
|
context_opportunity: 'opportunity:355',
|
|
project_title: 'SYNTHETIC QA — Consignação e presunções / Manica',
|
|
};
|
|
function row(t) {
|
|
return {
|
|
...newGoodsPresumption(t),
|
|
treatment: t,
|
|
description: 'SYNTHETIC QA ' + t,
|
|
period: '2026-12',
|
|
origin: 'projected',
|
|
operationDate: '2026-12-12',
|
|
operationRef: 'SYNTHETIC QA operação/conferência',
|
|
documentRef: 'SYNTHETIC QA documento/pressuposto',
|
|
documentDate: '2026-12-12',
|
|
goodsRef: 'SYNTHETIC QA conjunto individual',
|
|
scope: 'SYNTHETIC QA todos os estabelecimentos/período/conjunto',
|
|
evidence: 'SYNTHETIC QA factos/pressupostos expressos',
|
|
deliveryDate: '2026-06-01',
|
|
recipientRef: 'SYNTHETIC QA destinatário/entrega',
|
|
inventoryRef: 'SYNTHETIC QA levantamento todos estabelecimentos',
|
|
productionRef: 'SYNTHETIC QA volume de produção/comparação',
|
|
quantity: '125.123456',
|
|
quantityUnit: 'kg',
|
|
contraryProof: 'no',
|
|
contraryProofRef: '',
|
|
conditions: Object.fromEntries(
|
|
presumptionConditions(t).map(([k]) => [k, 'yes']),
|
|
),
|
|
};
|
|
}
|
|
for (const rule of goodsPresumptionTreatments)
|
|
void test(
|
|
rule.branch +
|
|
' ' +
|
|
rule.id +
|
|
' own facts/references and no fiscal amount',
|
|
() => {
|
|
const r = row(rule.id),
|
|
result = evaluateGoodsPresumption(r);
|
|
assert.equal(result.status, 'determined');
|
|
assert.equal(
|
|
result.classification,
|
|
rule.id === 'consignment_180'
|
|
? 'consignment_transmission'
|
|
: rule.id === 'found_goods'
|
|
? 'presumed_acquisition'
|
|
: 'presumed_transmission',
|
|
);
|
|
assert.equal(result.baseAmount, null);
|
|
assert.equal(result.vat, null);
|
|
assert.equal(result.rate, null);
|
|
for (const [k] of presumptionConditions(rule.id)) {
|
|
const missing = structuredClone(r);
|
|
delete missing.conditions[k];
|
|
assert.equal(
|
|
evaluateGoodsPresumption(missing).status,
|
|
'indeterminate',
|
|
);
|
|
missing.conditions[k] = 'no';
|
|
assert.equal(
|
|
evaluateGoodsPresumption(missing).status,
|
|
'not_applicable',
|
|
);
|
|
}
|
|
for (const field of [
|
|
'description',
|
|
'operationRef',
|
|
'documentRef',
|
|
'goodsRef',
|
|
'scope',
|
|
'evidence',
|
|
...(rule.id === 'consignment_180'
|
|
? ['deliveryDate', 'recipientRef']
|
|
: ['inventoryRef']),
|
|
...(rule.id === 'excess_consumption'
|
|
? ['productionRef', 'quantity', 'quantityUnit']
|
|
: []),
|
|
])
|
|
assert.equal(
|
|
evaluateGoodsPresumption({ ...r, [field]: '' }).status,
|
|
'indeterminate',
|
|
field,
|
|
);
|
|
for (const patch of [
|
|
{ operationDate: '2026-02-30' },
|
|
{ documentDate: '2026-02-30' },
|
|
{ period: '2025-12' },
|
|
{ period: '2026-11' },
|
|
])
|
|
assert.equal(
|
|
evaluateGoodsPresumption({ ...r, ...patch }).status,
|
|
'indeterminate',
|
|
);
|
|
},
|
|
);
|
|
void test('P123 exact180-day boundary,yearcrossing and chronology', () => {
|
|
const r = row('consignment_180');
|
|
const day = (n) =>
|
|
new Date(Date.parse(r.deliveryDate + 'T00:00:00Z') + n * 86400000)
|
|
.toISOString()
|
|
.slice(0, 10);
|
|
for (const n of [179, 180, 181]) {
|
|
const d = day(n),
|
|
v = evaluateGoodsPresumption({
|
|
...r,
|
|
operationDate: d,
|
|
period: d.slice(0, 7),
|
|
});
|
|
assert.equal(v.elapsedDays, n);
|
|
assert.equal(v.deadline, day(180));
|
|
assert.equal(v.status, n > 180 ? 'determined' : 'not_applicable');
|
|
}
|
|
assert.equal(
|
|
evaluateGoodsPresumption({
|
|
...r,
|
|
deliveryDate: '2025-12-31',
|
|
operationDate: '2026-06-30',
|
|
period: '2026-06',
|
|
}).elapsedDays,
|
|
181,
|
|
);
|
|
assert.equal(
|
|
evaluateGoodsPresumption({ ...r, deliveryDate: '2026-12-13' }).status,
|
|
'indeterminate',
|
|
);
|
|
const returned = structuredClone(r);
|
|
returned.conditions.notReturned = 'no';
|
|
assert.equal(evaluateGoodsPresumption(returned).status, 'not_applicable');
|
|
});
|
|
void test('P126 rebuttable presumption,unknown proof and scope retained', () => {
|
|
for (const t of ['missing_goods', 'excess_consumption', 'found_goods']) {
|
|
const r = row(t);
|
|
assert.equal(
|
|
evaluateGoodsPresumption({ ...r, contraryProof: '' }).status,
|
|
'indeterminate',
|
|
);
|
|
assert.equal(
|
|
evaluateGoodsPresumption({ ...r, contraryProof: 'yes' }).status,
|
|
'indeterminate',
|
|
);
|
|
const v = evaluateGoodsPresumption({
|
|
...r,
|
|
contraryProof: 'yes',
|
|
contraryProofRef: 'SYNTHETIC QA prova/conjunto/período',
|
|
});
|
|
assert.equal(v.classification, 'rebutted');
|
|
assert.equal(v.baseAmount, null);
|
|
assert.equal(v.vat, null);
|
|
}
|
|
for (const quantity of ['', '0', '-1', '1e3', '1.1234567'])
|
|
assert.equal(
|
|
evaluateGoodsPresumption({ ...row('excess_consumption'), quantity })
|
|
.status,
|
|
'indeterminate',
|
|
);
|
|
const switched = switchPresumptionTreatment(
|
|
{
|
|
...row('excess_consumption'),
|
|
contraryProof: 'yes',
|
|
contraryProofRef: 'retained',
|
|
},
|
|
'consignment_180',
|
|
);
|
|
assert.equal(
|
|
switched.productionRef,
|
|
row('excess_consumption').productionRef,
|
|
);
|
|
assert.equal(switched.contraryProofRef, 'retained');
|
|
assert.equal(switched.contraryProof, '');
|
|
assert.deepEqual(switched.conditions, {});
|
|
});
|
|
void test('private draft/report/history preserve allpreviousbooks and opportunity', () => {
|
|
const rows = goodsPresumptionTreatments.map((t) => row(t.id)),
|
|
first = recordGoodsPresumptions(draft, { rows });
|
|
const next = {
|
|
...draft,
|
|
modelo_a_snapshot: saveGoodsPresumptionBook(draft, first),
|
|
};
|
|
const second = recordGoodsPresumptions(next, first);
|
|
next.modelo_a_snapshot = saveGoodsPresumptionBook(next, second);
|
|
assert.equal(second.history.length, 1);
|
|
assert.deepEqual(second.history[0].inputs, rows);
|
|
assert.equal(goodsPresumptionCurrent(next, second), true);
|
|
assert.equal(
|
|
goodsPresumptionCurrent(next, {
|
|
...second,
|
|
rows: rows.map((r, i) => (i ? r : { ...r, scope: 'changed' })),
|
|
}),
|
|
false,
|
|
);
|
|
assert.equal(goodsPresumptionReportRows(next).length, 4);
|
|
assert.deepEqual(
|
|
goodsPresumptionBook({
|
|
...next,
|
|
context_opportunity: 'opportunity:356',
|
|
}),
|
|
{ rows: [] },
|
|
);
|
|
const store = JSON.parse(next.modelo_a_snapshot);
|
|
delete store.goodsPresumptions;
|
|
assert.deepEqual(store, JSON.parse(frozen.modelo_a_snapshot));
|
|
assert.throws(() =>
|
|
recordGoodsPresumptions(next, { rows: [rows[0], rows[0]] }),
|
|
);
|
|
assert.throws(() =>
|
|
goodsPresumptionBook({
|
|
...next,
|
|
modelo_a_snapshot: JSON.stringify({
|
|
...store,
|
|
goodsPresumptions: [],
|
|
}),
|
|
}),
|
|
);
|
|
if (process.env.IVA_GOODS_PRESUMPTIONS_FIXTURE)
|
|
fs.writeFileSync(
|
|
process.env.IVA_GOODS_PRESUMPTIONS_FIXTURE,
|
|
JSON.stringify(next, null, 2) + '\n',
|
|
);
|
|
});
|