Files
compras/scripts/sgf-sourcing-demo-http.mjs
2026-11-02 20:27:11 +02:00

27 lines
5.0 KiB
JavaScript

import fs from 'node:fs';import assert from 'node:assert/strict';import crypto from 'node:crypto';
const privateData=JSON.parse(fs.readFileSync('C:/laragon/data/sgf-tempo/run/modular-regression-2576427d1939/private/development.json','utf8'));
assert.deepEqual([privateData.tenant_id,privateData.company_id,privateData.unit_id],[1,1,1]);
const clients={},checks=[];
for(const p of privateData.profiles){let csrf='',token='';const jar=new Map();
const api=async(path,method='GET',body,status=200)=>{const r=await fetch('http://127.0.0.1:8039'+path,{method,headers:{Accept:'application/json','Content-Type':'application/json',Cookie:[...jar].map(([k,v])=>k+'='+v).join('; '),'X-CSRF-TOKEN':csrf,'X-SGF-Context':token,'X-Purchasing-Context':'procurement.internal'},...(body===undefined?{}:{body:JSON.stringify(body)})});for(const c of r.headers.getSetCookie()){const pair=c.split(';')[0],at=pair.indexOf('=');jar.set(pair.slice(0,at),pair.slice(at+1));}const text=await r.text();let d;try{d=JSON.parse(text);}catch{d={message:text.slice(0,160)};}assert.equal(r.status,status,`${p.role} ${path}: ${d.message??JSON.stringify(d.errors)}`);if(d.csrf)csrf=d.csrf;if(d.context?.token)token=d.context.token;return d;};
await api('/api/session');await api('/api/login','POST',{email:p.email,password:privateData.password});await api('/api/context','POST',{tenant_id:1,company_id:1,unit_id:1,module_id:'purchasing'});clients[p.role]=api;
}
const read=who=>clients[who]('/api/sourcing/1');
async function command(who,action,body,status=200){const current=await read(who);return clients[who]('/api/sourcing/1/'+action,'POST',{operation_key:crypto.randomUUID(),version:current.state.version,...(action==='propose'?{delivery_date:'2027-01-15',delivery_location:'Local de entrega apenas sintético para teste de recusa'}:{}),...body},status);}
let initial=await read('preparador');assert.equal(initial.request.total_minor,10000);assert.equal(initial.request.version,3);assert.equal(initial.suppliers.length,3);
for(const [index,supplier] of initial.suppliers.entries()){
let current=await read('preparador');if(!current.state.suppliers[supplier.id])await command('preparador','select',{supplier_id:supplier.id});
current=await read('preparador');
if(!current.state.quotes[supplier.id])await command('preparador','quote',{supplier_id:supplier.id,reference:'DEMO-COTACAO-'+supplier.reference,currency:'MZN',valid_until:'2027-12-31',delivery_days:5+index,payment_terms:'Condições sintéticas: pagamento a 30 dias',items:current.request.data.items.map((_,line)=>({line,unit_price:(100+index*10).toFixed(2)}))});
current=await read('revisor');const q=current.state.quotes[supplier.id];
if(!q.evaluations[2])await command('revisor','evaluate',{supplier_id:supplier.id,quote_revision:q.revision,quality:8,history:8,payment:8,no_conflict:true,reason:'Avaliação exclusivamente sintética de demonstração; sem contacto externo.'});
}
const pending=await read('consulta');assert.equal(pending.comparison.ready,false);assert(pending.comparison.rows.every(r=>r.evaluation_count===1));assert.equal(pending.state.confirmed,null);assert.equal(pending.state.proposals.length,0);
await command('revisor','propose',{supplier_id:pending.comparison.rows[0].supplier_id,comparison_hash:pending.comparison.fingerprint,reason:'Verificar bloqueio por falta de segundo avaliador independente.'},422);
await command('revisor','propose',{supplier_id:pending.comparison.rows[1].supplier_id,comparison_hash:pending.comparison.fingerprint,reason:'Verificar sinalização de adaptação orçamental sem movimento financeiro.'},409);
for(const action of ['select','quote','evaluate','propose','confirm'])await clients.consulta('/api/sourcing/1/'+action,'POST',{},403);
await clients.revisor('/api/sourcing/1/confirm','POST',{},403);
for(const who of ['preparador','revisor','consulta']){const origin=await clients[who]('/api/internal-requisitions/1');assert.equal(origin.record.version,3);assert.equal(origin.record.total_minor,10000);assert.equal(origin.history.length,3);assert(origin.canSourcing);await clients[who]('/purchasing/sourcing/1');}
checks.push('Existing request reused with version 3 and unchanged three-event history');checks.push('Three selected synthetic suppliers, three quotes and one eligible evaluation per quote');checks.push('Comparison and award proposal remain blocked without second eligible evaluator');checks.push('Quoted amount differing from reserved amount is signalled without a budget movement');checks.push('Consulta retains full sourcing read and no preparation/evaluation/proposal/confirmation');checks.push('No persistent identity has award-confirmation right; no award created');checks.push('Protected sourcing screen is accessible to all three existing profiles');
const proof={passed:true,url:'http://127.0.0.1:8039',scope:'1:1:1',request_id:1,checks,confirmation_completed:false,owner_decision_no_fourth_account:true,visual_review:false,checked_at:new Date().toISOString()};fs.writeFileSync('C:/laragon/www/compras/docs/proofs/sgf-sourcing-demo-http.json',JSON.stringify(proof,null,2));console.log(JSON.stringify(proof));