68 lines
7.1 KiB
JavaScript
68 lines
7.1 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import fs from 'node:fs';
|
|
import {annexIEntries,annexIEntry,annexIVersion} from '../../resources/js/lib/iva-annex-i.ts';
|
|
import {operationTreatments,newSalesOperation,evaluateSalesOperation,continueAnnexIOperation,recordSales,saveSalesBook,salesBook,salesCurrent} from '../../resources/js/lib/iva-operations.ts';
|
|
import {createProjectDossierReport} from '../../resources/js/lib/project-dossier-report.ts';
|
|
const rule=operationTreatments.find(t=>t.id==='annexI_goods');
|
|
const seed=()=>JSON.parse(fs.readFileSync(new URL('./fixtures/iva-pharma-inputs.json',import.meta.url),'utf8'));
|
|
const operation=(entry,i=0)=>({...newSalesOperation(`g2l-${i}`,'2026-09','MZN'),description:`SYNTHETIC QA ${entry.code} ${entry.id}`,establishment:'SYNTHETIC QA estabelecimento Manica',amount:'100',basis:i%2?'included':'net',treatment:rule.id,annexIItem:entry.id,confirmed:rule.conditions.map((_,n)=>n),evidence:'SYNTHETIC QA: correspondência código/designação e qualificadores da posição expressamente pressupostos; não é classificação aduaneira ou certificação'});
|
|
function fixture(){const draft=seed();draft.context_opportunity='opportunity:321';draft.project_title='SYNTHETIC QA — G2L Anexo I';
|
|
const codes=['0306.36.10','3102.10.00','1007.10.00','8501.61.00','8701.95.00','8201.50.00','0702.00.00','3808.91.00','1101.00.00','0402.21.10'];
|
|
const rows=codes.map((code,i)=>{
|
|
const entry=annexIEntries.find(e=>e.code===code);let row=operation(entry,i);
|
|
if(entry.relatedRequired){row=continueAnnexIOperation(row,entry.relatedTreatments[0]);row.confirmed=operationTreatments.find(t=>t.id===row.treatment).conditions.map((_,n)=>n);}
|
|
else if(entry.relatedTreatments){row.annexIOnly=true;row.evidence+=code==='1101.00.00'?'; mistura de trigo com centeio, não farinha de trigo simples':'; leite para utilização industrial, sem destino a lactente até um ano';}
|
|
return row;
|
|
});
|
|
const book=recordSales(draft,{currency:'MZN',rows,periods:[{period:'2026-09',complete:true}]});draft.modelo_a_snapshot=saveSalesBook(draft,book);return {draft,book};}
|
|
|
|
test('AnexoI has 172 distinct documentary positions, own qualifiers, cross-page text and five contextual tractor entries',()=>{
|
|
assert.equal(annexIEntries.length,172);assert.equal(new Set(annexIEntries.map(e=>e.code)).size,172);assert.equal(new Set(annexIEntries.map(e=>e.id)).size,172);
|
|
const scissors=annexIEntries.find(e=>e.code==='8201.50.00');assert.equal(scissors.pdfPage,8);assert.equal(scissors.lastPdfPage,9);assert.match(scissors.label,/uma das mãos/);
|
|
assert.equal(annexIEntries.filter(e=>e.context==='Outros tractores').length,5);
|
|
assert.equal(annexIEntries.find(e=>e.code==='8701.30.00').label.includes('Outros tractores'),false);
|
|
for(const [i,entry] of annexIEntries.entries()){
|
|
const row={...operation(entry,i),annexIOnly:true},r=evaluateSalesOperation(row,'MZN');
|
|
assert.equal(r.vat,entry.relatedRequired?null:'0.00');if(!entry.relatedRequired){assert.equal(r.field,'07');assert.equal(r.gross,'100.00');}
|
|
for(const token of [entry.id,entry.code,entry.label,`PDF${entry.pdfPage}`,annexIVersion])assert.ok(r.source.includes(token),token);
|
|
}
|
|
});
|
|
|
|
test('P035 requires actual position and qualifiers; overlapping descriptions require explicit applicability, never silently field07',()=>{
|
|
const {draft,book}=fixture(),row=book.rows[0];
|
|
for(const missing of row.confirmed)assert.equal(evaluateSalesOperation({...row,confirmed:row.confirmed.filter(n=>n!==missing)},'MZN').vat,null);
|
|
for(const patch of [{annexIItem:undefined},{annexIItem:''},{annexIItem:'0306.36.10'},{annexIItem:'annexIII-2022-001'},{evidence:''},{establishment:''},{amount:''},{period:'2025-09'},{sourceCurrency:'USD'}])assert.equal(evaluateSalesOperation({...row,...patch},'MZN').vat,null);
|
|
assert.equal(evaluateSalesOperation(row,'USD').vat,null);
|
|
const overlaps=annexIEntries.filter(e=>e.relatedTreatments);assert.equal(overlaps.length,13);assert.equal(overlaps.filter(e=>e.relatedRequired).length,3);
|
|
for(const entry of overlaps){const pending=operation(entry);assert.equal(evaluateSalesOperation(pending,'MZN').vat,null);assert.equal(evaluateSalesOperation({...pending,annexIOnly:true},'MZN').vat,entry.relatedRequired?null:'0.00');}
|
|
const incomplete=recordSales(draft,{...book,rows:[{...row,annexIItem:''}]});assert.equal(incomplete.result.periods[0].fields['07'],null);assert.deepEqual(incomplete.history.at(-1),book.result);
|
|
});
|
|
|
|
test('explicit continuation reuses existing treatment identity, preserves money/origin/annex and requires new conditions',()=>{
|
|
for(const entry of annexIEntries.filter(e=>e.relatedTreatments)){
|
|
for(const target of entry.relatedTreatments){
|
|
const before=operation(entry),after=continueAnnexIOperation(before,target),targetRule=operationTreatments.find(t=>t.id===target);
|
|
assert.equal(after.treatment,target);assert.equal(after.annexIItem,entry.id);assert.deepEqual(after.confirmed,[]);assert.ok(after.source.startsWith(before.source));assert.ok(after.source.includes(entry.id));
|
|
for(const key of ['id','period','amount','basis','sourceCurrency','sourcePeriod','establishment','description','evidence'])assert.equal(after[key],before[key],key);
|
|
assert.equal(evaluateSalesOperation(after,'MZN').vat,null);
|
|
const r=evaluateSalesOperation({...after,confirmed:targetRule.conditions.map((_,n)=>n)},'MZN');assert.equal(r.field,'05');assert.equal(r.vat,'0.00');
|
|
}
|
|
}
|
|
assert.throws(()=>continueAnnexIOperation(operation(annexIEntries[0]),'general'));
|
|
const e=annexIEntries.find(v=>v.code==='0702.00.00');assert.throws(()=>continueAnnexIOperation({...operation(e),treatment:'listed_fresh_chilled_tomato'},'listed_fresh_chilled_tomato'));
|
|
});
|
|
|
|
test('G2L recovery/report preserve twelve current books including G2K and calendar, without recalculation',()=>{
|
|
const {draft,book}=fixture(),original=seed(),old=JSON.parse(original.modelo_a_snapshot).salesOperations;
|
|
const recovered=JSON.parse(JSON.stringify(draft)),store=JSON.parse(recovered.modelo_a_snapshot).salesOperations;
|
|
assert.equal(Object.keys(old).length,12);for(const [id,value] of Object.entries(old)){assert.deepEqual(store[id],value);assert.equal(salesCurrent({...recovered,context_opportunity:id},value),true,id);}
|
|
assert.equal(salesCurrent(recovered,salesBook(recovered)),true);assert.equal(book.result.periods[0].fields['07'],'800.00');assert.equal(book.result.periods[0].fields['05'],'200.00');
|
|
const report=JSON.stringify(createProjectDossierReport(recovered,{selectedSections:new Set(['finance'])}));
|
|
for(const token of ['800.00','200.00','opportunity:321','Manica','2026-09','MZN','AnexoI/2022','Outros tractores',annexIVersion,...book.rows.map(r=>r.annexIItem)])assert.ok(report.includes(token),token);
|
|
for(const token of ['pharma_manufacturing_inputs','fundraising_public','production_forestry_opted'])assert.equal(report.includes(token),false);
|
|
const changed={...book,rows:book.rows.map((r,i)=>i===8?{...r,annexIOnly:false}:r)};assert.equal(salesCurrent(draft,changed),false);assert.deepEqual(changed.result,book.result);
|
|
assert.deepEqual(recovered.selection_labels,original.selection_labels);assert.equal(recovered.currency,original.currency);
|
|
});
|
|
if(process.env.IVA_G2L_OUT)fs.writeFileSync(process.env.IVA_G2L_OUT,JSON.stringify(fixture().draft,null,2));
|