529 lines
20 KiB
JavaScript
529 lines
20 KiB
JavaScript
import {
|
|
ispcEligibilityBook,
|
|
saveIspcEligibilityBook,
|
|
} from '../../resources/js/lib/ispc-eligibility.ts';
|
|
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import {
|
|
ispcResidualBook,
|
|
saveIspcResidualBook,
|
|
recordIspcResidual,
|
|
ispcResidualCurrent,
|
|
ispcResidualReportRows,
|
|
ispcFormalPreparationHtml,
|
|
} from '../../resources/js/lib/ispc-residuals.ts';
|
|
import {
|
|
ispcWithholdingBook,
|
|
saveIspcWithholdingBook,
|
|
} from '../../resources/js/lib/ispc-withholding.ts';
|
|
import { createProjectDossierReport } from '../../resources/js/lib/project-dossier-report.ts';
|
|
const seed = JSON.parse(
|
|
fs.readFileSync(
|
|
new URL('./fixtures/ispc-quarter-preserved.json', import.meta.url),
|
|
'utf8',
|
|
),
|
|
);
|
|
function own(p = {}) {
|
|
const d = structuredClone(seed),
|
|
b = ispcResidualBook(d, 'occasional');
|
|
b.input = {
|
|
...b.input,
|
|
personType: 'singular',
|
|
currency: 'MZN',
|
|
origin: 'projected',
|
|
operationDate: '2026-06-15',
|
|
quarter: '2',
|
|
noHabitual: 'yes',
|
|
territory: 'yes',
|
|
sporadic: 'yes',
|
|
activity: 'agriculture',
|
|
reference: 'SYNTHETIC QA colheita não reiterada',
|
|
amount: '100000',
|
|
allQuarterOperations: 'yes',
|
|
priorYearTurnover: '0',
|
|
priorYearRef: 'SYNTHETIC QA2025zeroexpresso',
|
|
organizedRequired: 'no',
|
|
option: 'preparation',
|
|
optionRef: '',
|
|
optionDate: '',
|
|
settlement: 'self',
|
|
annual2026: '100000',
|
|
annualOrigin: 'forecast',
|
|
annualRef: 'SYNTHETIC QA2026',
|
|
priorInYear: '0',
|
|
...p,
|
|
};
|
|
return { d, b };
|
|
}
|
|
function acq(p = {}, wpatch = {}) {
|
|
const d = structuredClone(seed),
|
|
w = ispcWithholdingBook(d);
|
|
w.input = {
|
|
...w.input,
|
|
applicable: 'yes',
|
|
quarter: '2',
|
|
base: '200000',
|
|
priorAnnualPurchases: '2400000',
|
|
naturalOccasional: 'yes',
|
|
unregistered: 'yes',
|
|
related: 'yes',
|
|
activity: 'agriculture',
|
|
reference: 'SYNTHETIC QA aquisição/ordem e acumulado',
|
|
...wpatch,
|
|
};
|
|
d.modelo_a_snapshot = saveIspcWithholdingBook(d, w);
|
|
const b = ispcResidualBook(d, 'acquisitions');
|
|
b.input = { ...b.input, ...p };
|
|
return { d, b };
|
|
}
|
|
const calc = ({ d, b }) => recordIspcResidual(d, b, '2026-10-25T15:40:00Z');
|
|
const number = (x) => (x ? Number(x.numerator) / Number(x.denominator) : null);
|
|
void test('positive ownoccasional agricultural isolated8h route;self3/4/5 andnoautomaticIRPS20', () => {
|
|
for (const [annual, n] of [
|
|
['100000', 3],
|
|
['2000000', 4],
|
|
['3000000', 5],
|
|
]) {
|
|
const r = calc(own({ annual2026: annual })).result;
|
|
assert.equal(r.status, 'calculated', r.missing.join(';'));
|
|
assert.equal(number(r.amounts.tax), 1000 * n);
|
|
assert.match(r.source, /8\(3h,4\)/);
|
|
assert.equal(r.details.IRPS.includes('Não se infere'), true);
|
|
}
|
|
});
|
|
void test('unknown notnonapplicable;known exclusions andactivityfamilyownscope', () => {
|
|
assert.equal(calc(own({ personType: '' })).result.status, 'indeterminate');
|
|
for (const p of [
|
|
{ personType: 'collective' },
|
|
{ noHabitual: 'no' },
|
|
{ sporadic: 'no' },
|
|
{ territory: 'no' },
|
|
{ activity: 'other' },
|
|
])
|
|
assert.equal(calc(own(p)).result.status, 'not_applicable');
|
|
for (const p of [
|
|
{ territory: '' },
|
|
{ currency: 'USD' },
|
|
{ quarter: '3' },
|
|
{ reference: '' },
|
|
{ priorYearTurnover: '' },
|
|
{ organizedRequired: '' },
|
|
])
|
|
assert.equal(calc(own(p)).result.status, 'indeterminate');
|
|
});
|
|
void test('ownminimum500withoutlosingdeclaration;zeroexplicit and4mexcessnotreused', () => {
|
|
let r = calc(own({ amount: '1000' })).result;
|
|
assert.equal(number(r.amounts.tax), 30);
|
|
assert.equal(number(r.amounts.payment), 0);
|
|
assert.match(r.details.declaration, /Mantida/);
|
|
r = calc(own({ amount: '0' })).result;
|
|
assert.equal(number(r.amounts.tax), 0);
|
|
assert.equal(calc(own({ amount: '' })).result.status, 'indeterminate');
|
|
r = calc(
|
|
own({
|
|
amount: '200000',
|
|
priorInYear: '3900000',
|
|
annual2026: '4100000',
|
|
}),
|
|
).result;
|
|
assert.equal(number(r.amounts.ordinary), 100000);
|
|
assert.equal(number(r.amounts.excess), 100000);
|
|
assert.equal(number(r.amounts.tax), 25000);
|
|
});
|
|
void test('documentedliberatory5meansno secondselftax;smallwithholdingnotminimum500;divergenceunknown', () => {
|
|
const p = {
|
|
settlement: 'withheld',
|
|
origin: 'effective',
|
|
buyerIspc: 'yes',
|
|
unregistered: 'yes',
|
|
buyerAnnualPurchases: '200000',
|
|
withheldActual: '5000',
|
|
withheldRef: 'SYNTHETIC QA recibo5',
|
|
};
|
|
let r = calc(own(p)).result;
|
|
assert.equal(r.status, 'settled_by_withholding', r.missing.join(';'));
|
|
assert.equal(number(r.amounts.withheld), 5000);
|
|
assert.equal(r.amounts.payment, undefined);
|
|
assert.match(r.reason, /não somar nova liquidação/);
|
|
r = calc(own({ ...p, amount: '100', withheldActual: '5' })).result;
|
|
assert.equal(r.status, 'settled_by_withholding');
|
|
assert.equal(number(r.amounts.tax), 5);
|
|
assert.equal(
|
|
calc(own({ ...p, withheldActual: '4000' })).result.status,
|
|
'indeterminate',
|
|
);
|
|
assert.equal(
|
|
calc(own({ ...p, buyerAnnualPurchases: '2500000.000001' })).result
|
|
.status,
|
|
'indeterminate',
|
|
);
|
|
});
|
|
void test('selfeffective requiresapplicableoption;forecastcannot masqueradeasactual;nonoptionnoISPC', () => {
|
|
assert.equal(
|
|
calc(own({ origin: 'effective' })).result.status,
|
|
'indeterminate',
|
|
);
|
|
assert.equal(
|
|
calc(own({ option: 'not_opted' })).result.status,
|
|
'not_applicable',
|
|
);
|
|
const r = calc(
|
|
own({
|
|
origin: 'effective',
|
|
operationDate: '2026-12-31',
|
|
quarter: '4',
|
|
option: 'opted',
|
|
optionRef: 'QA opção',
|
|
optionDate: '2026-01-01',
|
|
annualOrigin: 'closed_year',
|
|
}),
|
|
).result;
|
|
assert.equal(r.status, 'calculated', r.missing.join(';'));
|
|
assert.equal(
|
|
calc(
|
|
own({
|
|
origin: 'effective',
|
|
option: 'opted',
|
|
optionRef: 'QA',
|
|
optionDate: '2026-07-01',
|
|
}),
|
|
).result.status,
|
|
'indeterminate',
|
|
);
|
|
});
|
|
void test('above2.5m splits100kcovered/100kexcess;5kknownbuttotalnullwithunknownroute', () => {
|
|
const r = calc(acq()).result;
|
|
assert.equal(r.status, 'partial');
|
|
assert.equal(number(r.amounts.covered), 100000);
|
|
assert.equal(number(r.amounts.coveredTax), 5000);
|
|
assert.equal(number(r.amounts.excess), 100000);
|
|
assert.equal(r.amounts.excessTax, null);
|
|
assert.equal(r.amounts.total, null);
|
|
});
|
|
void test('positiveIRPS57k20onidentifiedgrossact—separatefromISPCexcess20andownbuyerpayment', () => {
|
|
const p = {
|
|
route: 'irps_isolated',
|
|
noIspcOnExcess: 'yes',
|
|
isolatedAct: 'yes',
|
|
incomeInMoz: 'yes',
|
|
grossExcess: '100000',
|
|
grossRef: 'SYNTHETIC QA base ilíquida',
|
|
reference: 'SYNTHETIC QA8h/57k sem opção5',
|
|
};
|
|
const r = calc(acq(p)).result;
|
|
assert.equal(r.status, 'calculated', r.missing.join(';'));
|
|
assert.equal(number(r.amounts.excessTax), 20000);
|
|
assert.equal(number(r.amounts.total), 25000);
|
|
assert.match(r.reason, /não são crédito/);
|
|
for (const patch of [
|
|
{ grossExcess: '' },
|
|
{ grossExcess: '80000' },
|
|
{ noIspcOnExcess: '' },
|
|
{ isolatedAct: '' },
|
|
{ incomeInMoz: 'no' },
|
|
{ reference: '' },
|
|
]) {
|
|
const r = calc(acq({ ...p, ...patch })).result;
|
|
assert.equal(r.status, 'partial');
|
|
assert.equal(r.amounts.total, null);
|
|
}
|
|
});
|
|
void test('supplierISPCnotautomaticallyIRPS;crossingexactboundaryandprioroverlimit;noimplicit5onexcess', () => {
|
|
let r = calc(
|
|
acq({
|
|
route: 'ispc_supplier',
|
|
supplierOptionRef: 'QA opção aplicável',
|
|
}),
|
|
).result;
|
|
assert.equal(r.status, 'partial');
|
|
assert.equal(r.amounts.excessTax, null);
|
|
assert.match(r.reason, /art5impede/);
|
|
r = calc(acq({}, { base: '100000' })).result;
|
|
assert.equal(r.status, 'not_applicable');
|
|
assert.equal(number(r.amounts.excess), 0);
|
|
r = calc(acq({}, { priorAnnualPurchases: '2500001' })).result;
|
|
assert.equal(number(r.amounts.covered), 0);
|
|
assert.equal(number(r.amounts.coveredTax), 0);
|
|
assert.equal(number(r.amounts.excess), 200000);
|
|
});
|
|
void test('newbooks preservehabitualandL3exactly;dependenciesstale withoutrewritingfrozenresults', () => {
|
|
let { d, b } = acq();
|
|
const old = JSON.parse(d.modelo_a_snapshot);
|
|
b = calc({ d, b });
|
|
d.modelo_a_snapshot = saveIspcResidualBook(d, b);
|
|
for (const [k, v] of Object.entries(old))
|
|
assert.deepEqual(JSON.parse(d.modelo_a_snapshot)[k], v);
|
|
assert.equal(ispcResidualCurrent(d, b), true);
|
|
const w = ispcWithholdingBook(d);
|
|
w.input.base = '300000';
|
|
d.modelo_a_snapshot = saveIspcWithholdingBook(d, w);
|
|
assert.equal(ispcResidualCurrent(d, b), false);
|
|
assert.equal(number(b.result.amounts.excess), 100000);
|
|
const next = recordIspcResidual(d, b);
|
|
assert.equal(next.history.length, 1);
|
|
assert.equal(number(next.result.amounts.excess), 200000);
|
|
assert.equal(number(next.history[0].amounts.excess), 100000);
|
|
});
|
|
void test('formalBGeneral mappingfromexistingcurrentquarterwithoutrecalculation;monthnotinferred;old30notused', () => {
|
|
const d = structuredClone(seed),
|
|
b = ispcResidualBook(d, 'formalities'),
|
|
before = d.modelo_a_snapshot;
|
|
const result = recordIspcResidual(d, b);
|
|
assert.equal(result.result.status, 'partial');
|
|
assert.equal(number(result.result.amounts.tax), 24000);
|
|
assert.equal(number(result.result.amounts.payment), 24000);
|
|
assert.match(result.result.details.formPeriod, /Indeterminado/);
|
|
assert.equal(result.result.details.paymentMonth, 'Julho2026');
|
|
assert.equal(result.input.formMonth, '');
|
|
d.modelo_a_snapshot = saveIspcResidualBook(d, result);
|
|
const html = ispcFormalPreparationHtml(d);
|
|
assert.match(html, /24000.00/);
|
|
assert.match(html, /Quadro8/);
|
|
assert.match(html, /Quadro10/);
|
|
assert.match(html, /não é formulário oficial/);
|
|
assert.deepEqual(
|
|
JSON.parse(d.modelo_a_snapshot).ispcQuarters,
|
|
JSON.parse(before).ispcQuarters,
|
|
);
|
|
});
|
|
void test('formalinputsguardedandexternalactsdeclaredonly;signed/officialareasnotautofilled;HTMLescaped', () => {
|
|
let d = structuredClone(seed),
|
|
b = ispcResidualBook(d, 'formalities');
|
|
b.input = {
|
|
...b.input,
|
|
name: 'QA <script>bad()</script>',
|
|
nuit: '123456789',
|
|
fiscalArea: 'QAUGC',
|
|
address: 'QAendereço',
|
|
formMonth: '06',
|
|
formYear: '2026',
|
|
formPeriodRef: 'SYNTHETIC QA correspondência período',
|
|
interestSituation: 'none',
|
|
interestRef: 'SYNTHETIC QA sem juros',
|
|
};
|
|
b = recordIspcResidual(d, b);
|
|
assert.equal(b.result.status, 'calculated', b.result.missing.join(';'));
|
|
d.modelo_a_snapshot = saveIspcResidualBook(d, b);
|
|
const html = ispcFormalPreparationHtml(d);
|
|
assert.ok(html.includes('<script>'));
|
|
assert.ok(!html.includes('<script>bad'));
|
|
assert.match(html, /não preencher.*assinatura/i);
|
|
b.input.externalStatus = 'paid';
|
|
assert.equal(recordIspcResidual(d, b).result.status, 'partial');
|
|
});
|
|
void test('documentedwithholdingownroutepreventsduplicatedformalselfpayment', () => {
|
|
let { d, b } = own({
|
|
settlement: 'withheld',
|
|
buyerIspc: 'yes',
|
|
unregistered: 'yes',
|
|
buyerAnnualPurchases: '200000',
|
|
withheldActual: '5000',
|
|
withheldRef: 'QA recibo5',
|
|
});
|
|
b = recordIspcResidual(d, b);
|
|
d.modelo_a_snapshot = saveIspcResidualBook(d, b);
|
|
const f = ispcResidualBook(d, 'formalities');
|
|
f.input.sourceKind = 'occasional';
|
|
const r = recordIspcResidual(d, f).result;
|
|
assert.equal(r.status, 'not_applicable');
|
|
assert.match(r.reason, /duplicada/);
|
|
});
|
|
void test('invalidstoredmaps/enumspreserveerrors;noemptyfallback', () => {
|
|
const d = structuredClone(seed),
|
|
s = JSON.parse(d.modelo_a_snapshot);
|
|
s.ispcResiduals = [];
|
|
assert.throws(() =>
|
|
ispcResidualBook(
|
|
{ ...d, modelo_a_snapshot: JSON.stringify(s) },
|
|
'occasional',
|
|
),
|
|
);
|
|
const b = ispcResidualBook(d, 'occasional');
|
|
b.input.activity = 'services12';
|
|
assert.throws(() => saveIspcResidualBook(d, b));
|
|
});
|
|
void test('exports separate seller376/buyer375 and report;habitualbooksunchanged', () => {
|
|
let { d, b } = own();
|
|
d.context_opportunity = 'opportunity:376';
|
|
d.person_type = 'singular';
|
|
d.project_title = 'SYNTHETIC QA — ISPC residuais';
|
|
const e = ispcEligibilityBook(d);
|
|
e.input.mode = 'occasional';
|
|
e.input.personType = 'singular';
|
|
d.modelo_a_snapshot = saveIspcEligibilityBook(d, e);
|
|
b = recordIspcResidual(d, b, '2026-10-25T15:40:00Z');
|
|
d.modelo_a_snapshot = saveIspcResidualBook(d, b);
|
|
d.context_opportunity = 'opportunity:375';
|
|
d.person_type = 'collective';
|
|
const a = acq({
|
|
route: 'irps_isolated',
|
|
noIspcOnExcess: 'yes',
|
|
isolatedAct: 'yes',
|
|
incomeInMoz: 'yes',
|
|
grossExcess: '100000',
|
|
grossRef: 'QA base ilíquida',
|
|
reference: 'QA acto57k',
|
|
});
|
|
const aw = ispcWithholdingBook(a.d);
|
|
d.modelo_a_snapshot = saveIspcWithholdingBook(d, aw);
|
|
d.modelo_a_snapshot = saveIspcResidualBook(
|
|
d,
|
|
recordIspcResidual(d, a.b, '2026-10-25T15:41:00Z'),
|
|
);
|
|
const f = ispcResidualBook(d, 'formalities');
|
|
d.modelo_a_snapshot = saveIspcResidualBook(
|
|
d,
|
|
recordIspcResidual(d, f, '2026-10-25T15:42:00Z'),
|
|
);
|
|
const report = createProjectDossierReport(d, {
|
|
selectedSections: new Set(['finance']),
|
|
version: 'QA residuais',
|
|
generatedAt: '2026-10-25T15:43:00Z',
|
|
});
|
|
assert.match(JSON.stringify(report), /25000.00/);
|
|
assert.match(JSON.stringify(report), /24000.00/);
|
|
assert.match(JSON.stringify(ispcResidualReportRows(d)), /IRPS57/);
|
|
d.context_opportunity = 'opportunity:376';
|
|
assert.equal(ispcResidualBook(d, 'occasional').result.status, 'calculated');
|
|
assert.equal(
|
|
number(ispcResidualBook(d, 'occasional').result.amounts.tax),
|
|
3000,
|
|
);
|
|
d.context_opportunity = 'opportunity:375';
|
|
if (process.env.ISPC_RESIDUAL_OUTPUT)
|
|
fs.writeFileSync(
|
|
process.env.ISPC_RESIDUAL_OUTPUT,
|
|
JSON.stringify(d, null, 2),
|
|
);
|
|
});
|
|
|
|
void test('new uniqueformsource preserves deliberatechoice;documentedoptionwithannualforecast staysscenario', () => {
|
|
let { d, b } = own({
|
|
origin: 'effective',
|
|
option: 'opted',
|
|
optionRef: 'QA opção',
|
|
optionDate: '2026-01-01',
|
|
});
|
|
b = recordIspcResidual(d, b);
|
|
assert.equal(b.result.status, 'calculated');
|
|
assert.equal(b.result.details.projected, 'yes');
|
|
assert.match(b.result.reason, /Cenário declarado/);
|
|
const e = ispcEligibilityBook(d);
|
|
e.input.mode = 'occasional';
|
|
d.modelo_a_snapshot = saveIspcEligibilityBook(d, e);
|
|
d.modelo_a_snapshot = saveIspcResidualBook(d, b);
|
|
let f = ispcResidualBook(d, 'formalities');
|
|
assert.equal(f.input.sourceKind, 'occasional');
|
|
f.input.sourceKind = 'quarter';
|
|
d.modelo_a_snapshot = saveIspcResidualBook(d, f);
|
|
assert.equal(
|
|
ispcResidualBook(d, 'formalities').input.sourceKind,
|
|
'quarter',
|
|
);
|
|
});
|
|
|
|
void test('acquisitionformalpreparation distinguishesISPC5andIRPS20andkeepsspecificexternalmodelpending', () => {
|
|
let { d, b } = acq({
|
|
route: 'irps_isolated',
|
|
noIspcOnExcess: 'yes',
|
|
isolatedAct: 'yes',
|
|
incomeInMoz: 'yes',
|
|
grossExcess: '100000',
|
|
grossRef: 'QA base ilíquida',
|
|
reference: 'QA57k',
|
|
});
|
|
b = recordIspcResidual(d, b);
|
|
d.modelo_a_snapshot = saveIspcResidualBook(d, b);
|
|
let f = ispcResidualBook(d, 'formalities');
|
|
f.input.sourceKind = 'acquisitions';
|
|
f = recordIspcResidual(d, f);
|
|
assert.equal(number(f.result.amounts.tax), 25000);
|
|
assert.ok(f.result.missing.some((s) => s.includes('Modelo/código')));
|
|
assert.equal(f.result.status, 'partial');
|
|
d.modelo_a_snapshot = saveIspcResidualBook(d, f);
|
|
const html = ispcFormalPreparationHtml(d);
|
|
assert.match(html, /ISPC—retenção12/);
|
|
assert.match(html, /IRPS—acto isolado57/);
|
|
assert.match(html, /5000.00/);
|
|
assert.match(html, /20000.00/);
|
|
assert.match(html, /Por corresponder/);
|
|
});
|
|
|
|
void test('knownownaccess exclusions are concrete notrequestsforrevalidation;unknownstillindeterminate', () => {
|
|
for (const patch of [
|
|
{ organizedRequired: 'yes' },
|
|
{ priorYearTurnover: '4000000.000001' },
|
|
]) {
|
|
const r = calc(own(patch)).result;
|
|
assert.equal(r.status, 'not_applicable');
|
|
assert.equal(r.amounts.payment, undefined);
|
|
assert.match(r.reason, /fora/i);
|
|
}
|
|
assert.equal(
|
|
calc(own({ organizedRequired: '' })).result.status,
|
|
'indeterminate',
|
|
);
|
|
});
|
|
|
|
void test('below500paymentdispensa iscalculatedbutformalrepresentationnotinventedaszerocalculatedtax', () => {
|
|
let { d, b } = own({ amount: '1000' });
|
|
b = recordIspcResidual(d, b);
|
|
d.modelo_a_snapshot = saveIspcResidualBook(d, b);
|
|
let f = ispcResidualBook(d, 'formalities');
|
|
f.input = {
|
|
...f.input,
|
|
sourceKind: 'occasional',
|
|
name: 'QA vendedor',
|
|
nuit: '123456789',
|
|
fiscalArea: 'QAUGC',
|
|
address: 'QA endereço',
|
|
formMonth: '06',
|
|
formYear: '2026',
|
|
formPeriodRef: 'QA correspondência',
|
|
interestSituation: 'none',
|
|
interestRef: 'QA sem juros',
|
|
};
|
|
f = recordIspcResidual(d, f);
|
|
assert.equal(number(f.result.amounts.tax), 30);
|
|
assert.equal(number(f.result.amounts.payment), 0);
|
|
assert.equal(f.result.status, 'partial');
|
|
assert.ok(f.result.missing.some((s) => s.includes('dispensa')));
|
|
d.modelo_a_snapshot = saveIspcResidualBook(d, f);
|
|
const html = ispcFormalPreparationHtml(d);
|
|
assert.match(html, /Imposto calculado:30.00;prestação legal:0.00/);
|
|
});
|
|
|
|
void test('switchfromdocumentedliberatoryroute cannotgenerate secondduewithoutconcretereconciliation', () => {
|
|
let { d, b } = own({
|
|
settlement: 'withheld',
|
|
buyerIspc: 'yes',
|
|
unregistered: 'yes',
|
|
buyerAnnualPurchases: '200000',
|
|
withheldActual: '5000',
|
|
withheldRef: 'QA retenção definitiva',
|
|
});
|
|
b = recordIspcResidual(d, b);
|
|
assert.equal(b.result.status, 'settled_by_withholding');
|
|
b.input.settlement = 'self';
|
|
let next = recordIspcResidual(d, b);
|
|
assert.equal(next.result.status, 'partial');
|
|
assert.equal(next.result.amounts.payment, null);
|
|
assert.equal(next.result.amounts.tax, null);
|
|
assert.equal(number(next.result.amounts.withheld), 5000);
|
|
assert.equal(next.history[0].status, 'settled_by_withholding');
|
|
const fresh = structuredClone(b);
|
|
fresh.input.reference = 'QA outra operação';
|
|
assert.equal(recordIspcResidual(d, fresh).result.status, 'calculated');
|
|
b.input.withholdingCorrected = 'yes';
|
|
b.input.withholdingCorrectionRef = 'QA documento externo de rectificação';
|
|
next = recordIspcResidual(d, b);
|
|
assert.equal(next.result.status, 'calculated');
|
|
assert.equal(number(next.result.amounts.payment), 3000);
|
|
assert.equal(
|
|
next.result.details.priorWithholdingRef,
|
|
'QA retenção definitiva',
|
|
);
|
|
assert.equal(next.history[0].status, 'settled_by_withholding');
|
|
});
|