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

525 lines
17 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import {
newSalesOperation,
recordSales,
saveSalesBook,
operationTreatments,
salesBook,
} from '../../resources/js/lib/iva-operations.ts';
import {
newAcquisition,
evaluateAcquisition,
recordAcquisitions,
saveAcquisitionBook,
acquisitionBook,
acquisitionCurrent,
acquisitionReportRows,
readAcquisitionStore,
} from '../../resources/js/lib/iva-acquisitions.ts';
import { createAcquisitionRightLink } from '../../resources/js/lib/iva-acquisition-right.ts';
import {
newAcquisitionBC,
reuseAcquisitionImport,
acquisitionImportSources,
} from '../../resources/js/lib/iva-acquisition-bc.ts';
import {
newImportExemption,
saveImportExemptionBook,
} from '../../resources/js/lib/iva-import-exemptions.ts';
const frozen = JSON.parse(
fs.readFileSync(
new URL('./fixtures/iva-l2bc-preserved.json', import.meta.url),
'utf8',
),
);
export function bcFixture() {
const draft = {
...structuredClone(frozen),
context_opportunity: 'opportunity:369',
project_title: 'SYNTHETIC QA — L2-B/C importações e exclusões',
};
const sale = {
...newSalesOperation('sale', '2026-12', 'MZN'),
description: 'SYNTHETIC QA venda geral',
establishment: 'Manica',
amount: '1000',
basis: 'net',
treatment: 'general',
confirmed: operationTreatments
.find((t) => t.id === 'general')
.conditions.map((_, i) => i),
evidence: 'SYNTHETIC QA condições',
};
draft.modelo_a_snapshot = saveSalesBook(
draft,
recordSales(draft, {
currency: 'MZN',
rows: [sale],
periods: [{ period: '2026-12', complete: true }],
}),
);
const link = {
...createAcquisitionRightLink(draft, 'sale'),
scopeConfirmed: 'yes',
allocationRef: 'SYNTHETIC QA afectação exclusiva',
noExcludedPayments: 'yes',
};
const row = (id, patch = {}, bc = {}) => ({
...newAcquisition(id, '2026-12', 'MZN'),
net: '100',
vat: '16',
gross: '116',
description: 'SYNTHETIC QA ' + id,
documentType: 'invoice',
documentRef: 'SYNTHETIC QA ' + id,
documentDate: '2026-12-10',
supplier: 'SYNTHETIC QA fornecedor',
supplierRegistered: 'yes',
legalDocument: 'yes',
held: 'yes',
domestic: 'yes',
allocation: 'taxable_only',
exclusion: 'none',
integrity: 'yes',
deductionLink: structuredClone(link),
bc: {
...newAcquisitionBC(),
expense: 'ordinary',
reducedSupply: 'no',
evidence: 'SYNTHETIC QA natureza e âmbito',
...bc,
},
...patch,
});
const imported = row(
'import-due',
{
field: '11',
domestic: 'no',
documentType: 'customs',
supplier: '',
supplierRegistered: '',
},
{
importer: 'SYNTHETIC QA importador',
belongs: 'yes',
ownImportVat: 'yes',
vatBasis: 'due',
goodsRef: 'SYNTHETIC QA bens/linha1',
vatRef: 'SYNTHETIC QA bilhete de despacho',
facts: { exigibleInPeriod: 'yes' },
},
);
const book = {
currency: 'MZN',
rightVersion: 'acquisition-operation-right/1',
taxpayerRegistered: 'yes',
rows: [
imported,
row(
'diesel',
{},
{
expense: 'fuel',
fuelKind: 'diesel',
dieselUse: 'other',
facts: { resale: 'no' },
},
),
row('travel', {}, { expense: 'travel' }),
row(
'fixed-phone',
{},
{ expense: 'telephone', facts: { ownLandline: 'yes' } },
),
],
periods: [
{
period: '2026-12',
complete: true,
importsComplete: true,
absent: ['12', '13'],
},
],
};
return { draft, book, row, imported };
}
void test('B customs document has own conditions, unknown is not exempt/zero and supplier domestic registration is irrelevant', () => {
const { draft, imported } = bcFixture();
const run = (patch = {}, bc = {}) =>
evaluateAcquisition(
{ ...imported, ...patch, bc: { ...imported.bc, ...bc } },
'MZN',
'yes',
draft,
);
assert.equal(run().deductible, '16.00');
assert.equal(run().eligibility.status, 'eligible');
assert.equal(
run({ net: '', gross: '' }, { reducedSupply: '' }).deductible,
'16.00',
);
assert.equal(run({ vat: '' }).deductible, null);
assert.equal(
run({ acquisitionTreatment: 'former_exploration_exemption' })
.deductible,
null,
);
assert.equal(run({ acquisitionTreatment: 'unknown' }).deductible, null);
for (const bc of [
{ belongs: '' },
{ importer: '' },
{ goodsRef: '' },
{ vatRef: '' },
{ ownImportVat: '' },
{ vatBasis: 'unknown' },
{ facts: { exigibleInPeriod: '' } },
{ facts: { exigibleInPeriod: 'no' } },
])
assert.equal(run({}, bc).deductible, null, JSON.stringify(bc));
assert.equal(run({ documentType: 'invoice' }).deductible, null);
assert.equal(run({ held: '' }).deductible, null);
assert.equal(run({ documentDate: '2026-11-10' }).deductible, null);
assert.equal(
run({ vat: '0', gross: '100' }, { vatBasis: 'exempt' }).deductible,
'0.00',
);
assert.equal(
run({ vat: '0', gross: '100' }, { vatBasis: 'zero' }).deductible,
'0.00',
);
assert.equal(
run({ vat: '', gross: '' }, { vatBasis: 'exempt' }).deductible,
null,
);
assert.equal(run({}, { vatBasis: 'exempt' }).deductible, null);
});
void test('C exclusions/exceptions retain their own scope and unknown facts never imply no exclusion', () => {
const { draft, row } = bcFixture();
const run = (bc, patch = {}) =>
evaluateAcquisition(row('case', patch, bc), 'MZN', 'yes', draft);
const cases = [
[
{
expense: 'vehicle',
facts: { tourism: 'yes', saleOrExploit: 'no' },
},
'0.00',
],
[
{
expense: 'vehicle',
facts: { tourism: 'yes', saleOrExploit: 'yes' },
},
'16.00',
],
[{ expense: 'vehicle', facts: { tourism: 'no' } }, '16.00'],
[{ expense: 'vehicle', facts: {} }, null],
[
{ expense: 'fuel', facts: { resale: 'no' }, fuelKind: 'other' },
'0.00',
],
[{ expense: 'fuel', facts: { resale: 'yes' } }, '16.00'],
[
{
expense: 'fuel',
facts: { resale: 'no' },
fuelKind: 'diesel',
dieselUse: 'other',
},
'8.00',
],
[{ expense: 'travel' }, '0.00'],
[
{ expense: 'lodging_food', facts: { commercialTraveller: 'yes' } },
'16.00',
],
[
{ expense: 'lodging_food', facts: { commercialTraveller: 'no' } },
'0.00',
],
[{ expense: 'lodging_food', facts: {} }, null],
[{ expense: 'beverages', facts: { beverageSpecial: 'no' } }, '0.00'],
[{ expense: 'beverages', facts: {} }, null],
[{ expense: 'telephone', facts: { ownLandline: 'yes' } }, '16.00'],
[{ expense: 'telephone', facts: { ownLandline: 'no' } }, '0.00'],
[{ expense: 'telephone', facts: {} }, null],
[{ expense: 'luxury' }, '0.00'],
[{ expense: 'ordinary', reducedSupply: 'yes' }, '0.00'],
[{ expense: 'ordinary', reducedSupply: '' }, null],
];
for (const [bc, expected] of cases) {
const r = run(bc);
assert.equal(r.deductible, expected, JSON.stringify(bc));
assert.ok(r.eligibility.reason);
assert.match(r.eligibility.source, /20\(3\)/);
}
for (const dieselUse of [
'heavy_passengers',
'public_passengers',
'machine',
'agriculture',
]) {
assert.equal(
run({
expense: 'fuel',
fuelKind: 'diesel',
dieselUse,
facts: { resale: 'no', dieselScope: 'yes' },
}).deductible,
'16.00',
);
assert.equal(
run({
expense: 'fuel',
fuelKind: 'diesel',
dieselUse,
facts: { resale: 'no', dieselScope: '' },
}).deductible,
null,
);
}
for (const patch of [
{ held: '' },
{ legalDocument: '' },
{ documentDate: '2026-11-01' },
{ allocation: 'mixed' },
{ integrity: '' },
])
assert.equal(
run({ expense: 'telephone', facts: { ownLandline: 'yes' } }, patch)
.deductible,
null,
);
});
void test('F includes import11, excludes denied purchases and preserves exact diesel fractions across totals', () => {
const { draft, book } = bcFixture();
const result = recordAcquisitions(draft, book).result;
assert.deepEqual(
['09', '11', '14', '15', '16', '17', '18'].map(
(k) => result.periods[0].fields[k],
),
['24.00', '16.00', '1000.00', '40.00', '160.00', '120.00', '0.00'],
);
const partial = structuredClone(book);
partial.periods[0].importsComplete = false;
assert.equal(
recordAcquisitions(draft, partial).result.periods[0].fields['11'],
null,
);
assert.equal(
recordAcquisitions(draft, partial).result.periods[0].fields['15'],
null,
);
partial.periods[0].absent.push('11');
assert.throws(() => recordAcquisitions(draft, partial), /importações/);
const precision = structuredClone(book);
precision.rows = [0, 1].map((i) => ({
...book.rows[1],
id: 'exact-' + i,
documentLine: String(i),
net: '1',
vat: '0.000001',
gross: '1.000001',
}));
const exact = recordAcquisitions(draft, precision).result;
assert.deepEqual(exact.rows[0].deductibleExact, {
numerator: '1',
denominator: '2000000',
});
assert.equal(exact.periods[0].fields['09'], '0.000001');
assert.deepEqual(exact.periods[0].exactFields['15'], {
numerator: '1',
denominator: '1000000',
});
const duplicate = structuredClone(book);
duplicate.rows.push({
...book.rows[0],
id: 'duplicate',
origin: 'effective',
});
assert.throws(() => recordAcquisitions(draft, duplicate), /dupla dedução/);
});
void test('import reuse fills only missing data, detects source drift and preserves A plus every prior map and frozen history', () => {
let { draft, book, imported } = bcFixture();
const origin = {
...newImportExemption('source', 'MZN'),
description: 'SYNTHETIC QA origemL1',
period: '2026-12',
importer: 'SYNTHETIC QA origem importador',
documentRef: 'ORIGINAL',
documentDate: '2026-12-12',
};
draft.modelo_a_snapshot = saveImportExemptionBook(draft, {
rows: [origin],
});
assert.equal(acquisitionImportSources(draft).length, 1);
const reused = reuseAcquisitionImport(
draft,
imported,
'importExemptions',
'source',
);
assert.equal(reused.documentRef, imported.documentRef);
assert.equal(reused.documentDate, imported.documentDate);
assert.equal(reused.bc.importer, imported.bc.importer);
assert.equal(reused.vat, '16');
book.rows[0] = reused;
book = recordAcquisitions(draft, book);
draft.modelo_a_snapshot = saveAcquisitionBook(draft, book);
const first = structuredClone(book.result);
book = recordAcquisitions(draft, book);
draft.modelo_a_snapshot = saveAcquisitionBook(draft, book);
const recovered = JSON.parse(JSON.stringify(draft));
assert.ok(acquisitionCurrent(recovered, acquisitionBook(recovered)));
assert.deepEqual(book.history, [first]);
const before = JSON.parse(frozen.modelo_a_snapshot),
after = JSON.parse(draft.modelo_a_snapshot);
for (const k of Object.keys(before).filter((k) => k !== 'version'))
for (const [id, value] of Object.entries(before[k]))
assert.deepEqual(after[k][id], value, k + id);
assert.ok(
acquisitionCurrent(
{ ...recovered, context_opportunity: 'opportunity:368' },
before.books['opportunity:368'],
),
);
const report = JSON.stringify(acquisitionReportRows(recovered));
for (const v of [
'CondiçõesB/C',
'40.00',
'120.00',
'importador',
'20(3)',
'8/1',
])
assert.ok(report.includes(v), v);
draft.modelo_a_snapshot = saveImportExemptionBook(draft, {
rows: [{ ...origin, description: 'Editado' }],
});
assert.equal(acquisitionCurrent(draft, book), false);
assert.equal(
evaluateAcquisition(book.rows[0], 'MZN', 'yes', draft).deductible,
null,
);
assert.deepEqual(book.history[0], first);
draft.modelo_a_snapshot = saveImportExemptionBook(draft, {
rows: [{ ...origin, treatment: 'service_in_customs_value' }],
});
const service = reuseAcquisitionImport(
draft,
book.rows[0],
'importExemptions',
'source',
);
assert.equal(
evaluateAcquisition(service, 'MZN', 'yes', draft).deductible,
null,
);
assert.match(
evaluateAcquisition(service, 'MZN', 'yes', draft).reason,
/não IVA próprio/,
);
const broken = JSON.parse(recovered.modelo_a_snapshot);
broken.books['opportunity:369'].rows[0].bc.facts = { bad: 'maybe' };
assert.throws(
() => readAcquisitionStore(JSON.stringify(broken)),
/CondiçõesB\/C/,
);
if (process.env.IVA_L2BC_CALCULATED)
fs.writeFileSync(
process.env.IVA_L2BC_CALCULATED,
JSON.stringify(recovered),
);
});
void test('beverages13(2) uses current c/d/h source and deduction-only scope without granting direct exemption or refund', () => {
for (const treatment of [
'onboard_seagoing_ship',
'onboard_international_aircraft',
'onboard_rescue_coastal_ship',
'onboard_departing_warship',
]) {
const { draft, row } = bcFixture();
const ops = salesBook(draft);
const rule = operationTreatments.find((t) => t.id === treatment);
ops.rows.push({
...newSalesOperation('beverages', '2026-12', 'MZN'),
description: 'SYNTHETIC QA bebidas de bordo',
establishment: 'Manica',
amount: '100',
basis: 'net',
treatment,
supplyCategory: 'beverages',
confirmed: rule.conditions.map((_, i) => i),
evidence: 'SYNTHETIC QA meio,destino,utilização e embarque',
});
draft.modelo_a_snapshot = saveSalesBook(draft, recordSales(draft, ops));
const r = row(
'beverages',
{},
{
expense: 'beverages',
beveragePath: 'deduction',
facts: {
beverageSpecial: 'yes',
onlyDeduction: 'yes',
notCoastalFishing: 'yes',
},
},
);
r.deductionLink = {
...createAcquisitionRightLink(draft, 'beverages'),
scopeConfirmed: 'yes',
allocationRef: 'SYNTHETIC QA parcela e operação',
noExcludedPayments: 'yes',
};
assert.equal(
evaluateAcquisition(r, 'MZN', 'yes', draft).deductible,
treatment === 'onboard_departing_warship' ? null : '16.00',
treatment,
);
assert.equal(
evaluateAcquisition(
{ ...r, bc: { ...r.bc, beveragePath: 'refund' } },
'MZN',
'yes',
draft,
).deductible,
treatment === 'onboard_departing_warship' ? null : '0.00',
);
assert.equal(
evaluateAcquisition(
{
...r,
bc: {
...r.bc,
facts: { ...r.bc.facts, onlyDeduction: '' },
},
},
'MZN',
'yes',
draft,
).deductible,
null,
);
if (treatment === 'onboard_rescue_coastal_ship')
assert.equal(
evaluateAcquisition(
{
...r,
bc: {
...r.bc,
facts: { ...r.bc.facts, notCoastalFishing: 'no' },
},
},
'MZN',
'yes',
draft,
).deductible,
null,
);
}
});