Files
Investidor-V2/tests/frontend/irpc-credit-carry.test.mjs
T

351 lines
13 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import { carryCase } from './helpers/irpc-carry-case.mjs';
import { internationalCase } from './helpers/irpc-international-case.mjs';
import {
newCarryUse,
calculateCarryLot,
} from '../../resources/js/lib/irpc-credit-carry.ts';
import {
calculateAnnual,
prepareCreditCarry,
recordAnnual,
annualBook,
annualCurrent,
saveAnnualBook,
annualHtml,
annualReportRows,
} from '../../resources/js/lib/irpc-annual-settlement.ts';
import { createProjectDossierReport } from '../../resources/js/lib/project-dossier-report.ts';
const number = (v) => (v ? Number(v.numerator) / Number(v.denominator) : null);
const calc = (c) => calculateAnnual(c.d, c.b);
const lot = (c, i = 0) => c.b.creditCarry.lots[i];
const pending = (c) => {
const m = calc(c);
assert.equal(m.status, 'indeterminate', JSON.stringify(m.pending));
assert.equal(m.arithmeticBalance, null);
assert.equal(m.internationalCredit.deducted, null);
assert.equal(m.pecDeduction.deducted, null);
return m;
};
void test('2021 international and2023 PEC remain eligible in2026; origin deductions/uses count once:1408-8-50-200-100-500=550', () => {
const m = calc(carryCase());
assert.deepEqual(m.pending, []);
assert.equal(number(m.payable), 550);
assert.equal(number(m.creditCarry.lots[0].opening), 100);
assert.equal(number(m.creditCarry.lots[0].used), 30);
assert.equal(number(m.creditCarry.lots[0].available), 70);
assert.equal(number(m.creditCarry.lots[0].remaining), 20);
assert.equal(m.creditCarry.lots[0].lastYear, 2026);
assert.equal(m.creditCarry.lots[1].lastYear, 2026);
assert.equal(number(m.creditCarry.lots[1].remaining), 50);
assert.equal(m.creditCarry.carryCreated, false);
assert.equal(m.creditCarry.officialBalancesConsumed, false);
});
void test('five subsequent exercises differs from PEC three; expired source retained with zero selection', () => {
for (const [i, year] of [
[0, '2020'],
[1, '2022'],
]) {
const c = carryCase();
lot(c, i).year = year;
if (i === 0) lot(c).uses[0].year = '2023';
assert.match(pending(c).pending.join(' '), /Prazo ordinário terminado/);
lot(c, i).selected = '0';
const m = calc(c);
assert.deepEqual(m.pending, []);
assert.equal(m.creditCarry.lots[i].expired, true);
assert.equal(number(m.creditCarry.lots[i].available), 0);
assert.ok(number(m.creditCarry.lots[i].remaining) > 0);
}
});
void test('future/current origin, nonordinary periods, currency and wrong subject do not create an eligible carry', () => {
for (const patch of [
{ year: '2026' },
{ year: '2027' },
{ year: '' },
{ ordinary: '' },
{ ordinary: 'no' },
{ currency: 'USD' },
{ subjectNuit: '999999999' },
{ country: 'MZ' },
{ country: 'ZZ' },
{ kind: '' },
]) {
const c = carryCase();
Object.assign(lot(c), patch);
pending(c);
}
const c = carryCase();
lot(c, 1).country = 'ZA';
pending(c);
});
void test('origin eligibility, insufficient collecta and applicable rule require concrete evidence', () => {
for (const patch of [
{ originDocument: '' },
{ originReference: '' },
{ ruleReference: '' },
{ insufficient: '' },
{ insufficient: 'no' },
{ eligible: '' },
{ originDeducted: '' },
{ originDeducted: '201' },
{ selected: '' },
{ selected: '70.01' },
]) {
const c = carryCase();
Object.assign(lot(c), patch);
pending(c);
}
});
void test('unknown uses are not zero; every actual use must fit its origin/window and documentary identity', () => {
for (const patch of [
{ usesState: '' },
{ usesReference: '' },
{ usesState: 'absent' },
{ uses: [] },
{
uses: [
{
...newCarryUse('u'),
year: '2021',
amount: '1',
document: 'D',
reference: 'R',
},
],
},
]) {
const c = carryCase();
Object.assign(lot(c), patch);
pending(c);
}
for (const patch of [
{ year: '2027' },
{ amount: '101' },
{ document: '' },
{ reference: '' },
{ amount: '' },
]) {
const c = carryCase();
Object.assign(lot(c).uses[0], patch);
pending(c);
}
const c = carryCase();
lot(c).uses.push({
...lot(c).uses[0],
id: 'copy',
year: '2024',
document: ' synthetic USE2023 ',
});
assert.match(pending(c).pending.join(' '), /repetido/);
});
void test('actual current-year use is subtracted before the proposed new deduction', () => {
const c = carryCase();
lot(c).uses.push({
id: 'current-actual',
year: '2026',
amount: '10',
document: 'SYNTHETIC current actual use',
reference: 'SYNTHETIC actual assessment, not this simulation',
});
const m = calc(c);
assert.equal(number(m.creditCarry.lots[0].available), 60);
assert.equal(number(m.creditCarry.lots[0].remaining), 10);
assert.equal(number(m.payable), 550);
});
void test('original country/nature/year cannot be duplicated by changing document or id', () => {
const c = carryCase();
c.b.creditCarry.lots.push({
...structuredClone(lot(c)),
id: 'other',
originDocument: 'OTHER',
uses: [],
usesState: 'absent',
});
assert.match(pending(c).pending.join(' '), /mesma natureza/);
});
void test('cross-context duplicate origin blocks and external edits invalidate saved current result', () => {
const c = carryCase();
c.d.modelo_a_snapshot = saveAnnualBook(c.d, recordAnnual(c.d, c.b));
const b = annualBook(c.d);
assert.equal(annualCurrent(c.d, b), true);
const s = JSON.parse(c.d.modelo_a_snapshot),
other = structuredClone(c.b);
other.creditCarry.lots = [
{
...structuredClone(lot(c)),
id: 'copied-origin',
originDocument: 'OTHER',
uses: [],
usesState: 'absent',
},
];
s.irpcAnnual['opportunity:other'] = other;
c.d.modelo_a_snapshot = JSON.stringify(s);
assert.equal(annualCurrent(c.d, b), false);
assert.match(pending(c).pending.join(' '), /noutro mapa/);
});
void test('explicit allocation can favour older balances without automatic reduction or FIFO', () => {
const c = carryCase();
Object.assign(c.b.creditCarry, {
currentInternational: '0',
currentPec: '100',
});
const m = calc(c);
assert.equal(number(m.payable), 658);
assert.equal(number(m.internationalCredit.deducted), 0);
assert.equal(number(m.internationalCredit.notSelected), 8);
assert.equal(m.internationalCredit.insufficientCollecta, null);
assert.equal(number(m.pecDeduction.deducted), 100);
assert.equal(number(m.pecDeduction.remaining), 100);
});
void test('current selection cannot exceed known availability or total collecta; planned choices are kept on failure', () => {
for (const patch of [
{ currentInternational: '9' },
{ currentPec: '201' },
{ currentInternational: '' },
{ currentPec: '' },
{ allocationReference: '' },
{ assessmentKey: 'stale' },
]) {
const c = carryCase();
Object.assign(c.b.creditCarry, patch);
pending(c);
}
const c = carryCase();
lot(c).eligible = '5000';
lot(c).selected = '1400';
const before = structuredClone(c.b);
assert.match(pending(c).pending.join(' '), /ultrapassam a colecta/);
assert.deepEqual(c.b, before);
});
void test('no credit or PEC reimbursement is generated by old balances; PPC refund stays separate', () => {
const c = carryCase();
Object.assign(c.b.creditCarry, {
currentInternational: '8',
currentPec: '0',
});
lot(c).eligible = '2000';
lot(c).selected = '1300';
lot(c, 1).selected = '100';
const m = calc(c);
assert.equal(number(m.recoverable), 500);
assert.equal(number(m.pecDeduction.deducted), 0);
assert.equal(m.creditCarry.carryCreated, false);
assert.equal(number(m.creditCarry.lots[0].remaining), 570);
});
void test('exact foreign-credit fractions are preserved; decimal presentation is never fiscal rounding', () => {
const c = carryCase();
Object.assign(lot(c), {
eligible: '2/3',
originDeducted: '1/3',
selected: '1/3',
usesState: 'absent',
uses: [],
});
Object.assign(c.b.creditCarry, { currentInternational: '1/3' });
const m = calc(c);
assert.deepEqual(m.pending, []);
assert.deepEqual(m.creditCarry.lots[0].available, {
numerator: '1',
denominator: '3',
});
assert.deepEqual(m.payable, { numerator: '1822', denominator: '3' });
lot(c).selected = '1/0';
pending(c);
});
void test('missing/absent/known-zero remain distinct; absence does not demand unused allocation fields', () => {
const c = internationalCase(),
baseline = calc(c);
c.b = prepareCreditCarry(c.b);
pending(c);
c.b.creditCarry.state = 'absent';
c.b.creditCarry.reference = 'SYNTHETIC QA documented absence';
let m = calc(c);
assert.equal(number(m.payable), number(baseline.payable));
assert.deepEqual(m.internationalCredit, baseline.internationalCredit);
c.b.creditCarry.lots.push({ ...carryCase().b.creditCarry.lots[0] });
pending(c);
const z = carryCase();
Object.assign(lot(z), {
eligible: '0',
originDeducted: '0',
selected: '0',
usesState: 'absent',
uses: [],
});
m = calc(z);
assert.deepEqual(m.pending, []);
assert.equal(number(m.creditCarry.lots[0].available), 0);
});
void test('optional preparation, recording, JSON recovery, HTML and dossier preserve accepted books and frozen history', () => {
const c = internationalCase();
c.d.modelo_a_snapshot = saveAnnualBook(
c.d,
recordAnnual(c.d, c.b, '2026-10-27T17:00:00+02:00'),
);
const old = annualBook(c.d),
frozen = structuredClone(old.result),
existing = JSON.parse(c.d.modelo_a_snapshot),
plan = c.d.business_plan_snapshot;
const next = prepareCreditCarry(old);
next.creditCarry = carryCase().b.creditCarry;
c.d.modelo_a_snapshot = saveAnnualBook(
c.d,
recordAnnual(c.d, next, '2026-10-28T12:00:00+02:00'),
);
const recovered = annualBook(JSON.parse(JSON.stringify(c.d)));
assert.deepEqual(recovered.history, [frozen]);
assert.equal(number(recovered.result.model.payable), 550);
assert.equal(annualCurrent(c.d, recovered), true);
assert.equal(c.d.business_plan_snapshot, plan);
const after = JSON.parse(c.d.modelo_a_snapshot);
delete after.irpcAnnual;
delete existing.irpcAnnual;
assert.deepEqual(after, existing);
assert.match(
annualHtml(c.d),
/Saldo anterior — Crédito internacional ZA — 2021/,
);
assert.match(annualHtml(c.d), /550.00/);
assert.match(annualHtml(c.d), /2026/);
assert.match(
JSON.stringify(
createProjectDossierReport(c.d, {
selectedSections: new Set(['finance']),
}),
),
/Afectação escolhida entre exercícios/,
);
recovered.creditCarry.lots[0].selected = '49';
c.d.modelo_a_snapshot = saveAnnualBook(c.d, recovered);
assert.equal(annualCurrent(c.d, recovered), false);
assert.deepEqual(recovered.result.model, annualBook(c.d).result.model);
assert.throws(() => annualHtml(c.d), /fontes actuais/);
assert.match(annualReportRows(c.d).flat().join(' '), /Histórico/);
});
void test('malformed detail and duplicate internal ids cannot overwrite original snapshots', () => {
for (const mutate of [
(c) => (c.b.creditCarry.lots[0].uses = null),
(c) => (c.b.creditCarry.version = 2),
(c) => (c.b.creditCarry.lots[0].uses[0].amount = null),
(c) => (c.b.creditCarry.lots[1].id = c.b.creditCarry.lots[0].id),
]) {
const c = carryCase(),
before = c.d.modelo_a_snapshot;
mutate(c);
assert.throws(() => saveAnnualBook(c.d, c.b), /recuper|repetido/);
assert.equal(c.d.modelo_a_snapshot, before);
}
});
void test('standalone expired PEC keeps origin and is not silently converted to international five-year window', () => {
const c = carryCase(),
r = { ...lot(c, 1), year: '2022', selected: '0' };
const m = calculateCarryLot(r, c.b.subjectNuit, c.b.year);
assert.equal(m.expired, true);
assert.equal(m.lastYear, 2025);
assert.equal(number(m.remaining), 150);
});