fix: diferenca usa sys_total; novo layout 80mm sem ENTRADAS; confirm modal
This commit is contained in:
@@ -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();
|
||||
|
||||
@@ -165,13 +169,6 @@ impl ReciboData {
|
||||
}
|
||||
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"));
|
||||
@@ -228,16 +225,7 @@ impl ReciboData {
|
||||
out.extend(escpos::divider());
|
||||
}
|
||||
|
||||
// ── CARTÕES ──
|
||||
out.extend(escpos::line_bold("CARTOES"));
|
||||
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("Cancelamentos:", &self.fmt_money(self.cancelamentos)));
|
||||
out.extend(escpos::divider());
|
||||
|
||||
// ── CANCELAMENTOS (detalhe) ──
|
||||
// ── CANCELAMENTOS ──
|
||||
if !self.cancelamentos_arr.is_empty() {
|
||||
out.extend(escpos::line_bold("CANCELAMENTOS"));
|
||||
for linha in &self.cancelamentos_arr {
|
||||
@@ -254,11 +242,49 @@ impl ReciboData {
|
||||
out.extend(escpos::divider());
|
||||
}
|
||||
|
||||
// ── TROCO ──
|
||||
out.extend(escpos::line_bold("TROCO"));
|
||||
out.extend(escpos::kv("Troco:", &self.fmt_money(self.saldo_troco)));
|
||||
// ── CARTÕES ──
|
||||
out.extend(escpos::line_bold("CARTOES"));
|
||||
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("Cancelamentos:", &self.fmt_money(self.cancelamentos)));
|
||||
out.extend(escpos::divider());
|
||||
|
||||
// ── 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());
|
||||
}
|
||||
|
||||
// ── 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.sys_total - 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));
|
||||
|
||||
// ── OBSERVAÇÕES ──
|
||||
if let Some(ref obs) = self.observacoes {
|
||||
if !obs.trim().is_empty() {
|
||||
@@ -268,26 +294,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(
|
||||
|
||||
+77
-24
@@ -1229,10 +1229,68 @@ async function tauriEnviar() {
|
||||
}
|
||||
}
|
||||
|
||||
function buildConfirmHTML(data) {
|
||||
const fmt = v => 'R$ ' + (v || 0).toFixed(2).replace('.', ',');
|
||||
const sangrias = (data.sangrias_arr || data.sangrias || []).reduce((s,r) => s+(parseNum(r.retirada||r.valor||0)||0), 0);
|
||||
const despesas = (data.despesas_arr || data.despesas || []).reduce((s,r) => s+(parseNum(r.valor||0)||0), 0);
|
||||
const vales = (data.vales_arr || data.vales || []).reduce((s,r) => s+(parseNum(r.valor||0)||0), 0);
|
||||
const receber = (data.receber_arr || data.receber || []).reduce((s,r) => s+(parseNum(r.valor||0)||0), 0);
|
||||
const pixcnpj = (data.pixcnpj_arr || data.pixcnpj || []).reduce((s,r) => s+(parseNum(r.valor||0)||0), 0);
|
||||
const cancelamentos = (data.cancelamentos_arr || data.cancelamentos || []).reduce((s,c) => s+(parseNum(c.valor||0)||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');
|
||||
|
||||
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; }
|
||||
@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} <strong>👤 Operador:</strong> ${data.operador} <strong>⏰ Turno:</strong> ${data.turno}</div>
|
||||
<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}">${fmt(diferenca)}</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 diferenca = (data.fechamento || 0) - (data.total_operado || 0);
|
||||
const diferenca = (data.fechamento || 0) - (data.saldo_esperado || 0);
|
||||
const diffStr = diferenca >= 0 ? `+${fmt(diferenca)}` : fmt(diferenca);
|
||||
const diffColor = diferenca === 0 ? '#2e7d32' : (diferenca > 0 ? '#e65100' : '#c0272d');
|
||||
|
||||
@@ -1307,7 +1365,7 @@ function buildPrintHTML(data) {
|
||||
|
||||
<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>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>` : ''}
|
||||
@@ -1328,7 +1386,8 @@ ${data.observacoes ? `<div class="obs">Obs: ${data.observacoes}</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 +1421,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 +1481,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 +1490,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,
|
||||
@@ -1502,7 +1559,7 @@ 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 diferenca = (parseNum(val('sys-troco')) || 0) - 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} <strong>👤 Operador:</strong> ${operador} <strong>⏰ Turno:</strong> ${turno} <strong>🏪 Loja:</strong> ${loja}</div>
|
||||
@@ -1532,36 +1589,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
|
||||
|
||||
Reference in New Issue
Block a user