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

125 lines
11 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;};
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);
});
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);
}
});
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);
});
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);
}
});
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/);
});
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);
});
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/);
});
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/);
});
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);
});
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);
});
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);
});
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);
});
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);
});
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/);
});
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);
}
});
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);
});