Files
2026-11-02 20:27:11 +02:00

24 lines
4.9 KiB
JavaScript

const c=JSON.parse(document.querySelector('#contract').textContent);let data;
const el=id=>document.getElementById(id),money=v=>(v/100).toLocaleString('pt-MZ',{minimumFractionDigits:2,maximumFractionDigits:2})+' MZN';
async function api(action,input){const r=await fetch('/api/sourcing/'+c.id+(action?'/'+action:''),{method:action?'POST':'GET',headers:{Accept:'application/json','Content-Type':'application/json','X-CSRF-TOKEN':c.csrf,'X-SGF-Context':c.context,'X-Purchasing-Context':'procurement.internal'},...(action?{body:JSON.stringify({...input,version:data.state.version,operation_key:crypto.randomUUID()})}:{})});const d=await r.json();if(!r.ok)throw Error(d.message??JSON.stringify(d.errors));return d;}
function options(select,rows){select.replaceChildren(...rows.map(([id,text])=>{const o=document.createElement('option');o.value=id;o.textContent=text;return o;}));}
function textNode(tag,text){const node=document.createElement(tag);node.textContent=text;return node;}
async function refresh(){data=await api();el('origin').textContent=`Requisição ${data.request.id}, versão ${data.request.version}, ${money(data.request.total_minor)}; processo versão ${data.state.version}. Mínimos: ${data.policy.minimum_proposals} fornecedores e ${data.policy.minimum_evaluators} avaliadores.`;
const table=document.createElement('table');table.append(textNode('caption',data.comparison.ready?'Comparação completa':'Comparação incompleta: recolher propostas e avaliações elegíveis'));const head=document.createElement('tr');for(const s of ['Fornecedor','Revisão','Valor','Entrega','Pagamento','Avaliações','Pontuação'])head.append(textNode('th',s));table.append(head);
for(const q of data.comparison.rows){const row=document.createElement('tr');for(const s of [data.state.suppliers[q.supplier_id]?.snapshot?.name??q.supplier_id,q.revision,money(q.total_minor),q.delivery_days,q.payment_terms,q.evaluation_count,data.comparison.scores[q.id]===undefined?'—':(data.comparison.scores[q.id]/100).toFixed(2)])row.append(textNode('td',String(s)));table.append(row);}el('comparison').replaceChildren(table);
const supplierRows=data.suppliers.map(s=>[s.id,JSON.parse(typeof s.payload==='string'?s.payload:JSON.stringify(s.payload)).name]);options(el('select').elements.supplier_id,supplierRows);
const selected=Object.entries(data.state.suppliers).map(([id,s])=>[id,s.snapshot.name]);options(el('quote').elements.supplier_id,selected);
const quotes=data.comparison.rows.map(q=>[q.supplier_id,`${data.state.suppliers[q.supplier_id].snapshot.name} · revisão ${q.revision}`]);options(el('evaluation').elements.supplier_id,quotes);options(el('proposal').elements.supplier_id,quotes);
el('lines').replaceChildren(...data.request.data.items.map((line,i)=>{const label=document.createElement('label');label.textContent=`${line.description} · ${line.quantity} · imposto ${line.tax_rate}% `;const input=document.createElement('input');input.name='price_'+i;input.required=true;input.inputMode='decimal';label.append(input);return label;}));
for(const right of ['prepare','evaluate','propose','confirm'])el(right).hidden=!data.rights[right]||data.state.confirmed!==null;
el('proposals').textContent=JSON.stringify(data.state.proposals,null,2);el('history').replaceChildren(...data.history.map(e=>textNode('p',`Versão ${e.version} · ${e.action} · identidade ${e.actor_id} · ${e.created_at}`)));
if(data.state.confirmed)el('message').textContent='Adjudicação confirmada. Processo fechado.';
}
async function submit(form,action,make){form.addEventListener('submit',async e=>{e.preventDefault();try{await api(action,make(new FormData(form)));el('message').textContent='Registado.';await refresh();}catch(error){el('message').textContent=error.message;}});}
submit(el('select'),'select',f=>({supplier_id:Number(f.get('supplier_id'))}));
submit(el('quote'),'quote',f=>({supplier_id:Number(f.get('supplier_id')),reference:f.get('reference'),currency:'MZN',valid_until:f.get('valid_until'),delivery_days:Number(f.get('delivery_days')),payment_terms:f.get('payment_terms'),items:data.request.data.items.map((_,i)=>({line:i,unit_price:f.get('price_'+i)}))}));
submit(el('evaluation'),'evaluate',f=>({supplier_id:Number(f.get('supplier_id')),quote_revision:data.state.quotes[f.get('supplier_id')].revision,quality:Number(f.get('quality')),history:Number(f.get('history')),payment:Number(f.get('payment')),no_conflict:f.get('no_conflict')==='on',reason:f.get('reason')}));
submit(el('proposal'),'propose',f=>({supplier_id:Number(f.get('supplier_id')),comparison_hash:data.comparison.fingerprint,reason:f.get('reason'),delivery_date:f.get('delivery_date'),delivery_location:f.get('delivery_location')}));
submit(el('confirmation'),'confirm',f=>({proposal_index:data.state.proposals.length-1,comparison_hash:data.comparison.fingerprint,reason:f.get('reason')}));
el('refresh').onclick=()=>refresh().catch(e=>el('message').textContent=e.message);refresh().catch(e=>el('message').textContent=e.message);