Compare commits

...

8 Commits

Author SHA1 Message Date
root c01133f39c v1.3.6: nomenclatura trocada; relatórios com todos os lançamentos detalhados por seção
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-26 02:52:08 +00:00
root a6b295a6d0 v1.3.5: to_escpos completo — totais por seção, diferenca via self.diferenca; confirm antes de dia anterior; sem senha
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-26 02:41:37 +00:00
root f850831e98 fix: buildPrintHTML usa data.diferenca (sys_total - saldo_esperado); btn-anterior null-check robusto
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-26 02:23:57 +00:00
root e70f31e00f debug: btn-anterior logs; cartoes.norm guard
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-26 01:44:15 +00:00
root 79e1dbd645 fix: capabilities Tauri 2 dialog; imprimir_recibo {data}; cartoes.map guard; debug logs
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-25 17:44:22 +00:00
root cce8f3df60 fix: newEstado is not defined — reset estado inline; clear form fields on limpar
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-25 17:30:07 +00:00
root 318d44e934 fix: modal confirm usa sysTotal pra diferenca, nao so sys-troco
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-25 16:38:26 +00:00
root bfbcd89be9 fix: diferenca usa sys_total; novo layout 80mm sem ENTRADAS; confirm modal
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-25 14:32:05 +00:00
4 changed files with 585 additions and 262 deletions
+23
View File
@@ -0,0 +1,23 @@
{
"identifier": "default",
"description": "Default capabilities for Fechamento de Caixa",
"windows": ["main"],
"permissions": [
"core:default",
"shell:allow-open",
"dialog:allow-message",
"dialog:allow-confirm",
"dialog:allow-ask",
"dialog:allow-save",
"dialog:allow-open",
"fs:default",
"fs:allow-app-read",
"fs:allow-app-write",
"fs:allow-appdata-read",
"fs:allow-appdata-write",
"fs:allow-appconfig-read",
"fs:allow-appconfig-write",
"fs:allow-applocaldata-read",
"fs:allow-applocaldata-write"
]
}
+116 -68
View File
@@ -88,7 +88,11 @@ pub struct ReciboData {
pub saldo_esperado: f64,
/// Diferença = fechamento saldo_esperado
pub diferenca: f64,
/// Soma de todas operações creditadas (crédito + débito + alimentação + voucher + pixcnpj)
/// Total do sistema (troco + crédito + débito + alimentação + voucher + pixcnpj + vales + receber)
#[serde(default)]
pub sys_total: f64,
/// Soma de todas operações creditadas (crédito + débito + alimentação + voucher + pixcnpj) — usado no BLACK section como TOTAL
#[serde(default)]
pub total_operado: f64,
// Totais de cartões
pub credito: f64,
@@ -131,7 +135,7 @@ impl ReciboData {
}
/// Gera todos os bytes ESC/POS do recibo 80mm
/// Layout: SANGRIAS → ENTRADAS → DESPESAS → PIX CNPJ → VALES → A RECEBER → CARTÕES → CANCELAMENTOS → TROCO → OBSERVAÇÕES → TOTAL SALDO CAIXA → SALDO ESPERADO → DIFERENÇA
/// Layout: Header → SANGRIAS → DESPESAS → PIX CNPJ → VALES → A RECEBER → CANCELAMENTOS → CARTÕES → TROCO/Saldo Caixa → SALDO ESPERADO → DIFERENÇA
pub fn to_escpos(&self) -> Vec<u8> {
let mut out = Vec::new();
@@ -152,31 +156,10 @@ impl ReciboData {
out.extend(escpos::divider());
// ── SANGRIAS ──
out.extend(escpos::line_bold("SANGRIAS"));
for linha in &self.sangrias_arr {
if linha.retirada.is_some() || linha.valor.is_some() {
let label = linha.label();
let obs = linha.obs();
out.extend(escpos::kv(&label, &linha.fmt_v()));
if !obs.is_empty() {
out.extend(escpos::line_center(&format!(" {}", obs)));
}
}
}
out.extend(escpos::divider());
// ── ENTRADAS ──
out.extend(escpos::line_bold("ENTRADAS"));
out.extend(escpos::kv("Saldo Troco:", &self.fmt_money(self.saldo_troco)));
out.extend(escpos::kv("Fechamento:", &self.fmt_money(self.fechamento)));
out.extend(escpos::kv("Saldo Esperado:", &self.fmt_money(self.saldo_esperado)));
out.extend(escpos::divider());
// ── DESPESAS ──
if !self.despesas_arr.is_empty() {
out.extend(escpos::line_bold("DESPESAS"));
for linha in &self.despesas_arr {
if linha.valor.is_some() {
if !self.sangrias_arr.is_empty() {
out.extend(escpos::line_bold("SANGRIAS"));
for linha in &self.sangrias_arr {
if linha.retirada.unwrap_or(0.0) > 0.0 || linha.valor.unwrap_or(0.0) > 0.0 {
let label = linha.label();
let obs = linha.obs();
out.extend(escpos::kv(&label, &linha.fmt_v()));
@@ -185,18 +168,53 @@ impl ReciboData {
}
}
}
// Total sangrias
let total_sangrias: f64 = self.sangrias_arr.iter()
.map(|l| l.retirada.unwrap_or(0.0) + l.valor.unwrap_or(0.0))
.sum();
if total_sangrias > 0.0 {
out.extend(escpos::kv("Total:", &self.fmt_money(total_sangrias)));
}
out.extend(escpos::divider());
}
// ── DESPESAS ──
if !self.despesas_arr.is_empty() {
out.extend(escpos::line_bold("DESPESAS"));
for linha in &self.despesas_arr {
if linha.valor.unwrap_or(0.0) > 0.0 {
let label = linha.label();
let obs = linha.obs();
out.extend(escpos::kv(&label, &linha.fmt_v()));
if !obs.is_empty() {
out.extend(escpos::line_center(&format!(" {}", obs)));
}
}
}
// Total despesas
let total_despesas: f64 = self.despesas_arr.iter()
.map(|l| l.valor.unwrap_or(0.0))
.sum();
out.extend(escpos::kv("Total:", &self.fmt_money(total_despesas)));
out.extend(escpos::divider());
}
// ── PIX CNPJ ──
if !self.pixcnpj_arr.is_empty() || self.pixcnpj > 0.0 {
if !self.pixcnpj_arr.is_empty() {
out.extend(escpos::line_bold("PIX CNPJ"));
for linha in &self.pixcnpj_arr {
if linha.valor.is_some() {
if linha.valor.unwrap_or(0.0) > 0.0 {
let label = linha.nome.clone().unwrap_or_else(|| "PIX CNPJ".to_string());
out.extend(escpos::kv(&label, &linha.fmt_v()));
}
}
// Total PIX CNPJ
let total_pixcnpj: f64 = self.pixcnpj_arr.iter()
.map(|l| l.valor.unwrap_or(0.0))
.sum();
if total_pixcnpj > 0.0 {
out.extend(escpos::kv("Total:", &self.fmt_money(total_pixcnpj)));
}
out.extend(escpos::divider());
}
@@ -204,7 +222,7 @@ impl ReciboData {
if !self.vales_arr.is_empty() {
out.extend(escpos::line_bold("VALES"));
for linha in &self.vales_arr {
if linha.valor.is_some() {
if linha.valor.unwrap_or(0.0) > 0.0 {
let label = linha.label();
let obs = linha.obs();
out.extend(escpos::kv(&label, &linha.fmt_v()));
@@ -213,6 +231,13 @@ impl ReciboData {
}
}
}
// Total vales
let total_vales: f64 = self.vales_arr.iter()
.map(|l| l.valor.unwrap_or(0.0))
.sum();
if total_vales > 0.0 {
out.extend(escpos::kv("Total:", &self.fmt_money(total_vales)));
}
out.extend(escpos::divider());
}
@@ -220,11 +245,42 @@ impl ReciboData {
if !self.receber_arr.is_empty() {
out.extend(escpos::line_bold("A RECEBER"));
for linha in &self.receber_arr {
if linha.valor.is_some() {
if linha.valor.unwrap_or(0.0) > 0.0 {
let label = linha.label();
out.extend(escpos::kv(&label, &linha.fmt_v()));
}
}
// Total a receber
let total_receber: f64 = self.receber_arr.iter()
.map(|l| l.valor.unwrap_or(0.0))
.sum();
if total_receber > 0.0 {
out.extend(escpos::kv("Total:", &self.fmt_money(total_receber)));
}
out.extend(escpos::divider());
}
// ── CANCELAMENTOS ──
if !self.cancelamentos_arr.is_empty() {
out.extend(escpos::line_bold("CANCELAMENTOS"));
for linha in &self.cancelamentos_arr {
if linha.numero.is_some() || linha.valor.unwrap_or(0.0) > 0.0 {
let num = linha.numero.clone().unwrap_or_default();
out.extend(escpos::kv(&num, &linha.fmt_v()));
if let Some(ref m) = linha.motivo {
if !m.is_empty() {
out.extend(escpos::line_center(&format!(" {}", m)));
}
}
}
}
// Total cancelamentos
let total_cancel: f64 = self.cancelamentos_arr.iter()
.map(|l| l.valor.unwrap_or(0.0))
.sum();
if total_cancel > 0.0 {
out.extend(escpos::kv("Total:", &self.fmt_money(total_cancel)));
}
out.extend(escpos::divider());
}
@@ -237,28 +293,40 @@ impl ReciboData {
out.extend(escpos::kv("Cancelamentos:", &self.fmt_money(self.cancelamentos)));
out.extend(escpos::divider());
// ── CANCELAMENTOS (detalhe) ──
if !self.cancelamentos_arr.is_empty() {
out.extend(escpos::line_bold("CANCELAMENTOS"));
for linha in &self.cancelamentos_arr {
if linha.valor.is_some() {
let num = linha.numero.clone().unwrap_or_default();
out.extend(escpos::kv(&num, &linha.fmt_v()));
if let Some(ref m) = linha.motivo {
if !m.is_empty() {
out.extend(escpos::line_center(&format!(" {}", m)));
}
}
}
}
// ── TROCO / SALDO DE CAIXA ──
out.extend(escpos::line_bold("SALDO DE CAIXA"));
out.extend(escpos::kv("Troco (+):", &self.fmt_money(self.saldo_troco)));
out.extend(escpos::kv("Credito (+):", &self.fmt_money(self.credito)));
out.extend(escpos::kv("Debito (+):", &self.fmt_money(self.debito)));
out.extend(escpos::kv("Alimentacao (+):", &self.fmt_money(self.alimentacao)));
out.extend(escpos::kv("PIX Cartao (+):", &self.fmt_money(self.voucher)));
out.extend(escpos::kv("Vales (+):", &self.fmt_money(self.vales)));
out.extend(escpos::kv("A Receber (+):", &self.fmt_money(self.areceber)));
out.extend(escpos::kv("PIX CNPJ (+):", &self.fmt_money(self.pixcnpj)));
out.extend(escpos::divider());
// ── TOTAL ──
if self.sys_total > 0.0 {
out.extend(escpos::line_bold("TOTAL"));
out.extend(escpos::kv("Total:", &self.fmt_money(self.sys_total)));
out.extend(escpos::divider());
}
// ── TROCO ──
out.extend(escpos::line_bold("TROCO"));
out.extend(escpos::kv("Troco:", &self.fmt_money(self.saldo_troco)));
// ── SALDO ESPERADO ──
out.extend(escpos::line_bold("SALDO ESPERADO"));
out.extend(escpos::kv("Esperado:", &self.fmt_money(self.saldo_esperado)));
out.extend(escpos::divider());
// ── DIFERENÇA ──
out.extend(escpos::line_bold("DIFERENCA"));
let diff = self.diferenca;
let diff_str = if diff >= 0.0 {
format!("+{}", self.fmt_money(diff))
} else {
self.fmt_money(diff)
};
out.extend(escpos::title(&diff_str));
// ── OBSERVAÇÕES ──
if let Some(ref obs) = self.observacoes {
if !obs.trim().is_empty() {
@@ -268,26 +336,6 @@ impl ReciboData {
}
}
// ── TOTAL SALDO CAIXA ──
out.extend(escpos::line_bold("TOTAL SALDO DE CAIXA"));
out.extend(escpos::kv("Total:", &self.fmt_money(self.fechamento)));
out.extend(escpos::divider());
// ── SALDO ESPERADO ──
out.extend(escpos::line_bold("SALDO ESPERADO"));
out.extend(escpos::kv("Esperado:", &self.fmt_money(self.saldo_esperado)));
out.extend(escpos::divider());
// ── DIFERENÇA ──
out.extend(escpos::line_bold("DIFERENCA"));
let diff = self.fechamento - self.saldo_esperado;
let diff_str = if diff >= 0.0 {
format!("+{}", self.fmt_money(diff))
} else {
self.fmt_money(diff)
};
out.extend(escpos::title(&diff_str));
// Rodapé
out.extend(escpos::divider());
out.extend(escpos::line_center(
+11 -4
View File
@@ -11,7 +11,7 @@
"app": {
"windows": [
{
"title": "Fechamento de Caixa O Frangão",
"title": "Fechamento de Caixa \u2014 O Frang\u00e3o",
"width": 1100,
"height": 780,
"minWidth": 900,
@@ -22,13 +22,18 @@
}
],
"security": {
"csp": null
"csp": null,
"capabilities": [
"default"
]
},
"withGlobalTauri": true
},
"bundle": {
"active": true,
"targets": ["nsis"],
"targets": [
"nsis"
],
"icon": [
"icons/32x32.png",
"icons/128x128.png",
@@ -37,7 +42,9 @@
"icons/icon.ico"
],
"windows": {
"webviewInstallMode": { "type": "embedBootstrapper" }
"webviewInstallMode": {
"type": "embedBootstrapper"
}
},
"resources": {
"../src/*": "./"
+435 -190
View File
@@ -384,7 +384,7 @@
<!-- SALDO DE CAIXA -->
<div class="section">
<div class="section-header">🖥️ Saldo de Caixa (Valores do Sistema)</div>
<div class="section-header">🖥️ Fechamento de Caixa</div>
<div class="sys-grid">
<div class="sys-row"><span class="lbl">Troco (+)</span><input type="text" id="sys-troco" class="editable" placeholder="0,00" /></div>
<div class="sys-row"><span class="lbl">Crédito (+)</span><input type="text" id="sys-credito" readonly tabindex="-1" /></div>
@@ -415,7 +415,7 @@
<!-- FECHAMENTO (VALORES CONTADOS) -->
<div class="section">
<div class="section-header">🛒 Fechamento (Valores Contados)</div>
<div class="section-header">🛒 Saldo de Caixa</div>
<div class="fech-grid">
<div class="fech-item"><div class="fech-item-label">Dinheiro</div><input type="text" id="f-fech-dinheiro" placeholder="0,00" /></div>
<div class="fech-item"><div class="fech-item-label">Cartões</div><input type="text" id="f-fech-cartoes" placeholder="0,00" /></div>
@@ -1229,50 +1229,255 @@ async function tauriEnviar() {
}
}
// ─── Print Preview Window ──────────────────────────────────────────────────
function buildPrintHTML(data) {
function buildConfirmHTML(data) {
const fmt = v => 'R$ ' + (v || 0).toFixed(2).replace('.', ',');
const diferenca = (data.fechamento || 0) - (data.total_operado || 0);
const diffStr = diferenca >= 0 ? `+${fmt(diferenca)}` : fmt(diferenca);
const diffColor = diferenca === 0 ? '#2e7d32' : (diferenca > 0 ? '#e65100' : '#c0272d');
const fmtNeg = v => (v < 0 ? '-' : '') + 'R$ ' + Math.abs(v || 0).toFixed(2).replace('.', ',');
const sangrias_arr = data.sangrias_arr || data.sangrias || [];
const despesas_arr = data.despesas_arr || data.despesas || [];
const vales_arr = data.vales_arr || data.vales || [];
const receber_arr = data.receber_arr || data.receber || [];
const pixcnpj_arr = data.pixcnpj_arr || data.pixcnpj || [];
const cancelamentos_arr = data.cancelamentos_arr || data.cancelamentos || [];
const sangrias = sangrias_arr.reduce((s,r) => s+(parseNum(r.retirada)||0)+(parseNum(r.valor)||0), 0);
const despesas = despesas_arr.reduce((s,r) => s+(parseNum(r.valor)||0), 0);
const vales = vales_arr.reduce((s,r) => s+(parseNum(r.valor)||0), 0);
const receber = receber_arr.reduce((s,r) => s+(parseNum(r.valor)||0), 0);
const pixcnpj = pixcnpj_arr.reduce((s,r) => s+(parseNum(r.valor)||0), 0);
const cancelamentos = cancelamentos_arr.reduce((s,c) => s+(parseNum(c.valor)||0), 0);
const cartCred = data.credito || 0;
const cartDeb = data.debito || 0;
const cartAli = data.alimentacao || 0;
const cartPix = data.voucher || data.pix || 0;
const fechamento = data.fechamento || 0;
const saldoEsp = data.saldo_esperado || 0;
const diferenca = fechamento - saldoEsp;
const diffClass = diferenca === 0 ? '#2e7d32' : (diferenca > 0 ? '#e65100' : '#c0272d');
const diffStr = diferenca >= 0 ? `+${fmt(diferenca)}` : fmtNeg(diferenca);
// Monta linhas de detalhe
let detailSections = '';
function detailSection(title, rows, total) {
if (!rows && total <= 0) return '';
let html = `<div class="detail-section"><div class="detail-title">${title}</div>`;
if (rows) html += rows;
html += `<div class="detail-total"><span>Total:</span><span>${fmt(total)}</span></div></div>`;
return html;
}
let rows = '';
// Cancelamentos (sum the array first — single cancelamentos is a float total, array is from confirm-print)
const cancelTotal = Array.isArray(data.cancelamentos)
? data.cancelamentos.reduce((s, c) => s + (parseNum(c.valor || 0) || 0), 0)
: (data.cancelamentos || 0);
// PIX total (flat number from tauriRecibo; pixcnpj is also flat total)
const pixTotal = data.pix || data.voucher || 0;
const pixCnpjTotal = typeof data.pixcnpj === 'number' ? data.pixcnpj : 0;
// Sangrias
if (data.sangrias && data.sangrias.length > 0) {
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>`; });
}
let sangriaRows = '';
sangrias_arr.forEach(s => {
const v = (parseNum(s.retirada)||0) + (parseNum(s.valor)||0);
if (v > 0) {
const hora = s.hora ? ` <span class="c2">${s.hora}</span>` : '';
const gerente = s.gerente ? ` <span class="c2">(${s.gerente})</span>` : '';
sangriaRows += `<div class="detail-row"><span>${hora}${gerente}</span><span>${fmt(v)}</span></div>`;
}
});
detailSections += detailSection('SANGRIAS', sangriaRows, sangrias);
// Despesas
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>`; });
}
let despRows = '';
despesas_arr.forEach(d => {
const v = parseNum(d.valor) || 0;
if (v > 0) {
const obs = d.obs ? ` <span class="c2">${d.obs}</span>` : '';
despRows += `<div class="detail-row"><span>${d.desc||''}${obs}</span><span>${fmt(v)}</span></div>`;
}
});
detailSections += detailSection('DESPESAS', despRows, despesas);
// 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>`;
}
let valesRows = '';
vales_arr.forEach(v => {
const val = parseNum(v.valor) || 0;
if (val > 0) {
const obs = v.obs ? ` <span class="c2">${v.obs}</span>` : '';
valesRows += `<div class="detail-row"><span>${v.nome||''}${obs}</span><span>${fmt(val)}</span></div>`;
}
});
detailSections += detailSection('VALES', valesRows, vales);
// A Receber
let recRows = '';
receber_arr.forEach(r => {
const v = parseNum(r.valor) || 0;
if (v > 0) recRows += `<div class="detail-row"><span>${r.nome||''}</span><span>${fmt(v)}</span></div>`;
});
detailSections += detailSection('A RECEBER', recRows, receber);
// PIX CNPJ
let pixRows = '';
pixcnpj_arr.forEach(p => {
const v = parseNum(p.valor) || 0;
if (v > 0) pixRows += `<div class="detail-row"><span>${p.nome||''}</span><span>${fmt(v)}</span></div>`;
});
detailSections += detailSection('PIX CNPJ', pixRows, pixcnpj);
// Cancelamentos
if (cancelTotal > 0) {
rows += `<tr><td>Cancelamentos</td><td>${fmt(cancelTotal)}</td><td></td></tr>`;
}
let cancRows = '';
cancelamentos_arr.forEach(c => {
const v = parseNum(c.valor) || 0;
if (c.numero || v > 0) {
const motivo = c.motivo ? ` <span class="c2">${c.motivo}</span>` : '';
cancRows += `<div class="detail-row"><span>${c.numero||''}${motivo}</span><span>${fmt(v)}</span></div>`;
}
});
detailSections += detailSection('CANCELAMENTOS', cancRows, cancelamentos);
return `<!DOCTYPE html>
<html><head>
<meta charset="UTF-8">
<title>Relatório de Fechamento</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: 'Courier New', monospace; font-size: 13px; width: 80mm; margin: 0 auto; padding: 8px; }
.header { text-align: center; margin-bottom: 8px; }
.header h1 { font-size: 18px; }
.header h2 { font-size: 14px; font-weight: normal; }
hr { border: none; border-top: 1px dashed #b3d9f7; margin: 6px 0; }
.row { display: flex; justify-content: space-between; padding: 2px 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; }
.detail-section { margin: 6px 0; }
.detail-title { font-weight: bold; font-size: 13px; border-top: 1px dashed #b3d9f7; padding-top: 3px; }
.detail-row { display: flex; justify-content: space-between; padding: 1px 0; font-size: 12px; }
.detail-total { display: flex; justify-content: space-between; padding: 1px 0; font-weight: bold; border-top: 1px dotted #b3d9f7; font-size: 12px; }
.c2 { color: #888; }
@media print { body { margin: 0; } }
</style>
</head><body>
<div class="header">
<h1>${data.loja}</h1>
<h2>Fechamento de Caixa</h2>
</div>
<div style="margin-bottom:6px"><strong>📅 Data:</strong> ${data.data} &nbsp; <strong>👤 Operador:</strong> ${data.operador} &nbsp; <strong>⏰ Turno:</strong> ${data.turno}</div>
<hr>
${detailSections}
<hr>
<div>💸 Sangrias: <strong>${fmt(sangrias)}</strong></div>
<div>📝 Despesas: <strong>${fmt(despesas)}</strong></div>
<div>💰 Vales: <strong>${fmt(vales)}</strong></div>
<div>👥 À Receber: <strong>${fmt(receber)}</strong></div>
<div>🔵 PIX CNPJ: <strong>${fmt(pixcnpj)}</strong></div>
<div>💳 Crédito: <strong>${fmt(cartCred)}</strong></div>
<div>💳 Débito: <strong>${fmt(cartDeb)}</strong></div>
<div>💳 Alimentação: <strong>${fmt(cartAli)}</strong></div>
<div>❌ Cancelamentos: <strong>${fmt(cancelamentos)}</strong></div>
<div>📋 Fechamento: <strong>${fmt(fechamento)}</strong></div>
<hr>
<div>🎯 Saldo Esperado: <strong>${fmt(saldoEsp)}</strong></div>
<div style="margin-top:4px">📐 Diferença: <strong style="color:${diffClass}">${diffStr}</strong></div>
<div style="margin-top:4px;font-size:10px;color:#888">ID: ${data.loja}_${data.data}_${data.operador}</div>
<div class="footer">Gerado em ${new Date().toLocaleString('pt-BR')}</div>
</body></html>`;
}
// ─── Print Preview Window
function buildPrintHTML(data) {
const fmt = v => 'R$ ' + (v || 0).toFixed(2).replace('.', ',');
const fmtNeg = v => (v < 0 ? '-' : '') + 'R$ ' + Math.abs(v || 0).toFixed(2).replace('.', ',');
const sangrias_arr = data.sangrias_arr || data.sangrias || [];
const despesas_arr = data.despesas_arr || data.despesas || [];
const vales_arr = data.vales_arr || data.vales || [];
const receber_arr = data.receber_arr || data.receber || [];
const pixcnpj_arr = data.pixcnpj_arr || data.pixcnpj || [];
const cancelamentos_arr = data.cancelamentos_arr || data.cancelamentos || [];
const sangrias = sangrias_arr.reduce((s,r) => s+(parseNum(r.retirada)||0)+(parseNum(r.valor)||0), 0);
const despesas = despesas_arr.reduce((s,r) => s+(parseNum(r.valor)||0), 0);
const vales = vales_arr.reduce((s,r) => s+(parseNum(r.valor)||0), 0);
const receber = receber_arr.reduce((s,r) => s+(parseNum(r.valor)||0), 0);
const pixcnpj = pixcnpj_arr.reduce((s,r) => s+(parseNum(r.valor)||0), 0);
const cancelamentos = cancelamentos_arr.reduce((s,c) => s+(parseNum(c.valor)||0), 0);
const cartCred = data.credito || 0;
const cartDeb = data.debito || 0;
const cartAli = data.alimentacao || 0;
const cartPix = data.voucher || data.pix || 0;
const fechamento = data.fechamento || data.sys_total || 0;
const saldoEsp = data.saldo_esperado || 0;
const diferenca = data.diferenca !== undefined ? data.diferenca : (fechamento - saldoEsp);
const diffStr = diferenca >= 0 ? `+${fmt(diferenca)}` : fmtNeg(diferenca);
const diffColor = diferenca === 0 ? '#2e7d32' : (diferenca > 0 ? '#e65100' : '#c0272d');
let detailSections = '';
function detailSection(title, rows, total) {
if (!rows && total <= 0) return '';
let html = `<div class="detail-section"><div class="detail-title">${title}</div>`;
if (rows) html += rows;
html += `<div class="detail-total"><span>Total:</span><span>${fmt(total)}</span></div></div>`;
return html;
}
// Sangrias
let sangriaRows = '';
sangrias_arr.forEach(s => {
const v = (parseNum(s.retirada)||0) + (parseNum(s.valor)||0);
if (v > 0) {
const hora = s.hora ? ` <span class="c2">${s.hora}</span>` : '';
const gerente = s.gerente ? ` <span class="c2">(${s.gerente})</span>` : '';
sangriaRows += `<div class="detail-row"><span>${hora}${gerente}</span><span>${fmt(v)}</span></div>`;
}
});
detailSections += detailSection('SANGRIAS', sangriaRows, sangrias);
// Despesas
let despRows = '';
despesas_arr.forEach(d => {
const v = parseNum(d.valor) || 0;
if (v > 0) {
const obs = d.obs ? ` <span class="c2">${d.obs}</span>` : '';
despRows += `<div class="detail-row"><span>${d.desc||''}${obs}</span><span>${fmt(v)}</span></div>`;
}
});
detailSections += detailSection('DESPESAS', despRows, despesas);
// Vales
let valesRows = '';
vales_arr.forEach(v => {
const val = parseNum(v.valor) || 0;
if (val > 0) {
const obs = v.obs ? ` <span class="c2">${v.obs}</span>` : '';
valesRows += `<div class="detail-row"><span>${v.nome||''}${obs}</span><span>${fmt(val)}</span></div>`;
}
});
detailSections += detailSection('VALES', valesRows, vales);
// A Receber
let recRows = '';
receber_arr.forEach(r => {
const v = parseNum(r.valor) || 0;
if (v > 0) recRows += `<div class="detail-row"><span>${r.nome||''}</span><span>${fmt(v)}</span></div>`;
});
detailSections += detailSection('A RECEBER', recRows, receber);
// PIX CNPJ
let pixRows = '';
pixcnpj_arr.forEach(p => {
const v = parseNum(p.valor) || 0;
if (v > 0) pixRows += `<div class="detail-row"><span>${p.nome||''}</span><span>${fmt(v)}</span></div>`;
});
detailSections += detailSection('PIX CNPJ', pixRows, pixcnpj);
// Cancelamentos
let cancRows = '';
cancelamentos_arr.forEach(c => {
const v = parseNum(c.valor) || 0;
if (c.numero || v > 0) {
const motivo = c.motivo ? ` <span class="c2">${c.motivo}</span>` : '';
cancRows += `<div class="detail-row"><span>${c.numero||''}${motivo}</span><span>${fmt(v)}</span></div>`;
}
});
detailSections += detailSection('CANCELAMENTOS', cancRows, cancelamentos);
return `<!DOCTYPE html>
<html><head>
<meta charset="UTF-8">
<title>Recibo de Fechamento</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: 'Courier New', monospace; font-size: 13px; width: 80mm; margin: 0 auto; padding: 8px; }
@@ -1280,19 +1485,16 @@ function buildPrintHTML(data) {
.header h1 { font-size: 18px; }
.header h2 { font-size: 14px; font-weight: normal; }
hr { border: none; border-top: 1px dashed #000; margin: 6px 0; }
.section { margin: 6px 0; }
.section-title { font-weight: bold; font-size: 14px; border-top: 1px dashed #000; padding-top: 4px; margin-top: 6px; }
.row { display: flex; justify-content: space-between; padding: 2px 0; }
.row.space-between { justify-content: space-between; }
.row.below { margin-top: 4px; }
table { width: 100%; border-collapse: collapse; font-size: 12px; }
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; }
.detail-section { margin: 6px 0; }
.detail-title { font-weight: bold; font-size: 13px; border-top: 1px dashed #000; padding-top: 3px; }
.detail-row { display: flex; justify-content: space-between; padding: 1px 0; font-size: 12px; }
.detail-total { display: flex; justify-content: space-between; padding: 1px 0; font-weight: bold; border-top: 1px dotted #000; font-size: 12px; }
.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; }
.c2 { color: #888; }
@media print { body { margin: 0; } }
</style>
</head><body>
@@ -1305,30 +1507,29 @@ function buildPrintHTML(data) {
<div class="row"><span>Operador:</span><span>${data.operador}</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.total_operado || 0)}</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>` : ''}
${detailSections}
<hr>
<div class="section-title">CARTÕES</div>
<div class="row"><span>Crédito:</span><span>${fmt(data.credito||0)}</span></div>
<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="row"><span>PIX Cartão:</span><span>${fmt(pixTotal)}</span></div>
<div class="row"><span>Cancelamentos:</span><span>${fmt(cancelTotal)}</span></div>
<div class="row"><span>💸 Sangrias:</span><span>${fmt(sangrias)}</span></div>
<div class="row"><span>📝 Despesas:</span><span>${fmt(despesas)}</span></div>
<div class="row"><span>💰 Vales:</span><span>${fmt(vales)}</span></div>
<div class="row"><span>👥 À Receber:</span><span>${fmt(receber)}</span></div>
<div class="row"><span>🔵 PIX CNPJ:</span><span>${fmt(pixcnpj)}</span></div>
<div class="row"><span>💳 Crédito:</span><span>${fmt(cartCred)}</span></div>
<div class="row"><span>💳 Débito:</span><span>${fmt(cartDeb)}</span></div>
<div class="row"><span>💳 Alimentação:</span><span>${fmt(cartAli)}</span></div>
<div class="row"><span>❌ Cancelamentos:</span><span>${fmt(cancelamentos)}</span></div>
<hr>
<div class="grand-total">DIFERENÇA: ${diffStr}</div>
${data.observacoes ? `<div class="obs">Obs: ${data.observacoes}</div>` : ''}
<div class="footer">Gerado em ${new Date().toLocaleString('pt-BR')}</div>
</body></html>`;
}
function openPrintPreview(data) {
}
function openPrintPreview(data, mode) {
const html = mode === 'confirm' ? buildConfirmHTML(data) : buildPrintHTML(data);
// Remove preview anterior se existir
const existing = document.getElementById('print-preview-overlay');
if (existing) document.body.removeChild(existing);
@@ -1362,7 +1563,6 @@ function openPrintPreview(data) {
</style>
`;
const html = buildPrintHTML(data);
const fullHtml = `<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Recibo</title>${previewStyle}</head><body>${html}</body></html>`;
const iframe = document.createElement('iframe');
@@ -1423,9 +1623,8 @@ async function tauriRecibo() {
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 sysTroco = parseNum(val('sys-troco'));
// total_operado = soma de todas operações creditadas (não é o fechamento contado)
const totalOperado = cartCred + cartDeb + cartAli + cartPix + pixCnpjTotal;
// SYS total = troco + crédito + débito + alimentação + vales + receber + pixCartão + pixCnpj
const sysTotal = parseNum(val('sys-troco')) + cartCred + cartDeb + cartAli + valesTotal + receberTotal + cartPix + pixCnpjTotal;
const data = {
uuid: estado.uuid,
id_local: estado.id_local,
@@ -1433,11 +1632,11 @@ async function tauriRecibo() {
data: estado.data,
operador: estado.operador,
turno: estado.turno,
saldo_troco: sysTroco,
saldo_troco: parseNum(val('sys-troco')),
saldo_esperado: saldoEsp,
fechamento: fechamentoCounted,
diferenca: fechamentoCounted - saldoEsp,
total_operado: totalOperado,
diferenca: sysTotal - saldoEsp,
sys_total: sysTotal,
// Flat totals for ReciboData struct (Rust) — must be numbers
credito: cartCred,
debito: cartDeb,
@@ -1463,7 +1662,7 @@ async function tauriRecibo() {
// Tenta ESC/POS via Tauri primeiro; se falhar, abre preview
try {
return await window.__TAURI__.core.invoke('imprimir_recibo', { estado: JSON.stringify(data) });
return await window.__TAURI__.core.invoke('imprimir_recibo', { data: JSON.stringify(data) });
} catch(e) {
console.warn('ESC/POS falhou, abrindo preview:', e);
openPrintPreview(data);
@@ -1482,13 +1681,17 @@ async function tauriCarregarRascunho() {
// ─── Confirm Modal ────────────────────────────────────────────────────────
function openConfirmModal() {
console.log('[DEBUG] openConfirmModal chamado');
const operador = val('f-operador').trim();
const turno = val('f-turno').trim();
const loja = val('f-loja').trim();
console.log('[DEBUG] openConfirmModal campos:', {operador, turno, loja});
if (!operador || !turno || !loja) {
console.log('[DEBUG] openConfirmModal: campos faltando, alert');
alert('⚠️ Preencha Operador, Turno e Loja antes de enviar.');
return;
}
console.log('[DEBUG] openConfirmModal: campos OK, montando modal');
// Build summary
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);
@@ -1502,7 +1705,8 @@ function openConfirmModal() {
const pixcnpj = estado.pixcnpj.reduce((s,r) => s+(parseNum(r.valor||0)||0), 0);
const fechamento = parseNum(val('f-fech-dinheiro')) + parseNum(val('f-fech-cartoes')) + parseNum(val('f-fech-receber'));
const saldoEsp = parseNum(val('f-saldo-esperado'));
const diferenca = fechamento - saldoEsp;
const sysTotal = (parseNum(val('sys-troco')) || 0) + cartCred + cartDeb + cartAli + cartPix + vales + receber + pixcnpj;
const diferenca = sysTotal - saldoEsp;
const diffClass = diferenca === 0 ? '#2e7d32' : (diferenca > 0 ? '#e65100' : '#c0272d');
document.getElementById('confirm-summary').innerHTML =
`<div style="margin-bottom:6px"><strong>📅 Data:</strong> ${estado.data} &nbsp; <strong>👤 Operador:</strong> ${operador} &nbsp; <strong>⏰ Turno:</strong> ${turno} &nbsp; <strong>🏪 Loja:</strong> ${loja}</div>
@@ -1516,11 +1720,13 @@ function openConfirmModal() {
<div>💳 Débito: <strong>${fmt(cartDeb)}</strong></div>
<div>💳 Alimentação: <strong>${fmt(cartAli)}</strong></div>
<div>❌ Cancelamentos: <strong>${fmt(cancelamentos)}</strong></div>
<div>📋 Fechamento: <strong>${fmt(fechamento)}</strong></div>
<div>📋 Fechamento (contado): <strong>${fmt(fechamento)}</strong></div>
<div>🖥️ Total Sistema: <strong>${fmt(sysTotal)}</strong></div>
<hr style="border:none;border-top:1px dashed #b3d9f7;margin:6px 0">
<div>🎯 Saldo Esperado: <strong>${fmt(saldoEsp)}</strong></div>
<div>🎯 Saldo Esperado (contado): <strong>${fmt(saldoEsp)}</strong></div>
<div style="margin-top:4px">📐 Diferença: <strong style="color:${diffClass}">${fmt(diferenca)}</strong></div>
<div style="margin-top:4px;font-size:10px;color:#888">ID: ${estado.loja}_${estado.data}_${estado.operador}</div>`;
console.log('[DEBUG] openConfirmModal: tentando abrir modal');
document.getElementById('modal-confirm').classList.add('open');
}
@@ -1532,36 +1738,32 @@ document.getElementById('confirm-close').addEventListener('click', closeConfirmM
document.getElementById('confirm-cancel').addEventListener('click', closeConfirmModal);
document.getElementById('modal-confirm').addEventListener('click', e => { if (e.target.id === 'modal-confirm') closeConfirmModal(); });
// Print from confirm modal — abre preview no navegador
// Print from confirm modal — abre preview idêntico ao modal de confirmação
document.getElementById('confirm-print').addEventListener('click', async () => {
closeConfirmModal();
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 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 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 totalOperado = cartCred + cartDeb + cartAli + cartPix + (estado.pixcnpj.reduce((s,r) => s+(parseNum(r.valor||0)||0), 0));
const data = {
loja: estado.loja, data: estado.data, operador: estado.operador, turno: estado.turno,
saldo_troco: parseNum(val('sys-troco')) || 0,
saldo_esperado: saldoEsp,
fechamento: fechamentoCounted,
diferenca: fechamentoCounted - saldoEsp,
total_operado: totalOperado,
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||'' })),
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||'' })),
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||'' })),
credito: cartCred, debito: cartDeb, alimentacao: cartAli,
pix: cartPix, // flat float for buildPrintHTML
cartoes: { credito: estado.cartoes.credito, debito: estado.cartoes.debito, alimentacao: estado.cartoes.alimentacao, pix: estado.cartoes.pix },
voucher: cartPix,
observacoes: estado.observacoes,
};
openPrintPreview(data);
openPrintPreview(data, 'confirm');
});
// Confirm send
@@ -1593,6 +1795,11 @@ document.getElementById('confirm-send').addEventListener('click', async () => {
// ─── Buttons ───────────────────────────────────────────────────────────────
document.getElementById('btn-enviar').addEventListener('click', () => {
console.log('[DEBUG] btn-enviar clicado');
const operador = document.getElementById('f-operador')?.value;
const turno = document.getElementById('f-turno')?.value;
const loja = document.getElementById('f-loja')?.value;
console.log('[DEBUG] operador:', operador, 'turno:', turno, 'loja:', loja);
openConfirmModal();
});
@@ -1616,131 +1823,169 @@ document.getElementById('btn-limpar').addEventListener('click', () => {
if (!confirm('Limpar rascunho? Esta acção não pode ser revertida.')) return;
localStorage.removeItem(LS_KEY);
// Reseta estado e re-renderiza sem reload
estado = newEstado();
estado.loja = val('f-loja') || 'União';
estado.data = val('f-data') || new Date().toISOString().split('T')[0];
estado = {
uuid: crypto.randomUUID ? crypto.randomUUID() : Date.now().toString(),
loja: estado.loja, data: estado.data, operador: '', turno: '',
saldo_troco: 0, fechamento: 0, saldo_esperado: 0, diferenca: 0,
despesas: [], sangrias: [], pixcnpj: [], vales: [], receber: [], cancelamentos: [],
cartoes: { credito: [], debito: [], alimentacao: [], pix: [] },
fechamento_contado: { dinheiro: 0, cartoes: 0, receber: 0 },
observacoes: '', id_local: null, sync_status: 'local'
};
setVal('f-operador', ''); setVal('f-turno', '');
setVal('f-fech-dinheiro', ''); setVal('f-fech-cartoes', ''); setVal('f-fech-receber', '');
setVal('f-saldo-esperado', ''); setVal('f-obs', '');
setVal('sys-troco', '');
calcular();
renderAllTables();
syncUI();
showToast('Rascunho limpo.', 'info');
});
document.getElementById('btn-anterior').addEventListener('click', async () => {
const loja = val('f-loja') || 'União';
const btnAnterior = document.getElementById('btn-anterior');
if (btnAnterior) {
btnAnterior.addEventListener('click', async () => {
console.log('[DEBUG] btn-anterior clicado');
const loja = val('f-loja') || 'União';
console.log('[DEBUG] btn-anterior loja:', loja);
// Carrega lista de fechamentos recentes do Supabase com uuid
let recentList = [];
try {
recentList = await window.__TAURI__.core.invoke('sb_listar_recentes', { loja, limite: 20 });
} catch(e) {
console.warn('sb_listar_recentes falhou:', e);
}
// Salva rascunho atual antes de tudo
await tauriSaveLocal();
// 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();
}
// Calcula dia anterior
const prevDate = new Date(estado.data);
prevDate.setDate(prevDate.getDate() - 1);
const prevDateStr = prevDate.toISOString().split('T')[0];
let selectedDate = null;
let selectedId = null;
if (dateList.length > 0) {
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)`);
if (escolha !== null && escolha.trim() !== '') {
const idx = parseInt(escolha) - 1;
if (idx >= 0 && idx < dateList.length) {
selectedDate = dateList[idx];
selectedId = dateIdMap[selectedDate];
// Confirmação explícita antes de trocar de dia
const confirmMsg = `ATENÇÃO: vai carregar o fechamento de ${prevDateStr}.\n\nO formulário atual será LIMPO caso não tenha enviado.\n\nContinuar?`;
if (!confirm(confirmMsg)) {
console.log('[DEBUG] btn-anterior cancelado pelo usuário');
return;
}
// Carrega lista de fechamentos recentes do Supabase com uuid
let recentList = [];
try {
recentList = await window.__TAURI__.core.invoke('sb_listar_recentes', { loja, limite: 20 });
console.log('[DEBUG] sb_listar_recentes retornou:', recentList?.length, 'itens');
} catch(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();
}
console.log('[DEBUG] dateList:', dateList);
let selectedDate = null;
let selectedId = null;
if (dateList.length > 0) {
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)`);
if (escolha !== null && escolha.trim() !== '') {
const idx = parseInt(escolha) - 1;
if (idx >= 0 && idx < dateList.length) {
selectedDate = dateList[idx];
selectedId = dateIdMap[selectedDate];
}
}
}
}
// Se não escolheu data, usa dia anterior automático (sem id — carrega por data)
if (!selectedDate) {
const d = new Date(estado.data); d.setDate(d.getDate() - 1);
selectedDate = d.toISOString().split('T')[0];
}
// Se não escolheu data, usa dia anterior automático (sem id — carrega por data)
if (!selectedDate) {
selectedDate = prevDateStr;
}
console.log('[DEBUG] selectedDate:', selectedDate, 'selectedId:', selectedId);
await tauriSaveLocal();
setVal('f-data', selectedDate);
estado.data = selectedDate;
estado.uuid = crypto.randomUUID ? crypto.randomUUID() : Date.now().toString();
estado.id_local = null;
syncHeader();
setVal('f-data', selectedDate);
estado.data = selectedDate;
estado.uuid = crypto.randomUUID ? crypto.randomUUID() : Date.now().toString();
estado.id_local = null;
syncHeader();
// Tenta Supabase primeiro (com id se disponível), senão local
let rascunho = null;
if (selectedId) {
// Usa sb_buscar_por_id para buscar por id numérico do Supabase
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); }
}
// Tenta Supabase primeiro (com id se disponível), senão local
let rascunho = null;
if (selectedId) {
try {
rascunho = await window.__TAURI__.core.invoke('sb_buscar_por_id', { id: selectedId });
console.log('[DEBUG] sb_buscar_por_id retornou:', !!rascunho);
} 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 });
console.log('[DEBUG] sb_carregar_rascunho retornou:', !!rascunho);
} catch(e) { console.warn('sb_carregar_rascunho falhou:', e); }
}
if (!rascunho) {
try {
rascunho = await window.__TAURI__.core.invoke('carregar_rascunho', { loja, data: selectedDate });
} catch(e) { console.warn('carregar_rascunho local falhou:', e); }
}
if (!rascunho) {
try {
rascunho = await window.__TAURI__.core.invoke('carregar_rascunho', { loja, data: selectedDate });
console.log('[DEBUG] carregar_rascunho local retornou:', !!rascunho);
} catch(e) { console.warn('carregar_rascunho local falhou:', e); }
}
if (rascunho) {
if (rascunho.operador) { setVal('f-operador', rascunho.operador); estado.operador = rascunho.operador; }
if (rascunho.turno) { setVal('f-turno', rascunho.turno); estado.turno = rascunho.turno; }
if (rascunho.saldo_troco) setVal('sys-troco', fmtNum(rascunho.saldo_troco));
if (rascunho.saldo_esperado) setVal('f-saldo-esperado', fmtNum(rascunho.saldo_esperado));
if (rascunho.observacoes) { setVal('f-obs', rascunho.observacoes); estado.observacoes = rascunho.observacoes; }
if (rascunho.despesas && Array.isArray(rascunho.despesas)) {
estado.despesas = rascunho.despesas.map(d => ({ desc: d.desc || d.descricao || '', obs: d.obs || '', valor: parseNum(d.valor) || 0 }));
if (rascunho) {
console.log('[DEBUG] rascunho carregado com sucesso');
if (rascunho.operador) { setVal('f-operador', rascunho.operador); estado.operador = rascunho.operador; }
if (rascunho.turno) { setVal('f-turno', rascunho.turno); estado.turno = rascunho.turno; }
if (rascunho.saldo_troco) setVal('sys-troco', fmtNum(rascunho.saldo_troco));
if (rascunho.saldo_esperado) setVal('f-saldo-esperado', fmtNum(rascunho.saldo_esperado));
if (rascunho.observacoes) { setVal('f-obs', rascunho.observacoes); estado.observacoes = rascunho.observacoes; }
if (rascunho.despesas && Array.isArray(rascunho.despesas)) {
estado.despesas = rascunho.despesas.map(d => ({ desc: d.desc || d.descricao || '', obs: d.obs || '', valor: parseNum(d.valor) || 0 }));
}
if (rascunho.sangrias && Array.isArray(rascunho.sangrias)) {
estado.sangrias = rascunho.sangrias.map(s => ({ hora: s.hora || '', retirada: parseNum(s.retirada) || 0, gerente: s.gerente || '' }));
}
if (rascunho.pixcnpj && Array.isArray(rascunho.pixcnpj)) {
estado.pixcnpj = rascunho.pixcnpj.map(p => ({ nome: p.nome || '', valor: parseNum(p.valor) || 0 }));
}
if (rascunho.vales && Array.isArray(rascunho.vales)) {
estado.vales = rascunho.vales.map(v => ({ nome: v.nome || '', obs: v.obs || '', valor: parseNum(v.valor) || 0 }));
}
if (rascunho.receber && Array.isArray(rascunho.receber)) {
estado.receber = rascunho.receber.map(r => ({ nome: r.nome || '', valor: parseNum(r.valor) || 0 }));
}
if (rascunho.cancelamentos && Array.isArray(rascunho.cancelamentos)) {
estado.cancelamentos = rascunho.cancelamentos.map(c => ({ numero: c.numero || '', valor: parseNum(c.valor) || 0, motivo: c.motivo || '' }));
}
if (rascunho.cartoes) {
const norm = (arr) => Array.isArray(arr) ? arr.map(v => ({ valor: (typeof v === 'object' && v !== null) ? (parseNum(v.valor) || 0) : (parseNum(v) || 0) })) : [];
estado.cartoes.credito = norm(rascunho.cartoes.credito);
estado.cartoes.debito = norm(rascunho.cartoes.debito);
estado.cartoes.alimentacao = norm(rascunho.cartoes.alimentacao);
estado.cartoes.pix = norm(rascunho.cartoes.pix);
}
} else {
console.log('[DEBUG] nenhum rascunho encontrado para esta data');
estado.despesas = []; estado.sangrias = []; estado.pixcnpj = [];
estado.vales = []; estado.receber = []; estado.cancelamentos = [];
estado.cartoes = { credito: [], debito: [], alimentacao: [], pix: [] };
setVal('sys-troco', ''); setVal('f-saldo-esperado', ''); setVal('f-obs', '');
}
if (rascunho.sangrias && Array.isArray(rascunho.sangrias)) {
estado.sangrias = rascunho.sangrias.map(s => ({ hora: s.hora || '', retirada: parseNum(s.retirada) || 0, gerente: s.gerente || '' }));
}
if (rascunho.pixcnpj && Array.isArray(rascunho.pixcnpj)) {
estado.pixcnpj = rascunho.pixcnpj.map(p => ({ nome: p.nome || '', valor: parseNum(p.valor) || 0 }));
}
if (rascunho.vales && Array.isArray(rascunho.vales)) {
estado.vales = rascunho.vales.map(v => ({ nome: v.nome || '', obs: v.obs || '', valor: parseNum(v.valor) || 0 }));
}
if (rascunho.receber && Array.isArray(rascunho.receber)) {
estado.receber = rascunho.receber.map(r => ({ nome: r.nome || '', valor: parseNum(r.valor) || 0 }));
}
if (rascunho.cancelamentos && Array.isArray(rascunho.cancelamentos)) {
estado.cancelamentos = rascunho.cancelamentos.map(c => ({ numero: c.numero || '', valor: parseNum(c.valor) || 0, motivo: c.motivo || '' }));
}
if (rascunho.cartoes) {
estado.cartoes.credito = (rascunho.cartoes.credito || []).map(v => ({ valor: parseNum(v) || 0 }));
estado.cartoes.debito = (rascunho.cartoes.debito || []).map(v => ({ valor: parseNum(v) || 0 }));
estado.cartoes.alimentacao = (rascunho.cartoes.alimentacao || []).map(v => ({ valor: parseNum(v) || 0 }));
estado.cartoes.pix = (rascunho.cartoes.pix || []).map(v => ({ valor: parseNum(v) || 0 }));
}
} else {
estado.despesas = []; estado.sangrias = []; estado.pixcnpj = [];
estado.vales = []; estado.receber = []; estado.cancelamentos = [];
estado.cartoes = { credito: [], debito: [], alimentacao: [], pix: [] };
setVal('sys-troco', ''); setVal('f-saldo-esperado', ''); setVal('f-obs', '');
}
// Marcar que este é um rascunho carregado (não novo)
estado.sync_status = rascunho ? 'loaded' : 'local';
estado.sync_status = rascunho ? 'loaded' : 'local';
PRE_FILL_TABLES.forEach(ensureOneRow);
Object.keys(TABLES).forEach(renderTable);
updateTotals();
scheduleSave();
});
PRE_FILL_TABLES.forEach(ensureOneRow);
Object.keys(TABLES).forEach(renderTable);
updateTotals();
scheduleSave();
console.log('[DEBUG] btn-anterior concluído');
});
} else {
console.error('[DEBUG] btn-anterior NÃO encontrado no DOM!');
}
document.getElementById('f-obs').addEventListener('input', e => { estado.observacoes = e.target.value; scheduleSave(); });