Files
2026-11-11 09:00:17 +02:00

18 lines
9.0 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;
const kinds={assessment:'Livro de saídas QA',form:'Mapa interno QA'},states={prepared:'Preparado',reviewed:'Revisto',refused:'Recusado'};let state,page=1,preview=null,operationKey=null,preparing=false,dirty=false,reading=false,readGeneration=0;
const el=(tag,text,parent)=>{const n=document.createElement(tag);if(text!==null)n.textContent=text;parent.append(n);return n;};const error=e=>$('message').textContent=e.message;
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(async body=>{if(operationKey){const old=await api('/api/fiscal-assessments/receipts/'+operationKey);if(old.status===200)return{status:200,value:{...old.value.result,replayed:true}};if(old.status!==404)return old;}return api('/api/fiscal-assessments/confirm',body);});
function locked(){const busy=Boolean(preparing||reading||preview||op.pending);$('fields').disabled=busy||Boolean(state?.blocked)||Boolean(state?.capabilities&&!Object.values(state.capabilities).some(Boolean));$('filters').disabled=busy;$('refresh').disabled=busy;$('confirm').hidden=!preview||op.uncertain;$('retry').hidden=!op.uncertain;$('cancel').hidden=!preview||op.pending;}
function form(){if(preview||op.pending)return;const kind=$('kind').value,previous=$('action').value;$('action').replaceChildren(new Option('Escolher…',''));for(const [value,label]of [['prepare','Preparar'],['review','Rever versão actual']])if(state?.capabilities[kind+'_'+value])$('action').append(new Option(label,value));if([...$('action').children].some(o=>o.value===previous))$('action').value=previous;fields();}
function fields(){if(preview||op.pending)return;$('inputs').replaceChildren();const kind=$('kind').value,action=$('action').value;if(!kind||!action)return;const current=state[kind];el('p','Versão actual: '+(current?.version??0)+' · '+(states[current?.state]??'Sem preparação'),$('inputs'));
if(action==='review'){el('p','Reveja a preparação actual e o seu fundamento: '+(current?.reason??'não existe preparação'),$('inputs'));const label=el('label','Decisão explícita',$('inputs')),select=el('select',null,label);select.id='decision';select.required=true;for(const [v,t]of [['','Escolher…'],['reviewed','Rever favoravelmente'],['refused','Recusar']])select.append(new Option(t,v));}
else{const label=el('label','Origem de ensaio', $('inputs')),select=el('select',null,label);select.id='source';select.required=true;select.append(new Option('Escolher documento…',''));if(state.source)select.append(new Option(state.source.document_reference+' · '+state.source.total+' '+state.source.currency,String(state.source.output_id)));const l=el('label','Evidência da classificação',$('inputs')),e=el('textarea',null,l);e.id='evidence';e.minLength=10;e.maxLength=2000;e.required=true;el('p','Período explícito da origem: '+state.period+' · classificação apenas de saída QA, sem dedução ou imposto a pagar presumidos.', $('inputs'));}}
async function load(){if(op.pending||preview)throw Error('Conclua ou cancele a conferência actual antes de actualizar.');const generation=++readGeneration,previousState=state;reading=true;locked();try{const r=await api('/api/fiscal-assessments?'+new URLSearchParams({page,per_page:$('size').value,...($('filterKind').value?{kind:$('filterKind').value}:{})}));if(generation!==readGeneration)return;if(r.status!==200)throw Error(r.value.message??'Consulta indisponível.');state=r.value;$('summary').replaceChildren();if(state.blocked)el('p',state.blocked,$('summary'));else{el('p','Origem '+state.source.document_reference+' · base '+state.source.base+' MZN · IVA documental '+state.source.vat+' MZN · total '+state.source.total+' MZN.', $('summary'));el('p','Livro: '+(states[state.assessment?.state]??'por preparar')+(state.assessment_current_review?' · revisão actual':' · exige conferência')+'; mapa: '+(states[state.form?.state]??'por preparar')+(state.form_current_review?' · revisão actual':' · não exportável'),$('summary'));}el('p','IVA dedutível: indeterminado. Imposto a pagar: indeterminado. Sem novos lançamentos ou transmissão oficial.',$('summary'));$('download').replaceChildren();if(state.form_current_review){const a=el('a','Descarregar mapa interno QA (CSV)', $('download'));a.href='/api/fiscal-assessments/forms/'+state.form.id+'/export';el('p','SHA256: '+state.form.data.artifact_sha256,$('download'));}
$('events').replaceChildren();for(const row of state.events.data){const tr=el('tr',null,$('events'));el('td',kinds[row.kind]+' · v'+row.version+' · autor '+row.actor_id,tr);el('td',states[row.state]+' · '+row.created_at,tr);el('td',row.reason,tr);const td=el('td',null,tr),button=el('button','Ver esta versão',td);button.type='button';button.onclick=()=>{if(preview||op.pending||preparing)return;$('detail').textContent=JSON.stringify({objecto:kinds[row.kind],versao:row.version,anterior:row.previous_id,estado:states[row.state],preparador:row.prepared_by,autor:row.actor_id,fundamento:row.reason,registo:row.data,dependencias:row.dependencies},null,2);};if(row.command_reference)el('small','Referência: '+row.command_reference,td);}const pg=state.events;$('page').textContent='Página '+pg.current_page+' de '+pg.last_page+' · '+pg.total+' versões';$('previous').disabled=page<=1;$('next').disabled=page>=pg.last_page;const signature=s=>JSON.stringify([s?.blocked,s?.capabilities,s?.assessment?.id,s?.form?.id,s?.source?.document_sha256]);if(!previousState||signature(previousState)!==signature(state))form();}catch(e){if(generation===readGeneration){state={blocked:e.message};$('summary').textContent=e.message;$('events').replaceChildren();$('download').replaceChildren();$('inputs').replaceChildren();}throw e;}finally{if(generation===readGeneration){reading=false;locked();}}}
$('operation').onsubmit=async e=>{e.preventDefault();if(preparing)return;preparing=true;locked();try{const kind=$('kind').value,action=$('action').value;if(!kind||!action)throw Error('Escolha objecto e acção.');operationKey=crypto.randomUUID();const data=action==='review'?{decision:$('decision').value}:{source_output_id:Number($('source').value),period:state.period,classification:'qa_output_only',evidence:$('evidence').value};const r=await api('/api/fiscal-assessments/preview',{kind,action,version:state[kind]?.version??0,reason:$('reason').value,data},operationKey);if(r.status!==200)throw Error(r.value.message??'Prévia recusada.');preview=r.value;$('referenceKey').value=operationKey;$('impact').replaceChildren();el('h3','Impacto antes da confirmação',$('impact'));const i=preview.impact;for(const [label,value]of [['Estado anterior',states[i.before.state]??'Sem preparação'],['Estado posterior',states[i.after.state]],['Versão posterior',i.after.version],['Documento',i.source.document_reference],['Base MZN',i.source.base],['IVA documental MZN',i.source.vat],['Total MZN',i.source.total],['IVA dedutível','Indeterminado'],['Imposto a pagar','Indeterminado'],['Novos lançamentos','Nenhum'],['Declaração oficial','Não']])el('p',label+': '+value,$('impact'));}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;dirty=false;$('impact').replaceChildren();$('receipt').textContent=kinds[r.kind]+' · versão '+r.version+' · '+states[r.state]+(r.replayed?' · recuperada sem duplicação':' · decisão registada');await load();}catch(e){error(e);if(!op.pending){preview=null;$('impact').replaceChildren();}}finally{locked();}}
$('recovery').onsubmit=async e=>{e.preventDefault();try{const key=$('referenceKey').value.trim().toLowerCase(),r=await api('/api/fiscal-assessments/receipts/'+encodeURIComponent(key));if(r.status!==200)throw Error(r.value.message??'Sem recibo confirmado desta função para a referência.');$('receipt').textContent='Recibo histórico: '+kinds[r.value.result.kind]+' · versão '+r.value.result.version+' · '+states[r.value.result.state]+'. Nenhuma operação repetida.';if(op.pending&&key===operationKey)await finish(true);}catch(e){error(e);}};
$('kind').onchange=form;$('action').onchange=fields;$('operation').oninput=()=>{dirty=true;};$('confirm').onclick=()=>finish();$('retry').onclick=()=>finish(true);$('cancel').onclick=()=>{if(op.pending)return;preview=null;$('impact').replaceChildren();locked();};$('refresh').onclick=()=>load().catch(error);$('apply').onclick=()=>{page=1;load().catch(error);};$('previous').onclick=()=>{page--;load().catch(error);};$('next').onclick=()=>{page++;load().catch(error);};window.addEventListener('beforeunload',e=>{if(dirty||preview||op.pending){e.preventDefault();e.returnValue='';}});load().catch(error);