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 = `
#
Nome
Contato
Débito Dinâmico
Status
Cadastrado em
Ações
${sorted.map(c => {
const debt = c.real_debt || 0;
return `
#${c.id}
${c.name}
${c.contact || '–'}
R$ ${debt.toFixed(2)}
${c.active ? 'Ativo' : 'Inativo'}
${formatDate(c.created_at)}
📜 Ver Fiados
Editar
`;
}).join('')}
`;
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: `
`,
footer: `
Cancelar
${isEdit ? 'Salvar' : 'Criar'} `,
});
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: `
Selecionados: 0
Total: R$ 0.00
Forma de Pagamento
${_paymentTypes.map(pt => `${pt.nome || pt.name} `).join('')}
💳 Pagar Selecionados
`,
footer: `Sair `
});
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'
});
}