Files
SGF/public/sgf-service-documents.mjs
2026-11-11 09:00:17 +02:00

19 lines
8.3 KiB
JavaScript

import {confirmation} from './sgf-stock-final.mjs';
const $=id=>document.getElementById(id),csrf=document.querySelector('meta[name="csrf-token"]').content,context=document.querySelector('meta[name="sgf-context"]').content;let state,selected=null,page=1,history=null,preview=null,documentKey=null,preparing=false;
const labels={receipt:'Recibo',credit_note:'Nota de crédito',invoice_receipt:'Factura-recibo'};
async function api(path,body,key){const r=await fetch(path,{credentials:'same-origin',headers:{Accept:'application/json','Content-Type':'application/json','X-CSRF-TOKEN':csrf,'X-SGF-Context':context,...(key?{'Idempotency-Key':key}:{})},...(body?{method:'POST',body:JSON.stringify(body)}:{})});return {status:r.status,value:await r.json()};}
const op=confirmation(body=>api('/api/service-documents/confirm',body));function error(e){$('message').textContent=e.message;}function el(tag,text,parent){const n=document.createElement(tag);if(text!==null)n.textContent=text;parent.append(n);return n;}
function locked(){const b=Boolean(preparing||preview||op.pending);$('fields').disabled=b;$('filters').disabled=b;$('confirm').hidden=!preview||op.uncertain;$('retry').hidden=!op.uncertain;$('cancel').hidden=!preview||op.pending;}
function field(id,label,type='text',value=''){const l=el('label',label,$('inputs')),i=el('input',null,l);i.id=id;i.type=type;i.required=true;i.value=value;return i;}
async function options(select,view,invoice=null){const q=new URLSearchParams({view,per_page:100});if(invoice)q.set('invoice_id',invoice);const r=await api('/api/service-documents/options?'+q);if(r.status!==200)throw Error(r.value.message??'Consulta recusada.');select.replaceChildren();el('option','Escolher…',select).value='';for(const row of r.value.records.data)el('option',view==='invoices'?`${row.series_code}/${row.number}`:`Afectação ${row.id} · ${row.amount} MZN`,select).value=String(row.id);}
async function form(){if(!state)return;const action=$('action').value,kind=$('kind').value;$('inputs').replaceChildren();if(action==='prepare'){
if(kind==='invoice_receipt'){el('p','Nova origem QA: base862,07 + imposto documental137,93 =1000,00 MZN. A emissão e a afectação só ocorrem na confirmação final.', $('inputs'));field('payment_date','Data do recebimento fictício','date');field('evidence','Comprovativo da intenção financeira');}
else{const l=el('label','Factura de origem nova', $('inputs')),i=el('select',null,l);i.id='invoice';i.required=true;await options(i,'invoices');if(kind==='receipt'){const l=el('label','Afectação efectiva', $('inputs')),a=el('select',null,l);a.id='allocation';a.required=true;el('option','Escolha primeiro a factura',a).value='';i.onchange=()=>{if(i.value)options(a,'allocations',i.value).catch(error);else{a.replaceChildren();el('option','Escolha primeiro a factura',a).value='';}};}else{field('amount','Crédito nominal QA (MZN)','text','100.00');field('explanation','Motivo da redução; sem devolução de dinheiro');}}
}else if(action==='review'){const l=el('label','Decisão de revisão', $('inputs')),s=el('select',null,l);s.id='decision';s.required=true;for(const [v,t]of [['','Escolher…'],['approved','Aprovar'],['refused','Recusar']])el('option',t,s).value=v;}}
function choose(row){selected=row;documentKey=row?.document_key??crypto.randomUUID();$('selected').textContent=row?`${labels[row.kind]} · ${row.document_key}, versão ${row.version}, ${row.state}.`:'Novo processo: escolha explicitamente o documento e a origem.';$('action').replaceChildren();el('option','Escolher…',$('action')).value='';const c=state.capabilities??{};if(c.prepare&&!row)el('option','Preparar', $('action')).value='prepare';if(c.review&&row?.state==='draft'&&row.prepared_by!==state.actor_id)el('option','Conferir versão', $('action')).value='review';if(c.issue&&row?.state==='approved'&&($('kind').value!=='credit_note'||c.credit_apply))el('option','Emitir esta versão', $('action')).value='issue';const events=row?.output?.cancellations??[],last=events.at(-1);if(row?.state==='issued'&&last?.state!=='cancelled'){if(c.cancel_prepare)el('option','Preparar anulação técnica',$('action')).value='cancel_prepare';if(c.cancel_review&&last?.state==='prepared'&&last.actor_id!==state.actor_id)el('option','Conferir anulação técnica',$('action')).value='cancel_review';}form().catch(error);}
async function load(){const q=new URLSearchParams({kind:$('kind').value,page,per_page:$('size').value});if(history)q.set('document_key',history);const r=await api('/api/service-documents?'+q);if(r.status!==200)throw Error(r.value.message??'Consulta recusada.');state=r.value;$('records').replaceChildren();for(const row of state.documents.data){const tr=el('tr',null,$('records'));el('td',row.document_key,tr);el('td',`${row.version} / ${row.state}`,tr);el('td',`${row.actor_id} · ${row.created_at}`,tr);const td=el('td',null,tr);el('button','Escolher versão',td).onclick=()=>choose(row);el('button','Histórico',td).onclick=()=>{history=row.document_key;page=1;load().catch(error);};if(row.output){for(const event of row.output.cancellations??[])el('p','Anulação '+event.version+': '+event.state+' · '+event.actor_id+' · '+event.reason,td);const a=el('a','Abrir PDF de teste',td);a.href='/api/service-documents/'+row.output.id+'/pdf';a.target='_blank';a.rel='noopener';}}const p=state.documents;$('page').textContent=`Página ${p.current_page} de ${p.last_page}`;$('previous').disabled=page<=1;$('next').disabled=page>=p.last_page;$('new').hidden=!state.capabilities?.prepare;choose(null);}
function data(){const v=id=>$(id)?.value??'';if($('action').value==='review')return {decision:v('decision')};if(['issue','cancel_prepare','cancel_review'].includes($('action').value))return {};if($('kind').value==='invoice_receipt'){const src=state.inputs.invoice_receipt;return {draft_id:src.draft_id,series_id:src.series_id,rule_id:src.rule_id,payment_date:v('payment_date'),evidence:v('evidence')};}return $('kind').value==='receipt'?{invoice_id:Number(v('invoice')),allocation_id:Number(v('allocation'))}:{invoice_id:Number(v('invoice')),amount:v('amount'),explanation:v('explanation')};}
$('operation').onsubmit=async e=>{e.preventDefault();if(preparing)return;preparing=true;locked();try{$('message').textContent='';const r=await api('/api/service-documents/preview',{kind:$('kind').value,action:$('action').value,document_key:documentKey,version:selected?.version??0,reason:$('reason').value,data:data()},crypto.randomUUID());if(r.status!==200)throw Error(r.value.message??'Conferência recusada.');preview=r.value;$('impact').replaceChildren();for(const [k,t]of [['kind','Documento'],['action','Acção'],['state','Estado posterior'],['amount','Montante MZN'],['financial_reduction','Redução do recebível'],['atomic_invoice_receipt','Emissão/recebimento/afectação atómicos'],['balance_increase_on_confirmation','Aumento da dívida ao confirmar anulação'],['balance_before','Dívida anterior'],['balance_after_confirmation','Dívida após confirmar anulação'],['invoice_preserved','Factura original preservada'],['money_refunded','Devolução de dinheiro']])if(preview.impact[k]!==undefined)el('p',`${t}: ${typeof preview.impact[k]==='boolean'?(preview.impact[k]?'Sim':'Não'):(labels[preview.impact[k]]??preview.impact[k])}`,$('impact'));locked();}catch(e){error(e);}finally{preparing=false;locked();}};
async function finish(retry=false){try{const r=await(retry?op.retry():op.run({preview:preview.preview}));preview=null;$('receipt').textContent=`Versão ${r.version} confirmada. ${r.replayed?'Recuperada sem duplicação.':''}`;$('impact').replaceChildren();await load();}catch(e){error(e);if(!op.pending){preview=null;$('impact').replaceChildren();}}finally{locked();}}
$('action').onchange=()=>form().catch(error);$('confirm').onclick=()=>finish();$('retry').onclick=()=>finish(true);$('cancel').onclick=()=>{if(op.pending)return;preview=null;$('impact').replaceChildren();locked();};$('new').onclick=()=>choose(null);$('all').onclick=()=>{history=null;page=1;load().catch(error);};$('refresh').onclick=()=>load().catch(error);for(const id of ['kind','size'])$(id).onchange=()=>{history=null;page=1;load().catch(error);};$('previous').onclick=()=>{page--;load().catch(error);};$('next').onclick=()=>{page++;load().catch(error);};window.addEventListener('beforeunload',e=>{if(preview||op.pending){e.preventDefault();e.returnValue='';}});$('kind').value=document.querySelector('meta[name="document-kind"]').content;load().catch(error);