Compare commits

...

4 Commits

Author SHA1 Message Date
root 7e57d30863 v1.2.7: conserta 4 bugs — impressao, enviar, dia anterior, localStorage
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-24 13:38:00 +00:00
root 7a93a30c22 v1.2.6: init() restaura operador+turno; tar inclui WebView2Loader.dll
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-24 13:11:43 +00:00
root 230304e878 v1.2.5 — fixes: diferenca=fechamento-esperado; btn-enviar func; mapas tabela Supabase
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
MAPA SUPABASE:
Tabela: fechamentos_web
  uuid | loja | data | operador | turno
  saldo_troco | saldo_esperado | fechamento | diferenca
  sync_status | observacoes | dados (JSON com tudo)
  atualizado_em

Tabela: despesas (via JSON em dados->despesas)
Tabela: sangrias (via JSON em dados->sangrias)
Tabela: vales (via JSON em dados->vales)
Tabela: receber (via JSON em dados->receber)
Tabela: pixcnpj (via JSON em dados->pixcnpj)
Tabela: cancelamentos (via JSON em dados->cancelamentos)
Tabela: cartoes (dados->cartoes: credito/debito/alimentacao/pix arrays)

Tabela: listas_personalizadas (operadores/gerentes/despesas_custom/clientes)
  loja | operadores[] | gerentes[] | despesas_custom[] | clientes[]

FUNCOES RPC:
  upsert_fechamento() — upsert fechamentos_web por uuid
  upsert_listas() — upsert listas_personalizadas por loja

BOTÃO DIA ANTERIOR: usa sb_listar_recentes → tabela fechamentos_web
  Seleciona por numero (1=N Mais Recente, 2=2o Mais Recente...)
  ou automatico: data-1dia
2026-06-24 01:32:18 +00:00
root d185f82bd5 Revert "v1.2.3: btn voltar no preview; relatorio corrigido (fechamento-esperado); cartoes com detalhes p/ Supabase"
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
This reverts commit 2253d45d00.
2026-06-24 01:18:57 +00:00
7 changed files with 188 additions and 153 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+188 -153
View File
@@ -1022,7 +1022,9 @@ function init() {
const today = new Date().toISOString().split('T')[0]; const today = new Date().toISOString().split('T')[0];
setVal('f-data', today); setVal('f-data', today);
estado.data = today; estado.data = today;
setVal('f-loja', estado.loja); setVal('f-loja', estado.loja || 'União');
setVal('f-operador', estado.operador || '');
setVal('f-turno', estado.turno || '');
syncHeader(); syncHeader();
PRE_FILL_TABLES.forEach(ensureOneRow); PRE_FILL_TABLES.forEach(ensureOneRow);
Object.keys(TABLES).forEach(renderTable); Object.keys(TABLES).forEach(renderTable);
@@ -1034,7 +1036,7 @@ const LS_KEY = 'fc_v7';
const CFG_KEY = 'fc_cfg'; const CFG_KEY = 'fc_cfg';
function buildEstadoForBackend() { function buildEstadoForBackend() {
// Monta o objeto que vai para o Supabase — inclui TUDO, todos os detalhes // Monta o objeto exactly as o backend espera (storage.rs → salvar_fechamento)
return { return {
uuid: estado.uuid, uuid: estado.uuid,
id_local: estado.id_local, id_local: estado.id_local,
@@ -1042,22 +1044,20 @@ function buildEstadoForBackend() {
data: estado.data, data: estado.data,
operador: estado.operador, operador: estado.operador,
turno: estado.turno, turno: estado.turno,
saldo_troco: parseNum(val('sys-troco')), saldo_troco: parseNum(val('sys-troco')), // troco do sistema
saldo_esperado: parseNum(val('f-saldo-esperado')), saldo_esperado: parseNum(val('f-saldo-esperado')),
fechamento: parseNum(val('f-fech-dinheiro')) + parseNum(val('f-fech-cartoes')) + parseNum(val('f-fech-receber')), fechamento: parseNum(val('f-fech-dinheiro')) + parseNum(val('f-fech-cartoes')) + parseNum(val('f-fech-receber')),
diferenca: parseNum(val('f-saldo-esperado')) - (parseNum(val('f-fech-dinheiro')) + parseNum(val('f-fech-cartoes')) + parseNum(val('f-fech-receber'))),
despesas: estado.despesas.map(r => ({ desc: r.desc || '', obs: r.obs || '', valor: r.valor || 0 })), despesas: estado.despesas.map(r => ({ desc: r.desc || '', obs: r.obs || '', valor: r.valor || 0 })),
sangrias: estado.sangrias.map(r => ({ hora: r.hora || '', retirada: r.retirada || 0, gerente: r.gerente || '' })), sangrias: estado.sangrias.map(r => ({ hora: r.hora || '', retirada: r.retirada || 0, gerente: r.gerente || '' })),
pixcnpj: estado.pixcnpj.map(r => ({ nome: r.nome || '', valor: r.valor || 0 })), pixcnpj: estado.pixcnpj.map(r => ({ nome: r.nome || '', valor: r.valor || 0 })),
vales: estado.vales.map(r => ({ nome: r.nome || '', obs: r.obs || '', valor: r.valor || 0 })), vales: estado.vales.map(r => ({ nome: r.nome || '', obs: r.obs || '', valor: r.valor || 0 })),
receber: estado.receber.map(r => ({ nome: r.nome || '', valor: r.valor || 0 })), receber: estado.receber.map(r => ({ nome: r.nome || '', valor: r.valor || 0 })),
cancelamentos: estado.cancelamentos.map(r => ({ numero: r.numero || '', valor: r.valor || 0, motivo: r.motivo || '' })), cancelamentos: estado.cancelamentos.map(r => ({ numero: r.numero || '', valor: r.valor || 0, motivo: r.motivo || '' })),
// Cartões com detalhes completos (não só totais)
cartoes: { cartoes: {
credito: estado.cartoes.credito.map(r => ({ valor: r.valor || 0, obs: r.obs || '' })), credito: estado.cartoes.credito.map(r => r.valor || 0),
debito: estado.cartoes.debito.map(r => ({ valor: r.valor || 0, obs: r.obs || '' })), debito: estado.cartoes.debito.map(r => r.valor || 0),
alimentacao: estado.cartoes.alimentacao.map(r => ({ valor: r.valor || 0, obs: r.obs || '' })), alimentacao: estado.cartoes.alimentacao.map(r => r.valor || 0),
pix: estado.cartoes.pix.map(r => ({ valor: r.valor || 0, obs: r.obs || '' })), pix: estado.cartoes.pix.map(r => r.valor || 0),
}, },
observacoes: estado.observacoes, observacoes: estado.observacoes,
sync_status: estado.sync_status, sync_status: estado.sync_status,
@@ -1084,8 +1084,44 @@ function loadState() {
const raw = localStorage.getItem(LS_KEY); const raw = localStorage.getItem(LS_KEY);
if (!raw) return false; if (!raw) return false;
const saved = JSON.parse(raw); const saved = JSON.parse(raw);
// Merge carefully (cartoes may be in old format) // Restore simple fields
Object.assign(estado, saved); estado.uuid = saved.uuid || estado.uuid;
estado.id_local = saved.id_local !== undefined ? saved.id_local : null;
estado.loja = saved.loja || estado.loja;
estado.data = saved.data || estado.data;
estado.operador = saved.operador || estado.operador;
estado.turno = saved.turno || estado.turno;
estado.saldo_troco = saved.saldo_troco || 0;
estado.saldo_esperado = saved.saldo_esperado || 0;
estado.fechamento = saved.fechamento || 0;
estado.observacoes = saved.observacoes || '';
estado.sync_status = saved.sync_status || 'local';
// Restore tables (always as arrays of objects)
estado.despesas = Array.isArray(saved.despesas) ? saved.despesas : [];
estado.sangrias = Array.isArray(saved.sangrias) ? saved.sangrias : [];
estado.pixcnpj = Array.isArray(saved.pixcnpj) ? saved.pixcnpj : [];
estado.vales = Array.isArray(saved.vales) ? saved.vales : [];
estado.receber = Array.isArray(saved.receber) ? saved.receber : [];
estado.cancelamentos = Array.isArray(saved.cancelamentos) ? saved.cancelamentos : [];
// Restore cartoes — each entry must be { valor: number }
if (saved.cartoes) {
estado.cartoes.credito = Array.isArray(saved.cartoes.credito)
? saved.cartoes.credito.map(v => ({ valor: typeof v === 'object' && v !== null ? (v.valor || 0) : (parseNum(v) || 0) }))
: [];
estado.cartoes.debito = Array.isArray(saved.cartoes.debito)
? saved.cartoes.debito.map(v => ({ valor: typeof v === 'object' && v !== null ? (v.valor || 0) : (parseNum(v) || 0) }))
: [];
estado.cartoes.alimentacao = Array.isArray(saved.cartoes.alimentacao)
? saved.cartoes.alimentacao.map(v => ({ valor: typeof v === 'object' && v !== null ? (v.valor || 0) : (parseNum(v) || 0) }))
: [];
estado.cartoes.pix = Array.isArray(saved.cartoes.pix)
? saved.cartoes.pix.map(v => ({ valor: typeof v === 'object' && v !== null ? (v.valor || 0) : (parseNum(v) || 0) }))
: [];
}
// Restore fechamento_contado
if (saved.fechamento_contado) {
estado.fechamento_contado = saved.fechamento_contado;
}
setVal('f-data', estado.data || ''); setVal('f-data', estado.data || '');
setVal('f-operador', estado.operador || ''); setVal('f-operador', estado.operador || '');
setVal('f-turno', estado.turno || ''); setVal('f-turno', estado.turno || '');
@@ -1093,9 +1129,10 @@ function loadState() {
setVal('f-obs', estado.observacoes || ''); setVal('f-obs', estado.observacoes || '');
setVal('f-saldo-esperado', estado.saldo_esperado === 0 ? '' : fmtNum(estado.saldo_esperado)); setVal('f-saldo-esperado', estado.saldo_esperado === 0 ? '' : fmtNum(estado.saldo_esperado));
setVal('sys-troco', estado.saldo_troco === 0 ? '' : fmtNum(estado.saldo_troco)); setVal('sys-troco', estado.saldo_troco === 0 ? '' : fmtNum(estado.saldo_troco));
setVal('f-fech-dinheiro', estado.fechamento_contado && estado.fechamento_contado.dinheiro ? fmtNum(estado.fechamento_contado.dinheiro) : ''); const fc = estado.fechamento_contado || {};
setVal('f-fech-cartoes', estado.fechamento_contado && estado.fechamento_contado.cartoes ? fmtNum(estado.fechamento_contado.cartoes) : ''); setVal('f-fech-dinheiro', fc.dinheiro ? fmtNum(fc.dinheiro) : '');
setVal('f-fech-receber', estado.fechamento_contado && estado.fechamento_contado.receber ? fmtNum(estado.fechamento_contado.receber) : ''); setVal('f-fech-cartoes', fc.cartoes ? fmtNum(fc.cartoes) : '');
setVal('f-fech-receber', fc.receber ? fmtNum(fc.receber) : '');
syncHeader(); syncHeader();
PRE_FILL_TABLES.forEach(ensureOneRow); PRE_FILL_TABLES.forEach(ensureOneRow);
Object.keys(TABLES).forEach(renderTable); Object.keys(TABLES).forEach(renderTable);
@@ -1195,37 +1232,42 @@ async function tauriEnviar() {
// ─── Print Preview Window ────────────────────────────────────────────────── // ─── Print Preview Window ──────────────────────────────────────────────────
function buildPrintHTML(data) { function buildPrintHTML(data) {
const fmt = v => 'R$ ' + (v || 0).toFixed(2).replace('.', ','); const fmt = v => 'R$ ' + (v || 0).toFixed(2).replace('.', ',');
const vSangrias = (data.sangrias||[]).reduce((s,r) => s+(parseFloat(r.retirada)||0), 0); const diferenca = (data.fechamento || 0) - (data.saldo_esperado || 0);
const vDespesas = (data.despesas||[]).reduce((s,r) => s+(parseFloat(r.valor)||0), 0);
const vVales = (data.vales||[]).reduce((s,r) => s+(parseFloat(r.valor)||0), 0);
const vReceber = (data.receber||[]).reduce((s,r) => s+(parseFloat(r.valor)||0), 0);
const vPixCnpj = (data.pixcnpj||[]).reduce((s,r) => s+(parseFloat(r.valor)||0), 0);
const vCredito = data.cartoes?.credito?.reduce?.((s,r) => s+(parseFloat(r)||0), 0) || data.credito || 0;
const vDebito = data.cartoes?.debito?.reduce?.((s,r) => s+(parseFloat(r)||0), 0) || data.debito || 0;
const vAliment = data.cartoes?.alimentacao?.reduce?.((s,r) => s+(parseFloat(r)||0), 0) || data.alimentacao || 0;
const vPixCartao = data.cartoes?.pix?.reduce?.((s,r) => s+(parseFloat(r)||0), 0) || 0;
const vCancel = (data.cancelamentos||[]).reduce((s,r) => s+(parseFloat(r.valor)||0), 0);
// Diferença = Fechamento Total Saldo Esperado
const vFechamento = (data.fechamento || 0);
const diferenca = vFechamento - (data.saldo_esperado || 0);
const diffStr = diferenca >= 0 ? `+${fmt(diferenca)}` : fmt(diferenca); const diffStr = diferenca >= 0 ? `+${fmt(diferenca)}` : fmt(diferenca);
const diffColor = diferenca === 0 ? '#2e7d32' : (diferenca > 0 ? '#e65100' : '#c0272d'); const diffColor = diferenca === 0 ? '#2e7d32' : (diferenca > 0 ? '#e65100' : '#c0272d');
const fmtRow = (label, valor) => let rows = '';
`<div class="row"><span>${label}</span><span>${fmt(valor)}</span></div>`; // Cancelamentos (sum the array first — single cancelamentos is a float total, array is from confirm-print)
const fmtDetail = (label, valor, obs) => const cancelTotal = Array.isArray(data.cancelamentos)
`<div class="row sub"><span>${label}</span><span>${fmt(valor)}</span></div>`; ? data.cancelamentos.reduce((s, c) => s + (parseNum(c.valor || 0) || 0), 0)
: (data.cancelamentos || 0);
const mkDet = (arr, fmtFn) => { // PIX total (flat number from tauriRecibo; pixcnpj is also flat total)
if (!arr || arr.length === 0) return ''; const pixTotal = data.pix || data.voucher || 0;
return arr.map(r => { const pixCnpjTotal = typeof data.pixcnpj === 'number' ? data.pixcnpj : 0;
if (!r) return ''; // Sangrias
const v = fmtFn(r); if (data.sangrias && data.sangrias.length > 0) {
const nome = r.nome || r.desc || r.hora || r.numero || ''; data.sangrias.forEach(s => { if (s.hora || s.retirada) rows += `<tr><td>${s.hora||''}</td><td>${fmt(s.retirada)}</td><td>${s.gerente||''}</td></tr>`; });
const obs = r.obs || r.motivo || ''; }
return fmtDetail(nome, v, obs); // Despesas
}).join(''); if (data.despesas && data.despesas.length > 0) {
}; data.despesas.forEach(d => { if (d.desc || d.valor) rows += `<tr><td>${d.desc||''}</td><td>${fmt(d.valor)}</td><td>${d.obs||''}</td></tr>`; });
}
// Vales
if (data.vales && data.vales.length > 0) {
data.vales.forEach(v => { if (v.nome || v.valor) rows += `<tr><td>${v.nome||''}</td><td>${fmt(v.valor)}</td><td>${v.obs||''}</td></tr>`; });
}
// Receber
if (data.receber && data.receber.length > 0) {
data.receber.forEach(r => { if (r.nome || r.valor) rows += `<tr><td>${r.nome||''}</td><td>${fmt(r.valor)}</td><td></td></tr>`; });
}
// PIX CNPJ (flat total, not array)
if (pixCnpjTotal > 0) {
rows += `<tr><td>PIX CNPJ</td><td>${fmt(pixCnpjTotal)}</td><td></td></tr>`;
}
// Cancelamentos
if (cancelTotal > 0) {
rows += `<tr><td>Cancelamentos</td><td>${fmt(cancelTotal)}</td><td></td></tr>`;
}
return `<!DOCTYPE html> return `<!DOCTYPE html>
<html><head> <html><head>
@@ -1233,122 +1275,91 @@ function buildPrintHTML(data) {
<title>Relatório de Fechamento</title> <title>Relatório de Fechamento</title>
<style> <style>
* { box-sizing: border-box; margin: 0; padding: 0; } * { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: 'Courier New', monospace; font-size: 12px; width: 80mm; margin: 0 auto; padding: 6px; } body { font-family: 'Courier New', monospace; font-size: 13px; width: 80mm; margin: 0 auto; padding: 8px; }
.header { text-align: center; margin-bottom: 6px; } .header { text-align: center; margin-bottom: 8px; }
.header h1 { font-size: 16px; } .header h1 { font-size: 18px; }
.header h2 { font-size: 12px; font-weight: normal; } .header h2 { font-size: 14px; font-weight: normal; }
hr { border: none; border-top: 1px dashed #000; margin: 4px 0; } hr { border: none; border-top: 1px dashed #000; margin: 6px 0; }
.row { display: flex; justify-content: space-between; padding: 1px 0; } .section { margin: 6px 0; }
.row.sub { padding-left: 8px; font-size: 11px; color: #444; } .section-title { font-weight: bold; font-size: 14px; border-top: 1px dashed #000; padding-top: 4px; margin-top: 6px; }
.row strong { font-weight: bold; } .row { display: flex; justify-content: space-between; padding: 2px 0; }
.section-title { font-weight: bold; font-size: 12px; border-top: 1px dashed #000; padding-top: 3px; margin-top: 5px; } .row.space-between { justify-content: space-between; }
.grand-total { font-size: 14px; font-weight: bold; text-align: center; padding: 5px; border: 2px solid #000; margin: 6px 0; } .row.below { margin-top: 4px; }
.footer { text-align: center; font-size: 10px; color: #777; margin-top: 6px; } table { width: 100%; border-collapse: collapse; font-size: 12px; }
@media print { body { margin: 0; width: 80mm; } } td { padding: 2px 4px; vertical-align: top; }
td:nth-child(2) { text-align: right; }
td:nth-child(3) { text-align: right; color: #555; font-size: 11px; }
.total-row { font-weight: bold; border-top: 1px dashed #000; }
.grand-total { font-size: 16px; font-weight: bold; text-align: center; padding: 6px; border: 2px solid #000; margin: 8px 0; }
.obs { font-size: 11px; color: #555; margin-top: 6px; white-space: pre-wrap; }
.footer { text-align: center; font-size: 11px; color: #555; margin-top: 8px; }
@media print { body { margin: 0; } }
</style> </style>
</head><body> </head><body>
<div class="header"> <div class="header">
<h1>${data.loja || ''}</h1> <h1>${data.loja}</h1>
<h2>Fechamento de Caixa</h2> <h2>Fechamento de Caixa</h2>
</div> </div>
<hr> <hr>
<div class="row"><span>DATA:</span><span>${data.data || ''}</span></div> <div class="row"><span>Data:</span><span>${data.data}</span></div>
<div class="row"><span>OPERADOR:</span><span>${data.operador || ''}</span></div> <div class="row"><span>Operador:</span><span>${data.operador}</span></div>
<div class="row"><span>TURNO:</span><span>${data.turno || ''}</span></div> <div class="row"><span>Turno:</span><span>${data.turno}</span></div>
<div class="section-title">ENTRADAS</div>
<div class="row"><span>Saldo Troco:</span><span>${fmt(data.saldo_troco)}</span></div>
<div class="row"><span>Fechamento:</span><span>${fmt(data.fechamento)}</span></div>
<div class="row"><span>Saldo Esperado:</span><span>${fmt(data.saldo_esperado)}</span></div>
${rows ? `<div class="section-title">DETALHES</div><table><tbody>${rows}</tbody></table>` : ''}
${data.observacoes ? `<div class="obs">Obs: ${data.observacoes}</div>` : ''}
<hr> <hr>
<div class="section-title">SANGRIAS</div> <div class="section-title">CARTÕES</div>
${mkDet(data.sangrias, r => parseFloat(r.retirada)||0)} <div class="row"><span>Crédito:</span><span>${fmt(data.credito||0)}</span></div>
${fmtRow('TOTAL SANGRIAS', vSangrias)} <div class="row"><span>Débito:</span><span>${fmt(data.debito||0)}</span></div>
<div class="row"><span>Alimentação:</span><span>${fmt(data.alimentacao||0)}</span></div>
<div class="section-title">DESPESAS</div> <div class="row"><span>PIX Cartão:</span><span>${fmt(pixTotal)}</span></div>
${mkDet(data.despesas, r => parseFloat(r.valor)||0)} <div class="row"><span>Cancelamentos:</span><span>${fmt(cancelTotal)}</span></div>
${fmtRow('TOTAL DESPESAS', vDespesas)}
<div class="section-title">VALES</div>
${mkDet(data.vales, r => parseFloat(r.valor)||0)}
${fmtRow('TOTAL VALES', vVales)}
<div class="section-title">A RECEBER</div>
${mkDet(data.receber, r => parseFloat(r.valor)||0)}
${fmtRow('TOTAL A RECEBER', vReceber)}
<div class="section-title">PIX</div>
<div class="section-title" style="font-size:11px;font-weight:normal">— Cartão —</div>
${fmtRow('PIX CARTAO', vPixCartao)}
<div class="section-title" style="font-size:11px;font-weight:normal">— CNPJ —</div>
${mkDet(data.pixcnpj, r => parseFloat(r.valor)||0)}
${fmtRow('TOTAL PIX CNPJ', vPixCnpj)}
<div class="section-title">CARTOES</div>
${fmtRow('CREDITO', vCredito)}
${fmtRow('DEBITO', vDebito)}
${fmtRow('VOUCHER', vAliment)}
<div class="section-title">CANCELAMENTOS</div>
${mkDet(data.cancelamentos, r => parseFloat(r.valor)||0)}
${fmtRow('TOTAL CANCELAMENTOS', vCancel)}
<hr> <hr>
${fmtRow('SALDO ESPERADO', data.saldo_esperado || 0)} <div class="grand-total">DIFERENÇA: ${diffStr}</div>
${fmtRow('FECHAMENTO', vFechamento)}
<div class="grand-total" style="color:${diffColor}">DIFERENCA: ${diffStr}</div>
<div class="footer">Gerado em ${new Date().toLocaleString('pt-BR')}</div> <div class="footer">Gerado em ${new Date().toLocaleString('pt-BR')}</div>
</body></html>`; </body></html>`;
} }
function openPrintPreview(data) { function openPrintPreview(data) {
// Gera HTML e injeta no body para window.print() // Gera HTML e injeta no body de uma div temporária para window.print()
const html = buildPrintHTML(data); const html = buildPrintHTML(data);
const style = document.createElement('style'); const style = document.createElement('style');
style.id = '_print_style';
style.textContent = ` style.textContent = `
@media print { @media print {
body { width: 80mm; margin: 0 auto; font-family: 'Courier New', monospace; font-size: 13px; } body { width: 80mm; margin: 0 auto; font-family: 'Courier New', monospace; font-size: 13px; }
.no-print, .print-actions { display: none !important; } .no-print { display: none !important; }
hr { border-top: 1px dashed #000 !important; }
} }
.print-actions { position: fixed; top: 10px; right: 10px; z-index: 9999; } .print-actions { position: fixed; top: 10px; right: 10px; z-index: 9999; }
.print-actions button { padding: 8px 16px; cursor: pointer; background: #2563eb; color: #fff; border: none; border-radius: 4px; font-size: 14px; margin-left: 8px; } .print-actions button { padding: 8px 16px; cursor: pointer; background: #2563eb; color: #fff; border: none; border-radius: 4px; font-size: 14px; }
.print-actions button:hover { background: #1d4ed8; } .print-actions button:hover { background: #1d4ed8; }
.print-actions .btn-back { background: #6b7280; }
.print-actions .btn-back:hover { background: #4b5563; }
`; `;
const actions = document.createElement('div'); const actions = document.createElement('div');
actions.className = 'print-actions no-print'; actions.className = 'print-actions no-print';
actions.innerHTML = actions.innerHTML = '<button onclick="window.print()">🖨️ Imprimir (Ctrl+P)</button>';
'<button class="btn-back" id="_btn_back_print">← Voltar ao App</button>' +
'<button onclick="window.print()">🖨️ Imprimir (Ctrl+P)</button>';
// Guarda originais // Guarda conteúdo original
const originalBody = document.body.innerHTML; const originalBody = document.body.innerHTML;
const originalOnAfterPrint = window.onafterprint;
// Botão voltar restaura sem reload
document.addEventListener('click', function onBackClick(e) {
if (e.target.id === '_btn_back_print') {
document.body.innerHTML = originalBody;
const oldStyle = document.getElementById('_print_style');
if (oldStyle) oldStyle.remove();
document.removeEventListener('click', onBackClick);
window.onafterprint = originalOnAfterPrint;
// Re-init app state — mínimo viable restore
initApp();
}
});
document.body.innerHTML = html; document.body.innerHTML = html;
document.head.appendChild(style); document.head.appendChild(style);
document.body.insertBefore(actions, document.body.firstChild); document.body.insertBefore(actions, document.body.firstChild);
window.onafterprint = () => { // Quando fechar print dialog, restaura
const onAfterPrint = () => {
document.body.innerHTML = originalBody; document.body.innerHTML = originalBody;
const oldStyle = document.getElementById('_print_style'); window.removeEventListener('afterprint', onAfterPrint);
if (oldStyle) oldStyle.remove(); // Re-carrega scripts essenciais
window.onafterprint = originalOnAfterPrint; location.reload();
document.removeEventListener('click', onBackClick);
initApp();
}; };
window.addEventListener('afterprint', onAfterPrint);
} }
function triggerWindowPrint() { function triggerWindowPrint() {
@@ -1372,7 +1383,6 @@ async function tauriRecibo() {
const fechamentoCounted = parseNum(val('f-fech-dinheiro')) + parseNum(val('f-fech-cartoes')) + parseNum(val('f-fech-receber')); const fechamentoCounted = parseNum(val('f-fech-dinheiro')) + parseNum(val('f-fech-cartoes')) + parseNum(val('f-fech-receber'));
const saldoEsp = parseNum(val('f-saldo-esperado')); const saldoEsp = parseNum(val('f-saldo-esperado'));
const sysTroco = parseNum(val('sys-troco')); const sysTroco = parseNum(val('sys-troco'));
const data = { const data = {
uuid: estado.uuid, uuid: estado.uuid,
id_local: estado.id_local, id_local: estado.id_local,
@@ -1383,15 +1393,8 @@ async function tauriRecibo() {
saldo_troco: sysTroco, saldo_troco: sysTroco,
saldo_esperado: saldoEsp, saldo_esperado: saldoEsp,
fechamento: fechamentoCounted, fechamento: fechamentoCounted,
diferenca: sysTroco - saldoEsp, diferenca: fechamentoCounted - saldoEsp,
despesas: estado.despesas.map(r => ({ desc: r.desc||'', obs: r.obs||'', valor: r.valor||0 })), // Flat totals for ReciboData struct (Rust) — must be numbers
sangrias: estado.sangrias.map(r => ({ hora: r.hora||'', retirada: parseNum(r.retirada)||0, gerente: r.gerente||'' })),
pixcnpj: estado.pixcnpj.map(r => ({ nome: r.nome||'', valor: r.valor||0 })),
vales: estado.vales.map(r => ({ nome: r.nome||'', obs: r.obs||'', valor: r.valor||0 })),
receber: estado.receber.map(r => ({ nome: r.nome||'', valor: r.valor||0 })),
cancelamentos: estado.cancelamentos.map(r => ({ numero: r.numero||'', valor: r.valor||0, motivo: r.motivo||'' })),
cartoes: { credito: cartCred, debito: cartDeb, alimentacao: cartAli, pix: cartPix },
// Flat totals for ReciboData struct (Rust)
credito: cartCred, credito: cartCred,
debito: cartDeb, debito: cartDeb,
alimentacao: cartAli, alimentacao: cartAli,
@@ -1402,6 +1405,14 @@ async function tauriRecibo() {
vales: valesTotal, vales: valesTotal,
areceber: receberTotal, areceber: receberTotal,
cancelamentos: cancelTotal, cancelamentos: cancelTotal,
// Full arrays for backend/storage
despesas_arr: estado.despesas.map(r => ({ desc: r.desc||'', obs: r.obs||'', valor: r.valor||0 })),
sangrias_arr: estado.sangrias.map(r => ({ hora: r.hora||'', retirada: parseNum(r.retirada)||0, gerente: r.gerente||'' })),
pixcnpj_arr: estado.pixcnpj.map(r => ({ nome: r.nome||'', valor: r.valor||0 })),
vales_arr: estado.vales.map(r => ({ nome: r.nome||'', obs: r.obs||'', valor: r.valor||0 })),
receber_arr: estado.receber.map(r => ({ nome: r.nome||'', valor: r.valor||0 })),
cancelamentos_arr: estado.cancelamentos.map(r => ({ numero: r.numero||'', valor: r.valor||0, motivo: r.motivo||'' })),
cartoes: { credito: cartCred, debito: cartDeb, alimentacao: cartAli, pix: cartPix },
observacoes: estado.observacoes, observacoes: estado.observacoes,
sync_status: estado.sync_status, sync_status: estado.sync_status,
}; };
@@ -1480,16 +1491,19 @@ document.getElementById('modal-confirm').addEventListener('click', e => { if (e.
// Print from confirm modal — abre preview no navegador // Print from confirm modal — abre preview no navegador
document.getElementById('confirm-print').addEventListener('click', async () => { document.getElementById('confirm-print').addEventListener('click', async () => {
closeConfirmModal(); closeConfirmModal();
// Reconstrói os mesmos dados do tauriRecibo e abre preview
const cartCred = estado.cartoes.credito.reduce((s,r) => s+(r.valor||0), 0); const cartCred = estado.cartoes.credito.reduce((s,r) => s+(r.valor||0), 0);
const cartDeb = estado.cartoes.debito.reduce((s,r) => s+(r.valor||0), 0); const cartDeb = estado.cartoes.debito.reduce((s,r) => s+(r.valor||0), 0);
const cartAli = estado.cartoes.alimentacao.reduce((s,r) => s+(r.valor||0), 0); const cartAli = estado.cartoes.alimentacao.reduce((s,r) => s+(r.valor||0), 0);
const cartPix = estado.cartoes.pix.reduce((s,r) => s+(r.valor||0), 0); const cartPix = estado.cartoes.pix.reduce((s,r) => s+(r.valor||0), 0);
const cancelTotal = estado.cancelamentos.reduce((s,r) => s+(parseNum(r.valor||0)||0), 0);
const fechamentoCounted = parseNum(val('f-fech-dinheiro')) + parseNum(val('f-fech-cartoes')) + parseNum(val('f-fech-receber'));
const saldoEsp = parseNum(val('f-saldo-esperado'));
const data = { const data = {
loja: estado.loja, data: estado.data, operador: estado.operador, turno: estado.turno, loja: estado.loja, data: estado.data, operador: estado.operador, turno: estado.turno,
saldo_troco: parseNum(val('sys-troco')) || 0, saldo_troco: parseNum(val('sys-troco')) || 0,
saldo_esperado: parseNum(val('f-saldo-esperado')) || 0, saldo_esperado: saldoEsp,
fechamento: parseNum(val('f-fech-dinheiro')) + parseNum(val('f-fech-cartoes')) + parseNum(val('f-fech-receber')), fechamento: fechamentoCounted,
diferenca: fechamentoCounted - saldoEsp,
despesas: estado.despesas.map(r => ({ desc: r.desc||'', obs: r.obs||'', valor: r.valor||0 })), despesas: estado.despesas.map(r => ({ desc: r.desc||'', obs: r.obs||'', valor: r.valor||0 })),
sangrias: estado.sangrias.map(r => ({ hora: r.hora||'', retirada: parseNum(r.retirada)||0, gerente: r.gerente||'' })), sangrias: estado.sangrias.map(r => ({ hora: r.hora||'', retirada: parseNum(r.retirada)||0, gerente: r.gerente||'' })),
pixcnpj: estado.pixcnpj.map(r => ({ nome: r.nome||'', valor: r.valor||0 })), pixcnpj: estado.pixcnpj.map(r => ({ nome: r.nome||'', valor: r.valor||0 })),
@@ -1497,7 +1511,8 @@ document.getElementById('confirm-print').addEventListener('click', async () => {
receber: estado.receber.map(r => ({ nome: r.nome||'', valor: r.valor||0 })), receber: estado.receber.map(r => ({ nome: r.nome||'', valor: r.valor||0 })),
cancelamentos: estado.cancelamentos.map(r => ({ numero: r.numero||'', valor: r.valor||0, motivo: r.motivo||'' })), cancelamentos: estado.cancelamentos.map(r => ({ numero: r.numero||'', valor: r.valor||0, motivo: r.motivo||'' })),
credito: cartCred, debito: cartDeb, alimentacao: cartAli, credito: cartCred, debito: cartDeb, alimentacao: cartAli,
cartoes: { pix: cartPix }, pix: cartPix, // flat float for buildPrintHTML
cartoes: { credito: estado.cartoes.credito, debito: estado.cartoes.debito, alimentacao: estado.cartoes.alimentacao, pix: estado.cartoes.pix },
observacoes: estado.observacoes, observacoes: estado.observacoes,
}; };
openPrintPreview(data); openPrintPreview(data);
@@ -1560,30 +1575,42 @@ document.getElementById('btn-limpar').addEventListener('click', () => {
document.getElementById('btn-anterior').addEventListener('click', async () => { document.getElementById('btn-anterior').addEventListener('click', async () => {
const loja = val('f-loja') || 'União'; const loja = val('f-loja') || 'União';
// Carrega lista de datas recentes do Supabase // Carrega lista de fechamentos recentes do Supabase com uuid
let datas = []; let recentList = [];
try { try {
const lista = await window.__TAURI__.core.invoke('sb_listar_recentes', { loja, limite: 20 }); recentList = await window.__TAURI__.core.invoke('sb_listar_recentes', { loja, limite: 20 });
if (lista && lista.length > 0) {
// Extrai datas únicas e já ordenadas
datas = [...new Set(lista.map(l => l.data).filter(Boolean))].sort().reverse();
}
} catch(e) { } catch(e) {
console.warn('sb_listar_recentes falhou:', e); console.warn('sb_listar_recentes falhou:', e);
} }
// Extrai datas únicas ordenadas + mapeamento data→id
const dateIdMap = {};
const dateList = [];
if (recentList && recentList.length > 0) {
recentList.forEach(item => {
if (item.data && !dateIdMap[item.data]) {
dateIdMap[item.data] = item.id;
dateList.push(item.data);
}
});
dateList.sort().reverse();
}
let selectedDate = null; let selectedDate = null;
if (datas.length > 0) { let selectedId = null;
// Mostra seletor com as datas disponíveis if (dateList.length > 0) {
const msg = datas.map((d, i) => `${i + 1}) ${d}`).join('\n'); const msg = dateList.map((d, i) => `${i + 1}) ${d}`).join('\n');
const escolha = prompt(`Selecione o número do dia anterior:\n${msg}\n\n(Deixe em branco e clique Cancelar para ver só o dia anterior automático)`); const escolha = prompt(`Selecione o número do dia anterior:\n${msg}\n\n(Deixe em branco e clique Cancelar para ver só o dia anterior automático)`);
if (escolha !== null && escolha.trim() !== '') { if (escolha !== null && escolha.trim() !== '') {
const idx = parseInt(escolha) - 1; const idx = parseInt(escolha) - 1;
if (idx >= 0 && idx < datas.length) selectedDate = datas[idx]; if (idx >= 0 && idx < dateList.length) {
selectedDate = dateList[idx];
selectedId = dateIdMap[selectedDate];
}
} }
} }
// Se não escolheu data, usa dia anterior automático // Se não escolheu data, usa dia anterior automático (sem id — carrega por data)
if (!selectedDate) { if (!selectedDate) {
const d = new Date(estado.data); d.setDate(d.getDate() - 1); const d = new Date(estado.data); d.setDate(d.getDate() - 1);
selectedDate = d.toISOString().split('T')[0]; selectedDate = d.toISOString().split('T')[0];
@@ -1596,11 +1623,19 @@ document.getElementById('btn-anterior').addEventListener('click', async () => {
estado.id_local = null; estado.id_local = null;
syncHeader(); syncHeader();
// Tenta Supabase primeiro, senão local // Tenta Supabase primeiro (com id se disponível), senão local
let rascunho = null; let rascunho = null;
try { if (selectedId) {
rascunho = await window.__TAURI__.core.invoke('sb_carregar_rascunho', { loja, data: selectedDate }); // Usa sb_buscar_por_id para buscar por id numérico do Supabase
} catch(e) { console.warn('sb_carregar_rascunho falhou:', e); } try {
rascunho = await window.__TAURI__.core.invoke('sb_buscar_por_id', { id: selectedId });
} catch(e) { console.warn('sb_buscar_por_id falhou:', e); }
}
if (!rascunho) {
try {
rascunho = await window.__TAURI__.core.invoke('sb_carregar_rascunho', { loja, data: selectedDate });
} catch(e) { console.warn('sb_carregar_rascunho falhou:', e); }
}
if (!rascunho) { if (!rascunho) {
try { try {