import React,{useEffect,useMemo,useState,useRef} from 'react'; import {fiscalMapDownload} from './statement-download.js'; import {WorkshopStockOperation} from './workshop-stock-operation.js'; import {checkedObligations,obligationsPageCsv,obligationLabels,decisionLabels} from './fiscal-obligations-view.js'; const qs=q=>new URLSearchParams(Object.entries(q).filter(([,v])=>v!==''&&v!==null)).toString(); export function FiscalObligationRows({data,onSelect,disabled}){return
{data.records.data.map(r=>)}
Registo / versãoCondição aprovada actualPrazo / referênciaResponsável / evidênciaAutor e fundamento da versãoConferir
{r.record_key}v{r.version} · {decisionLabels[r.state]} · {r.data.operation}{obligationLabels[r.effective?.administrative_status??'unapproved']}Mapa interno #{r.data.form_id} · {r.data.period}{!r.current_source_matches&&Origem alterada: decisão bloqueada.}{r.effective?.due_on??'Prazo desconhecido'}{r.days_overdue===null?'Atraso indeterminado':r.days_overdue+' dia(s) à referência '+data.reference_on}{r.effective?'QA'+r.effective.responsible_id:'Por aprovar'}{r.effective?.evidence??'Preparação não equivale a aprovação.'}{r.effective?.delivery_evidence&&SHA da evidência interna: {r.effective.delivery_evidence.export_sha256}. Entrega apenas simulada.}QA{r.actor_id} · {r.created_at}{r.reason}
{!data.records.data.length&&

Nenhum registo corresponde ao âmbito/filtros.

}
;} export default function FiscalObligationsWorkspace({context,api}){ const initial={reference_on:'',period:'',status:'',due_state:'',due_from:'',due_to:'',per_page:'25'},[filters,setFilters]=useState(initial),[query,setQuery]=useState(null),[data,setData]=useState(null),[selected,setSelected]=useState(null),[form,setForm]=useState(null),[preview,setPreview]=useState(null),[busy,setBusy]=useState(false),[error,setError]=useState(''),[notice,setNotice]=useState(''),[receiptKey,setReceiptKey]=useState(''),[receipt,setReceipt]=useState(null); const mounted=useRef(true),generation=useRef(context.token);generation.current=context.token;useEffect(()=>{mounted.current=true;return()=>{mounted.current=false;};},[context.token]); const operation=useMemo(()=>new WorkshopStockOperation(api),[context.token]),locked=busy||operation.uncertain; useEffect(()=>{if(!query)return;let active=true;setData(null);setBusy(true);setError('');api('/api/fiscal-obligations?'+qs(query)).then(r=>{if(active)setData(checkedObligations(r,query));}).catch(e=>{if(active)setError(e.message);}).finally(()=>{if(active)setBusy(false);});return()=>{active=false;};},[context.token,query]); const choose=r=>{setSelected(r);setReceipt(null);setNotice('');}; function start(op,review=false,newRecord=false){const target=newRecord?null:selected;if(newRecord)setSelected(null);const source=data.source,e=target?.effective;setPreview(null);setError('');setForm({action:review?'review':'prepare',record_key:target?.record_key??crypto.randomUUID(),version:target?.version??0,reason:'',data:review?{decision:''}:{operation:op,form_id:source.form_id,period:source.period,responsible_id:e?.responsible_id??'',due_on:e?.due_on??'',evidence:'',export_sha256:''},unknown:review?false:Boolean(e&&e.due_on===null)});} async function prepare(e){e.preventDefault();setBusy(true);setError('');try{const{unknown,...input}=form;if(input.action==='prepare')input.data={...input.data,responsible_id:Number(input.data.responsible_id),due_on:unknown?null:input.data.due_on,export_sha256:input.data.operation==='delivery'?input.data.export_sha256:null};const key=crypto.randomUUID(),p=await api('/api/fiscal-obligations/preview','POST',input,undefined,key);setPreview({...p,key});}catch(e){setError(e.message);}finally{setBusy(false);}} async function confirm(retry=false){setBusy(true);setError('');try{const out=retry?await operation.retry():await operation.send('/api/fiscal-obligations/confirm',{preview:preview.preview},preview.key);setReceiptKey(preview.key);setNotice('v'+out.version+' · '+decisionLabels[out.state]+(out.replayed?' · recibo recuperado':''));setSelected(null);setForm(null);setPreview(null);setQuery(q=>({...q,page:1}));}catch(e){setError(e.message);if(!operation.uncertain)setPreview(null);}finally{setBusy(false);}} async function downloadMap(){const token=context.token;setBusy(true);setError('');try{const result=await fiscalMapDownload({id:data.source.form_id,token,sha256:data.source.export_sha256,isCurrent:()=>mounted.current&&generation.current===token});const url=URL.createObjectURL(new Blob([result.bytes],{type:'text/csv;charset=utf-8'}));const a=document.createElement('a');a.href=url;a.download='sgf-mapa-interno-IVA-QA.csv';a.click();setTimeout(()=>URL.revokeObjectURL(url),1000);setNotice('Mapa interno descarregado e SHA verificado; sem submissão.');}catch(e){if(mounted.current&&generation.current===token)setError(e.message);}finally{if(mounted.current&&generation.current===token)setBusy(false);}} const exportPage=()=>{const url=URL.createObjectURL(new Blob(['\uFEFF'+obligationsPageCsv(data,query)],{type:'text/csv;charset=utf-8'}));const a=document.createElement('a');a.href=url;a.download='sgf-obrigacoes-QA-pagina-'+data.records.current_page+'.csv';a.click();setTimeout(()=>URL.revokeObjectURL(url),1000);}; async function recover(){setBusy(true);setError('');try{setReceipt(await api('/api/fiscal-obligations/receipts/'+encodeURIComponent(receiptKey)));}catch(e){setError(e.message);}finally{setBusy(false);}} return

CONTABILIDADE

Obrigações e prazos administrativos QA

Prazo artificial e entrega apenas simulada ligados ao mapa interno revisto. Sem prazo legal, submissão à AT, apuramento ou pagamento. A referência classifica o prazo; não reconstrói um estado histórico.

{error&&

{error}

}{notice&&

{notice}

}
{e.preventDefault();setSelected(null);setQuery({...filters,page:1});}}>
{data&&<>{data.blocked&&

{data.blocked} Pode consultar o histórico; não há origem válida para novas decisões.

}{data.source&&

Origem disponível para escolha: mapa interno #{data.source.form_id} v{data.source.form_version}, período{data.source.period}, {data.source.document_reference}. SHA da exportação: {data.source.export_sha256}.

}{data.capabilities.prepare&&data.source&&!query.record_key&&}

Página{data.records.current_page} de{data.records.last_page} · {data.records.total} registo(s). O estado temporal do prazo é independente da entrega/cancelamento.

{query.record_key&&} {selected&&

Registo escolhido: {selected.record_key} · v{selected.version}

{decisionLabels[selected.state]} · proposta: {selected.data.operation}, data{selected.data.due_on??'desconhecida'}, responsável QA{selected.data.responsible_id} · {selected.data.evidence}

Impacto registado nesta versão: antes v{selected.impact.before.version}, {obligationLabels[selected.impact.before.effective?.administrative_status??'unapproved']}, prazo{selected.impact.before.effective?.due_on??'desconhecido'}; depois v{selected.impact.after.version}, {obligationLabels[selected.impact.after.effective?.administrative_status??'unapproved']}, prazo{selected.impact.after.effective?.due_on??'desconhecido'}.

{!query.record_key&&data.source&&selected.current_source_matches&&<>{data.capabilities.prepare&&selected.effective?.administrative_status!=='cancelled'&&<>{selected.effective?<>{selected.effective.administrative_status!=='delivery_simulated'&&}:}}{data.capabilities.review&&selected.state==='prepared'&&}}
}} {form&&

{form.action==='prepare'?'Preparação individual':'Revisão distinta'} · versão observada {form.version}

{form.action==='review'?:<>

Operação: {form.data.operation} · mapa#{form.data.form_id} · período{form.data.period}.