import React,{useEffect,useRef,useState} from 'react'; import {positionStates,dueStates,exactMoney,checkedPositions,positionsCsv} from './receivable-position-view.js'; export function ReceivablePositionRows({data,onOpen}){return
{data.positions.data.map(r=>)}
Documento / cliente na emissãoEstadoValor de origemRedução / afectação líquidasSaldo reconhecidoVencimento e situaçãoEvidências
{r.reference}{r.client??'Cliente por conferir na origem'}{positionStates[r.state]}{exactMoney(r.original)} MZN{exactMoney(r.reduced)} / {exactMoney(r.allocated)} MZN{r.balance===null?'Ainda não reconhecido':exactMoney(r.balance)+' MZN'}{r.due_date??'Data desconhecida'}{dueStates[r.due_state]}{r.overdue_days===null?'Dias de atraso indeterminados':r.overdue_days+' dia(s) de atraso na referência'}Revisão {r.revision}
{data.positions.data.length===0&&

Nenhuma posição corresponde aos filtros autorizados.

}
;} export default function ReceivablePositionsWorkspace({context,api,onOpen}){ const [form,setForm]=useState({reference_on:'',search:'',state:'',due_state:'',per_page:'25'}),[query,setQuery]=useState(null),[data,setData]=useState(null),[error,setError]=useState(''),[loading,setLoading]=useState(false);const basis=useRef(null); useEffect(()=>{let current=true;setData(null);setError('');if(!query)return()=>{current=false;};setLoading(true);const expected=query.page>1?basis.current:null;api('/api/service-receivables/positions?'+new URLSearchParams(Object.entries(query).filter(([,v])=>v!==''))).then(v=>{const result=checkedPositions(v,query,expected);if(current){setData(result);basis.current=JSON.stringify(result.basis);}}).catch(e=>{if(current)setError(e.message);}).finally(()=>{if(current)setLoading(false);});return()=>{current=false;};},[context.token,query]); const load=(value,page=1)=>{if(!/^\d{4}-\d{2}-\d{2}$/.test(value.reference_on)){setError('Escolha uma data explícita para conferir vencimentos.');return;}if(page===1)basis.current=null;setQuery({...value,page});}; const download=()=>{const url=URL.createObjectURL(new Blob(['\uFEFF'+positionsCsv(data,query)],{type:'text/csv;charset=utf-8'}));const a=document.createElement('a');a.href=url;a.download='sgf-posicoes-clientes-pagina-'+data.positions.current_page+'.csv';a.click();setTimeout(()=>URL.revokeObjectURL(url),1000);}; return

FINANCEIRO

Posições de clientes

Saldos actuais do livro de recebíveis. A data escolhida classifica apenas o vencimento; não reconstitui saldos históricos. Propostas por confirmar e recusadas não entram no saldo reconhecido.

{e.preventDefault();load(form);}}>
{loading&&

A consultar a página e os efeitos confirmados…

}{error&&

{error}

Uma consulta indisponível não significa saldo zero.

{query&&}
}{data&&<>
Saldo reconhecido do filtro completo
{exactMoney(data.totals.recognized_balance)} MZN
Posições reconhecidas
{data.totals.recognized_positions}
Propostas sem reconhecimento
{data.totals.unrecognized_proposals}
Vencimento desconhecido com saldo
{data.totals.unknown_due_positions}

{data.positions.total} registo(s) · página {data.positions.current_page} de {data.positions.last_page} · leitura {data.observed_at}. Referência para vencimentos: {data.reference_on}.

}

Documento, recebimento confirmado, afectação e saldo são factos distintos. Esta lista não cria cobranças, juros, dívida ou lançamentos, nem conclui que uma posição liquidada corresponde a dinheiro recebido. Abrir uma posição conserva a escolha explícita e revalida a autoridade no percurso original.

; }