export async function renderClientes(container) { container.innerHTML = `
`; await loadClientes(); document.getElementById('btn-novo-cliente').addEventListener('click', () => abrirModalCliente()); document.getElementById('btn-refresh-clientes').addEventListener('click', loadClientes); document.getElementById('search-cliente').addEventListener('input', () => filtrarClientes()); document.getElementById('filter-debt').addEventListener('change', () => filtrarClientes()); } let _clientesData = []; let _comandasData = []; let _productsMap = {}; let _paymentTypes = []; async function loadClientes() { const wrap = document.getElementById('clientes-table'); if (!wrap) return; wrap.innerHTML = `
`; // Carrega clientes, produtos, comandas e tipos de pagamento em paralelo const [res, pRes, cRes, ptRes] = await Promise.all([ window.electronAPI.get('/clients'), window.electronAPI.get('/products'), window.electronAPI.get('/comandas'), window.electronAPI.get('/payment-types') ]); if (ptRes.ok) _paymentTypes = ptRes.data; if (pRes.ok) { _productsMap = pRes.data.reduce((acc, p) => { acc[String(p.id)] = { name: p.name, price: parseFloat(p.price || 0) }; return acc; }, {}); } if (cRes.ok) _comandasData = cRes.data; if (!res.ok) { wrap.innerHTML = `
Erro ao carregar clientes.
`; return; } _clientesData = res.data || []; _comandasData = cRes.ok ? (cRes.data || []) : []; // Calcula o débito real de cada cliente somando suas comandas FIADO _clientesData.forEach(c => { const fiados = _comandasData.filter(com => { // Baseado no model Go: json:"client" const cid = com.client; return String(cid) === String(c.id) && String(com.status).toUpperCase() === 'FIADO'; }); c.real_debt = fiados.reduce((acc, com) => { const totalComanda = (com.items || []).reduce((sum, item) => { const pInfo = _productsMap[String(item.product)]; const preco = pInfo ? pInfo.price : parseFloat(item.product_price || 0); return sum + preco; }, 0); return acc + totalComanda; }, 0); }); const comDebito = _clientesData.filter(c => c.real_debt > 0); console.log(`[DEBUG_CLIENTS] Cálculo dinâmico finalizado. Sucesso p/ ${comDebito.length} clientes.`); renderClientesTable(_clientesData); } function renderClientesTable(data) { const wrap = document.getElementById('clientes-table'); if (!wrap) return; if (!data.length) { wrap.innerHTML = `
Nenhum cliente encontrado.
`; return; } // Ordena por maior débito por padrão usando o cálculo dinâmico const sorted = [...data].sort((a, b) => (b.real_debt || 0) - (a.real_debt || 0)); wrap.innerHTML = ` ${sorted.map(c => { const debt = c.real_debt || 0; return ``; }).join('')}
# Nome Contato Débito Dinâmico Status Cadastrado em Ações
#${c.id} ${c.name} ${c.contact || '–'} R$ ${debt.toFixed(2)} ${c.active ? 'Ativo' : 'Inativo'} ${formatDate(c.created_at)}
`; wrap.querySelectorAll('.btn-hist-cli').forEach(btn => { btn.addEventListener('click', () => { const c = _clientesData.find(x => x.id === parseInt(btn.dataset.id)); if (c) abrirHistoricoFiados(c); }); }); wrap.querySelectorAll('.btn-edit-cli').forEach(btn => { btn.addEventListener('click', () => { const c = _clientesData.find(x => x.id === parseInt(btn.dataset.id)); if (c) abrirModalCliente(c); }); }); } function filtrarClientes() { const q = document.getElementById('search-cliente')?.value.toLowerCase() || ''; const debtFltr = document.getElementById('filter-debt')?.value || ''; const filtered = _clientesData.filter(c => { const matchQ = !q || (c.name || '').toLowerCase().includes(q) || (c.contact || '').toLowerCase().includes(q) || String(c.id).includes(q); const debtValue = c.real_debt || 0; const matchDebt = !debtFltr || (debtFltr === 'has-debt' ? debtValue > 0 : debtValue === 0); return matchQ && matchDebt; }); renderClientesTable(filtered); } function abrirModalCliente(cliente = null) { const isEdit = !!cliente; openModal({ title: isEdit ? `Editar: ${cliente.name}` : 'Novo Cliente', body: `
${isEdit ? 'Ajuste via pagamentos/comandas' : ''}
`, footer: ` `, }); document.getElementById('btn-salvar-cli').addEventListener('click', async () => { const data = { name: document.getElementById('cli-nome').value.trim(), contact: document.getElementById('cli-contact').value.trim(), active: document.getElementById('cli-active').value === 'true', }; // Só envia débito na criação se for o caso da API suportar if (!isEdit) { data.debt = parseFloat(document.getElementById('cli-debt').value || 0).toFixed(2); } if (!data.name) return showToast('Informe o nome do cliente.', 'error'); const r = isEdit ? await window.electronAPI.put(`/clients/${cliente.id}`, data) : await window.electronAPI.post('/clients', data); if (r.ok) { showToast(isEdit ? 'Cliente atualizado!' : 'Cliente criado!', 'success'); closeModal(); loadClientes(); } else showToast(r.error, 'error'); }); } async function abrirHistoricoFiados(cliente) { openModal({ title: `📜 Fiados: ${cliente.name}`, body: `
`, footer: `` }); const listContainer = document.getElementById('fiados-list'); const summary = document.getElementById('fiados-summary'); if (!listContainer) return; console.log(`[DEBUG_FIADOS] Procurando fiados para cliente ${cliente.id} (${cliente.name})...`); console.log(`[DEBUG_FIADOS] Total de comandas na memória: ${_comandasData.length}`); // Filtra as comandas FIADO do cliente de forma robusta const fiados = _comandasData.filter(com => { const isFiado = String(com.status).toUpperCase() === 'FIADO'; const isMeuClient = String(com.client) === String(cliente.id); // Usando json:"client" return isFiado && isMeuClient; }); console.log(`[DEBUG_FIADOS] Encontradas:`, fiados); if (!fiados.length) { listContainer.innerHTML = `
Nenhuma comanda pendente para este cliente.
Debug: ${_comandasData.length} comandas totais na memória
`; return; } listContainer.style.maxHeight = '400px'; listContainer.style.overflowY = 'auto'; summary.classList.remove('hidden'); listContainer.innerHTML = fiados.map(f => { const totalComanda = (f.items || []).reduce((acc, it) => { const pInfo = _productsMap[String(it.product)]; const preco = pInfo ? pInfo.price : parseFloat(it.product_price || 0); return acc + preco; }, 0); return `
Comanda #${f.id} — ${f.name || 'Sem nome'}
Abertura: ${formatDate(f.dt_open)}
${f.status}
R$ ${totalComanda.toFixed(2)}
Ver Itens (${(f.items || []).length})
    ${(f.items || []).map(it => { const pInfo = _productsMap[String(it.product)]; const prodName = pInfo ? pInfo.name : (it.product_name || `Produto #${it.product}`); const prodPreco = pInfo ? pInfo.price : parseFloat(it.product_price || 0); return `
  • • ${prodName}
    R$ ${prodPreco.toFixed(2)}
    ${formatDateShort(it.data_time)}
  • `}).join('')}
`; }).join(''); // Lógica de Soma Dinâmica const updateSum = () => { const checks = listContainer.querySelectorAll('.fiado-check:checked'); let sum = 0; checks.forEach(c => sum += parseFloat(c.dataset.total)); document.getElementById('selected-count').textContent = checks.length; document.getElementById('selected-total').textContent = sum.toFixed(2); const btnPagar = document.getElementById('btn-pagar-selecionados'); btnPagar.disabled = checks.length === 0; }; listContainer.querySelectorAll('.fiado-check').forEach(chk => { chk.addEventListener('change', updateSum); }); document.getElementById('btn-pagar-selecionados').addEventListener('click', async () => { const checks = Array.from(listContainer.querySelectorAll('.fiado-check:checked')); const selecionados = checks.map(c => ({ id: parseInt(c.dataset.id), total: parseFloat(c.dataset.total) })); const payTypeId = parseInt(document.getElementById('pay-select-type').value); const totalPrompt = document.getElementById('selected-total').textContent; if (confirm(`Confirmar o recebimento de R$ ${totalPrompt} referente a ${selecionados.length} comanda(s)?`)) { const btn = document.getElementById('btn-pagar-selecionados'); btn.disabled = true; btn.textContent = 'Processando...'; let erros = 0; for (const item of selecionados) { // Encontra os detalhes da comanda para a descrição const comanda = _comandasData.find(c => c.id === item.id); const desc = `RECEBIMENTO FIADO — Comanda #${item.id} (${comanda?.name || '–'})`.trim(); const payload = { value: item.total, type_pay: payTypeId, client: parseInt(cliente.id), description: desc, status: 'CLOSED' }; const r = await window.electronAPI.post(`/comandas/${item.id}/pagar`, payload); if (!r.ok) erros++; } if (erros === 0) { showToast('Todos os pagamentos foram processados!', 'success'); closeModal(); loadClientes(); } else { showToast(`Concluído com ${erros} erro(s). Verifique os recibos.`, 'warning'); loadClientes(); } } }); updateSum(); } function formatDateShort(str) { if (!str) return '–'; const d = new Date(str); return d.toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' }); } function formatDate(str) { if (!str) return '–'; return new Date(str).toLocaleDateString('pt-BR', { day: '2-digit', month: '2-digit', year: 'numeric' }); }