@@ -427,6 +427,21 @@
< / div >
< / div >
<!-- CONTADORES -->
< div class = "section" >
< div class = "section-header" > 👥 Clientes & Frango Assado< / div >
< div style = "display:flex;gap:12px" >
< div style = "flex:1" >
< label style = "font-size:12px;color:#666" > Clientes< / label >
< input type = "number" id = "f-clientes" min = "0" placeholder = "0" style = "width:100%;padding:8px;border:1px solid var(--border);border-radius:var(--radius);font-size:15px" / >
< / div >
< div style = "flex:1" >
< label style = "font-size:12px;color:#666" > Frango Assado< / label >
< input type = "number" id = "f-frango" min = "0" placeholder = "0" style = "width:100%;padding:8px;border:1px solid var(--border);border-radius:var(--radius);font-size:15px" / >
< / div >
< / div >
< / div >
<!-- OBS -->
< div class = "section" style = "flex:1;display:flex;flex-direction:column" >
< div class = "section-header" > 📝 Observações< / div >
@@ -514,6 +529,12 @@
< div id = "cfg-vales-list" > < / div >
< button class = "btn-add-config" id = "cfg-add-vales" > + Adicionar nome< / button >
< / div >
< div class = "cfg-section" >
< div class = "cfg-section-title" > 🔐 Senha Admin (editar dias anteriores)< / div >
< div style = "display:flex;gap:6px;margin-top:4px" >
< input type = "password" id = "cfg-admin-password" style = "flex:1;padding:6px;border:1px solid #ddd;border-radius:4px;font-size:13px" placeholder = "Deixe em branco se não quiser senha" >
< / div >
< / div >
< / div >
< div class = "modal-footer" >
< button class = "btn-primary" id = "cfg-salvar" > Salvar Configuração< / button >
@@ -561,6 +582,7 @@ let config = {
despesas : [ 'Energia' , 'Água' , 'Manutenção' , 'Limpeza' , 'Insumos' ] ,
clientes : [ ] , // preenchimento livre — não é datalist fixo
vales _nomes : [ ] , // nomes usados em vales ( histórico para autofill)
admin _password : '' , // senha para editar dias anteriores no Supabase
} ;
// ─── App State ────────────────────────────────────────────────────────────
@@ -571,6 +593,7 @@ let estado = {
despesas : [ ] , sangrias : [ ] , pixcnpj : [ ] , vales : [ ] , receber : [ ] , cancelamentos : [ ] ,
cartoes : { credito : [ ] , debito : [ ] , alimentacao : [ ] , pix : [ ] } ,
fechamento _contado : { dinheiro : 0 , cartoes : 0 , receber : 0 } ,
clientes : 0 , frango : 0 , // contadores
observacoes : '' , id _local : null , sync _status : 'local'
} ;
@@ -832,8 +855,8 @@ function updateTotals() {
const receberTotal = estado . receber . reduce ( ( s , r ) => s + ( parseNum ( r [ 'valor' ] || 0 ) || 0 ) , 0 ) ;
document . getElementById ( 'sys-receber' ) . value = receberTotal > 0 ? fmtNum ( receberTotal ) : '' ;
// 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 ;
// SYS total = crédito + débito + alimentação + vales + receber + pixCartão + pixCnpj (NÃO inclui troco)
const sysTotal = cartCred + cartDeb + cartAli + valesTotal + receberTotal + cartPix + pixCnpjTotal ;
setText ( 'sys-total' , fmt ( sysTotal ) ) ;
// ── Fechamento Contado — auto-populate (readonly, filled from tables) ──
@@ -850,9 +873,10 @@ function updateTotals() {
const fr = receberTotal ;
setText ( 'fech-total' , fmt ( fd + fc + fr ) ) ;
// Diferença = Saldo Caixa − S aldo E sperado
const saldoCaixa = sysTotal ;
const diff = saldoCaixa - parseNum ( val ( 'f-saldo-esperado' ) ) ;
// Diferença = fechamento - s aldo_e sperado - cancelamentos
const fechamentoTotal = fd + fc + fr ;
const cancelTotal = estado . cancelamentos . reduce ( ( s , r ) => s + ( parseNum ( r . valor || 0 ) || 0 ) , 0 ) ;
const diff = fechamentoTotal - parseNum ( val ( 'f-saldo-esperado' ) ) - cancelTotal ;
const difEl = document . getElementById ( 'diferenca' ) ;
const badgeEl = document . getElementById ( 'dif-badge' ) ;
if ( difEl ) {
@@ -1060,6 +1084,8 @@ function buildEstadoForBackend() {
pix : estado . cartoes . pix . map ( r => r . valor || 0 ) ,
} ,
observacoes : estado . observacoes ,
clientes : estado . clientes ,
frango : estado . frango ,
sync _status : estado . sync _status ,
} ;
}
@@ -1076,6 +1102,8 @@ function saveState() {
cartoes : parseNum ( val ( 'f-fech-cartoes' ) ) ,
receber : parseNum ( val ( 'f-fech-receber' ) ) ,
} ;
estado . clientes = parseInt ( document . getElementById ( 'f-clientes' ) . value ) || 0 ;
estado . frango = parseInt ( document . getElementById ( 'f-frango' ) . value ) || 0 ;
estado . updatedAt = new Date ( ) . toISOString ( ) ;
try { localStorage . setItem ( LS _KEY , JSON . stringify ( estado ) ) ; } catch ( e ) { }
}
@@ -1095,6 +1123,8 @@ function loadState() {
estado . saldo _esperado = saved . saldo _esperado || 0 ;
estado . fechamento = saved . fechamento || 0 ;
estado . observacoes = saved . observacoes || '' ;
estado . clientes = saved . clientes || 0 ;
estado . frango = saved . frango || 0 ;
estado . sync _status = saved . sync _status || 'local' ;
// Restore tables (always as arrays of objects)
estado . despesas = Array . isArray ( saved . despesas ) ? saved . despesas : [ ] ;
@@ -1147,6 +1177,7 @@ function loadConfig() {
} catch ( e ) { }
}
function saveConfig ( ) {
config . admin _password = document . getElementById ( 'cfg-admin-password' ) . value . trim ( ) ;
try { localStorage . setItem ( CFG _KEY , JSON . stringify ( config ) ) ; } catch ( e ) { }
buildDatalists ( ) ;
}
@@ -1159,6 +1190,7 @@ function openConfig() {
renderCfgList ( 'despesas' , 'cfg-despesas-list' ) ;
renderCfgList ( 'clientes' , 'cfg-clientes-list' ) ;
renderCfgList ( 'vales_nomes' , 'cfg-vales-list' ) ;
document . getElementById ( 'cfg-admin-password' ) . value = config . admin _password || '' ;
}
function closeConfig ( ) { document . getElementById ( 'modal-config' ) . classList . remove ( 'open' ) ; }
function renderCfgList ( key , containerId ) {
@@ -1233,8 +1265,9 @@ function buildConfirmHTML(data) {
// Relatório completo — mesmo layout do PDF
const fmt = v => 'R$ ' + ( v || 0 ) . toFixed ( 2 ) . replace ( '.' , ',' ) ;
const fmtNeg = v => ( v < 0 ? '-' : '' ) + 'R$ ' + Math . abs ( v || 0 ) . toFixed ( 2 ) . replace ( '.' , ',' ) ;
// Diferença = fechamento - saldo_esperado - cancelamentos
const diferenca = data . diferenca !== undefined ? data . diferenca
: ( ( data . sys _total || 0 ) - ( data . saldo _esperado || 0 ) ) ;
: ( ( data . fechamento || 0 ) - ( data . saldo _esperado || 0 ) - ( data . cancelamentos || 0 ) ) ;
const diffStr = diferenca >= 0 ? ` + ${ fmt ( diferenca ) } ` : fmtNeg ( diferenca ) ;
const diffOk = diferenca === 0 ;
@@ -1279,10 +1312,10 @@ function buildConfirmHTML(data) {
const al = cartAli _arr [ i ] || 0 ;
const px = cartPix _arr [ i ] || 0 ;
cartTableRows += ` <tr>
<td> ${ cr > 0 ? fmt ( cr ) : '' } </td>
<td> ${ db > 0 ? fmt ( db ) : '' } </td>
<td> ${ al > 0 ? fmt ( al ) : '' } </td>
<td> ${ px > 0 ? fmt ( px ) : '' } </td>
<td> ${ cr > 0 ? fmt ( cr ) : '- ' } </td>
<td> ${ db > 0 ? fmt ( db ) : '- ' } </td>
<td> ${ al > 0 ? fmt ( al ) : '- ' } </td>
<td> ${ px > 0 ? fmt ( px ) : '- ' } </td>
</tr> ` ;
}
cartTableRows += ` <tr class="total-row">
@@ -1384,6 +1417,10 @@ ${cancelamentos_arr.map(c => `<div class="row"><span>${c.numero ? `Nº ${c.numer
<div class="row"><span>À Receber:</span><span> ${ fmt ( frec ) } </span></div>
<div class="row total-row"><span>TOTAL:</span><span> ${ fmt ( ftotal ) } </span></div>
<div class="section-title">CONTADORES</div>
<div class="row"><span>Clientes:</span><span> ${ data . clientes || 0 } </span></div>
<div class="row"><span>Frango Assado:</span><span> ${ data . frango || 0 } </span></div>
${ data . observacoes ? ` <div class="obs"> ${ data . observacoes } </div> ` : '' }
<div class="footer">Gerado em ${ new Date ( ) . toLocaleString ( 'pt-BR' ) } </div>
@@ -1394,8 +1431,9 @@ ${data.observacoes ? `<div class="obs">${data.observacoes}</div>` : ''}
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 ( '.' , ',' ) ;
// Diferença = fechamento - saldo_esperado - cancelamentos
const diferenca = data . diferenca !== undefined ? data . diferenca
: ( ( data . sys _total || 0 ) - ( data . saldo _esperado || 0 ) ) ;
: ( ( data . fechamento || 0 ) - ( data . saldo _esperado || 0 ) - ( data . cancelamentos || 0 ) ) ;
const diffStr = diferenca >= 0 ? ` + ${ fmt ( diferenca ) } ` : fmtNeg ( diferenca ) ;
const diffColor = diferenca === 0 ? '#2e7d32' : ( diferenca > 0 ? '#e65100' : '#c0272d' ) ;
@@ -1487,10 +1525,10 @@ function buildPrintHTML(data) {
const al = cartAli _arr [ i ] || 0 ;
const px = cartPix _arr [ i ] || 0 ;
cartTableRows += ` <tr>
<td> ${ cr > 0 ? fmt ( cr ) : '' } </td>
<td> ${ db > 0 ? fmt ( db ) : '' } </td>
<td> ${ al > 0 ? fmt ( al ) : '' } </td>
<td> ${ px > 0 ? fmt ( px ) : '' } </td>
<td> ${ cr > 0 ? fmt ( cr ) : '- ' } </td>
<td> ${ db > 0 ? fmt ( db ) : '- ' } </td>
<td> ${ al > 0 ? fmt ( al ) : '- ' } </td>
<td> ${ px > 0 ? fmt ( px ) : '- ' } </td>
</tr> ` ;
}
// Totals row
@@ -1606,8 +1644,75 @@ ${data.observacoes ? `<div class="obs">${data.observacoes}</div>` : ''}
return html ;
}
// ─── Relatório de Impressão (layout exato do usuário) ──────────────────────
function buildReportHTML ( data ) {
const fmt = v => 'R$ ' + ( v || 0 ) . toFixed ( 2 ) . replace ( '.' , ',' ) ;
const fmtNeg = v => ( v < 0 ? '-' : '' ) + 'R$ ' + Math . abs ( v || 0 ) . toFixed ( 2 ) . replace ( '.' , ',' ) ;
const cartCred = data . credito || 0 ;
const cartDeb = data . debito || 0 ;
const cartAli = data . alimentacao || 0 ;
const cartPix = data . voucher || 0 ;
const sangrias = data . sangrias || 0 ;
const despesas = data . despesas || 0 ;
const vales = data . vales || 0 ;
const receber = data . areceber || 0 ;
const pixcnpj = data . pixcnpj || 0 ;
const cancelamentos = data . cancelamentos || 0 ;
const fechamento = data . fechamento || 0 ;
const sysTotal = data . sys _total || 0 ;
const saldoEsp = data . saldo _esperado || 0 ;
const diferenca = data . diferenca !== undefined ? data . diferenca
: ( fechamento - saldoEsp - cancelamentos ) ;
const diffStr = diferenca >= 0 ? ` + ${ fmt ( diferenca ) } ` : fmtNeg ( diferenca ) ;
const diffColor = diferenca === 0 ? '#2e7d32' : '#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; }
hr { border: none; border-top: 1px dashed #000; margin: 6px 0; }
.row { display: flex; justify-content: space-between; padding: 2px 0; font-size: 12px; }
.row .val { font-weight: bold; }
.footer { text-align: center; font-size: 11px; color: #555; margin-top: 8px; }
@media print { body { margin: 0; } }
</style>
</head><body>
<div class="header">
<div>📅 Data: ${ data . data } 👤 Operador: ${ data . operador } ⏰ Turno: ${ data . turno } 🏪 Loja: ${ data . loja } </div>
</div>
<hr>
<div class="row"><span>💸 Sangrias:</span><span class="val"> ${ fmt ( sangrias ) } </span></div>
<div class="row"><span>📝 Despesas:</span><span class="val"> ${ fmt ( despesas ) } </span></div>
<div class="row"><span>💰 Vales:</span><span class="val"> ${ fmt ( vales ) } </span></div>
<div class="row"><span>👥 À Receber:</span><span class="val"> ${ fmt ( receber ) } </span></div>
<div class="row"><span>🔵 PIX CNPJ:</span><span class="val"> ${ fmt ( pixcnpj ) } </span></div>
<div class="row"><span>💳 Crédito:</span><span class="val"> ${ fmt ( cartCred ) } </span></div>
<div class="row"><span>💳 Débito:</span><span class="val"> ${ fmt ( cartDeb ) } </span></div>
<div class="row"><span>💳 Alimentação:</span><span class="val"> ${ fmt ( cartAli ) } </span></div>
<div class="row"><span>❌ Cancelamentos:</span><span class="val"> ${ fmt ( cancelamentos ) } </span></div>
<div class="row"><span>👥 Clientes:</span><span class="val"> ${ data . clientes || 0 } </span></div>
<div class="row"><span>🍗 Frango Assado:</span><span class="val"> ${ data . frango || 0 } </span></div>
<hr>
<div class="row"><span>📋 Fechamento (contado):</span><span class="val"> ${ fmt ( fechamento ) } </span></div>
<div class="row"><span>🖥️ Total Sistema:</span><span class="val"> ${ fmt ( sysTotal ) } </span></div>
<hr>
<div class="row"><span>🎯 Saldo Esperado (contado):</span><span class="val"> ${ fmt ( saldoEsp ) } </span></div>
<div class="row"><span>📐 Diferença:</span><span class="val" style="color: ${ diffColor } "> ${ diffStr } </span></div>
<hr>
<div style="text-align:center;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> ` ;
}
function openPrintPreview ( data , mode ) {
const html = mode === 'confirm' ? buildConfirmHTML ( data ) : buildPrintHTML ( data ) ;
const html = mode === 'confirm' ? buildConfirmHTML ( data )
: mode === 'report' ? buildReportHTML ( data )
: buildPrintHTML ( data ) ;
// Remove preview anterior se existir
const existing = document . getElementById ( 'print-preview-overlay' ) ;
if ( existing ) document . body . removeChild ( existing ) ;
@@ -1687,8 +1792,9 @@ function triggerWindowPrint() {
}
// ─── Recibo 80mm via Tauri (ESC/POS USB) ─────────────────────────────────
async function tauriRecibo ( ) {
// Build flat totals for the Rust ReciboData struct
// ─── Recibo 80mm via Tauri (ESC/POS USB) ─────────────────────────────────
// Build data object shared by both ESC/POS and preview
function buildReciboData ( ) {
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 ) ;
@@ -1701,9 +1807,11 @@ 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' ) ) ;
// 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 = {
// SYS total = crédito + débito + alimentação + voucher pix + pixcnpj + vales + receber (NÃO inclui troco)
const sysTotal = cartCred + cartDeb + cartAli + cartPix + pixCnpjTotal + valesTotal + receberTotal ;
// Diferença = fechamento - saldo_esperado - cancelamentos
const diferenca = fechamentoCounted - saldoEsp - cancelTotal ;
return {
uuid : estado . uuid ,
id _local : estado . id _local ,
loja : estado . loja ,
@@ -1713,8 +1821,11 @@ async function tauriRecibo() {
saldo _troco : parseNum ( val ( 'sys-troco' ) ) ,
saldo _esperado : saldoEsp ,
fechamento : fechamentoCounted ,
diferenca : sysTotal - saldoEsp ,
diferenca : diferenca ,
sys _total : sysTotal ,
fechamento _dinheiro : parseNum ( val ( 'f-fech-dinheiro' ) ) ,
fechamento _cartoes : parseNum ( val ( 'f-fech-cartoes' ) ) ,
fechamento _receber : parseNum ( val ( 'f-fech-receber' ) ) ,
// Flat totals for ReciboData struct (Rust) — must be numbers
credito : cartCred ,
debito : cartDeb ,
@@ -1726,25 +1837,33 @@ async function tauriRecibo() {
vales : valesTotal ,
areceber : receberTotal ,
cancelamentos : cancelTotal ,
// Full arrays for backend/storage
// Full arrays for preview
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 } ,
cartoes _credito : estado . cartoes . credito . map ( r => r . valor || 0 ) ,
cartoes _debito : estado . cartoes . debito . map ( r => r . valor || 0 ) ,
cartoes _alimentacao : estado . cartoes . alimentacao . map ( r => r . valor || 0 ) ,
cartoes _pix : estado . cartoes . pix . map ( r => r . valor || 0 ) ,
observacoes : estado . observacoes ,
clientes : estado . clientes ,
frango : estado . frango ,
sync _status : estado . sync _status ,
} ;
}
// Tenta ESC/POS via Tauri primeiro; se falhar, abre preview
async function tauriRecibo ( ) {
const data = buildReciboData ( ) ;
// Tenta ESC/POS via Tauri primeiro; se falhar, so lanca erro (preview tratado pelo caller)
try {
return await window . _ _TAURI _ _ . core . invoke ( 'imprimir_recibo' , { data : JSON . stringify ( data ) } ) ;
} catch ( e ) {
console . warn ( 'ESC/POS falhou, abrindo preview :' , e ) ;
openPrintPreview ( data ) ;
throw e ;
console . warn ( 'ESC/POS falhou:' , e ) ;
throw e ; // re-lanca para o caller tratar o preview
}
}
@@ -1783,8 +1902,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 sysTotal = ( parseNum ( val ( 'sys-troco' ) ) || 0 ) + cartCred + cartDeb + cartAli + cartPix + vales + receber + pixcnpj ;
const diferenca = sysTotal - saldoEsp ;
const sysTotal = cartCred + cartDeb + cartAli + cartPix + vales + receber + pixcnpj ;
const diferenca = fechamento - saldoEsp - cancelamentos ;
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>
@@ -1798,6 +1917,8 @@ 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>👥 Clientes: <strong> ${ estado . clientes || 0 } </strong></div>
<div>🍗 Frango Assado: <strong> ${ estado . frango || 0 } </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">
@@ -1818,40 +1939,18 @@ document.getElementById('modal-confirm').addEventListener('click', e => { if (e.
// Print from confirm modal — abre preview idêntico ao modal de confirmação
document . getElementById ( 'confirm-print' ) . addEventListener ( 'click' , async ( ) => {
// Usa buildReciboData para não duplicar lógica e ter todos os campos certos
let data ;
try {
data = buildReciboData ( ) ;
} catch ( e ) {
console . error ( 'Erro ao montar dados do recibo:' , e ) ;
alert ( 'Erro ao montar dados do recibo.' ) ;
return ;
}
// Fecha modal APÓS gerar preview (não antes)
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 fechamentoCounted = parseNum ( val ( 'f-fech-dinheiro' ) ) + parseNum ( val ( 'f-fech-cartoes' ) ) + parseNum ( val ( 'f-fech-receber' ) ) ;
const saldoEsp = parseNum ( val ( 'f-saldo-esperado' ) ) ;
const sysTotal = ( parseNum ( val ( 'sys-troco' ) ) || 0 ) + cartCred + cartDeb + cartAli + cartPix + vales + receber + pixcnpj ;
const diferenca = sysTotal - saldoEsp ;
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 _dinheiro : parseNum ( val ( 'f-fech-dinheiro' ) ) ,
fechamento _cartoes : parseNum ( val ( 'f-fech-cartoes' ) ) ,
fechamento _receber : parseNum ( val ( 'f-fech-receber' ) ) ,
fechamento : fechamentoCounted ,
diferenca : diferenca ,
sys _total : sysTotal ,
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 ,
voucher : cartPix ,
cartoes _credito : estado . cartoes . credito . map ( r => r . valor || 0 ) ,
cartoes _debito : estado . cartoes . debito . map ( r => r . valor || 0 ) ,
cartoes _alimentacao : estado . cartoes . alimentacao . map ( r => r . valor || 0 ) ,
cartoes _pix : estado . cartoes . pix . map ( r => r . valor || 0 ) ,
observacoes : estado . observacoes ,
} ;
openPrintPreview ( data , 'confirm' ) ;
openPrintPreview ( data , 'report' ) ;
} ) ;
// Confirm send
@@ -1882,26 +1981,24 @@ 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 ( ) ;
} ) ;
document . getElementById ( 'f-obs ' ) . addEventListener ( 'input ' , e => { estado . observacoes = e . target . value ; scheduleSave ( ) ; } ) ;
document . getElementById ( 'f-clientes' ) . addEventListener ( 'input' , e => { estado . clientes = parseInt ( e . target . value ) || 0 ; scheduleSave ( ) ; } ) ;
document . getElementById ( 'f-frango' ) . addEventListener ( 'input' , e => { estado . frango = parseInt ( e . target . value ) || 0 ; scheduleSave ( ) ; } ) ;
document . getElementById ( 'btn-recibo' ) . addEventListener ( 'click' , async ( ) => {
const btn = document . getElementById ( 'btn-recibo' ) ;
btn . disabled = true ;
try {
// Tenta ESC/POS primeiro; se falhar, abre preview no navegador
await tauriRecibo ( ) ;
// Se chegou aqui sem erro, ESC/POS funcionou
} catch ( e ) {
// ESC/POS falhou — openPrintPreview já foi chamado dentro de tauriRecibo
// Só mostra alerta se nem o preview abriu
console . warn ( 'Recibo:' , e ) ;
console . warn ( ' ESC/POS falhou:' , e ) ;
}
try {
const data = buildReciboData ( ) ;
openPrintPreview ( data , 'recibo' ) ;
} catch ( e2 ) {
console . error ( 'Preview falhou:' , e2 ) ;
alert ( 'Erro ao gerar preview do recibo.' ) ;
} finally {
btn . disabled = false ;
}
@@ -1910,7 +2007,6 @@ document.getElementById('btn-recibo').addEventListener('click', async () => {
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 = {
uuid : crypto . randomUUID ? crypto . randomUUID ( ) : Date . now ( ) . toString ( ) ,
loja : estado . loja , data : estado . data , operador : '' , turno : '' ,
@@ -1924,158 +2020,257 @@ document.getElementById('btn-limpar').addEventListener('click', () => {
setVal ( 'f-fech-dinheiro' , '' ) ; setVal ( 'f-fech-cartoes' , '' ) ; setVal ( 'f-fech-receber' , '' ) ;
setVal ( 'f-saldo-esperado' , '' ) ; setVal ( 'f-obs' , '' ) ;
setVal ( 'sys-troco' , '' ) ;
calcular ( ) ;
updateTotals ( ) ;
renderAllTables ( ) ;
syncUI ( ) ;
showToast ( 'Rascunho limpo.' , 'info' ) ;
} ) ;
// ─── Dia Anterior — seleção de registro do Supabase ────────────────────────
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 ) ;
const hasTauri = ! ! ( window . _ _TAURI _ _ && window . _ _TAURI _ _ . core ) ;
// Salva rascunho atual antes de tudo
await tauriSaveLocal ( ) ;
// Calcula dia anterior
const prevDate = new Date ( estado . data ) ;
prevDate . setDate ( prevDate . getDate ( ) - 1 ) ;
const prevDateStr = prevDate . toISOString ( ) . split ( 'T' ) [ 0 ] ;
// Confirmação explícita antes de trocar de dia
const confirmMsg = ` ATENÇÃO: vai carregar o fechamento de ${ prevDateStr } . \n \n O formulário atual será LIMPO caso não tenha enviado. \n \n Continuar? ` ;
if ( ! confirm ( confirmMsg ) ) {
console . log ( '[DEBUG] btn-anterior cancelado pelo usuário' ) ;
return ;
}
// Carrega lista de fechamentos recentes do Supabase com uuid
// Pega lista do Supabase
let recentList = [ ] ;
if ( hasTauri ) {
try {
recentList = await window . _ _TAURI _ _ . core . invoke ( 'sb_listar_recentes' , { loja , limite : 20 } ) ;
const loja = val ( 'f-loja' ) || 'União' ;
recentList = await window . _ _TAURI _ _ . core . invoke ( 'sb_listar_recentes' , { loja , limite : 50 } ) ;
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 ) ;
}
if ( ! recentList || recentList . length === 0 ) {
alert ( 'Nenhum fechamento encontrado no servidor para esta loja.' ) ;
return ;
}
// Mostra modal de seleção com datas
buildAnteriorModal ( recentList ) ;
} ) ;
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 ] ;
}
// Monta e abre o modal de seleção de dia anterior
function buildAnteriorModal ( recentList ) {
// Remove modal anterior
const old = document . getElementById ( 'modal-anterior ') ;
if ( old ) document . body . removeChild ( old ) ;
const overlay = document . createElement ( 'div' ) ;
overlay . id = 'modal-anterior' ;
overlay . style . cssText = `
position:fixed;top:0;left:0;right:0;bottom:0;
background:rgba(0,0,0,0.6);z-index:99998;
display:flex;align-items:center;justify-content:center;
` ;
const fmtDate = d => {
if ( ! d ) return '' ;
const [ y , m , day ] = d . split ( '-' ) ;
return ` ${ day } / ${ m } / ${ y } ` ;
} ;
const syncBadge = s => {
const colors = { synced : '#2e7d32' , local : '#e65100' , syncing : '#f57f17' , error : '#c0272d' } ;
const labels = { synced : '✓ Sync' , local : '⚠ Local' , syncing : '⏳ Sync' , error : '✗ Erro' } ;
return ` <span style="background: ${ colors [ s ] || '#888' } ;color:#fff;padding:1px 5px;border-radius:3px;font-size:10px"> ${ labels [ s ] || s || '?' } </span> ` ;
} ;
const rows = recentList . map ( ( item , i ) => `
<tr style="cursor:pointer" onclick="selectDiaAnterior( ${ i } )">
<td style="padding:6px 8px;border-bottom:1px solid #eee;font-size:12px"> ${ fmtDate ( item . data ) } </td>
<td style="padding:6px 8px;border-bottom:1px solid #eee;font-size:12px"> ${ item . operador || '?' } </td>
<td style="padding:6px 8px;border-bottom:1px solid #eee;font-size:12px"> ${ item . turno || '?' } </td>
<td style="padding:6px 8px;border-bottom:1px solid #eee;font-size:12px"> ${ syncBadge ( item . sync _status ) } </td>
</tr>
` ) . join ( '' ) ;
overlay . innerHTML = `
<div style="background:#fff;border-radius:10px;padding:20px;max-width:480px;width:90%;max-height:80vh;overflow-y:auto">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px">
<h2 style="margin:0;font-size:16px">📅 Selecionar Dia Anterior</h2>
<button onclick="closeAnteriorModal()" style="background:none;border:none;font-size:18px;cursor:pointer">✕</button>
</div>
<p style="font-size:12px;color:#666;margin:0 0 10px">Clique em um registro para carregar. Dados do Supabase.</p>
<table style="width:100%;border-collapse:collapse">
<thead>
<tr style="background:#f5f5f5">
<th style="padding:6px 8px;text-align:left;font-size:11px;color:#888">DATA</th>
<th style="padding:6px 8px;text-align:left;font-size:11px;color:#888">OPERADOR</th>
<th style="padding:6px 8px;text-align:left;font-size:11px;color:#888">TURNO</th>
<th style="padding:6px 8px;text-align:left;font-size:11px;color:#888">STATUS</th>
</tr>
</thead>
<tbody> ${ rows } </tbody>
</table>
</div>
` ;
document . body . appendChild ( overlay ) ;
window . _recentList = recentList ;
}
function closeAnteriorModal ( ) {
const m = document . getElementById ( 'modal-anterior' ) ;
if ( m ) document . body . removeChild ( m ) ;
window . _recentList = null ;
}
// Usuário clicou numa linha — pede senha se configurada
window . selectDiaAnterior = async function ( idx ) {
const item = window . _recentList [ idx ] ;
if ( ! item ) return ;
closeAnteriorModal ( ) ;
const pw = config . admin _password ;
if ( pw ) {
const input = prompt ( '🔐 Digite a senha admin para editar este registro:' ) ;
if ( input !== pw ) {
alert ( 'Senha incorreta. Acesso negado.' ) ;
return ;
}
}
// 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 ) ;
const hasTauri = ! ! ( window . _ _TAURI _ _ && window . _ _TAURI _ _ . core ) ;
if ( ! hasTauri ) { alert ( 'Tauri não disponível.' ) ; return ; }
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 ) {
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 ) ; }
// Primeiro tenta buscar por id (retorna registro completo)
let full = null ;
if ( item . id ) {
try { full = await window . _ _TAURI _ _ . core . invoke ( 'sb_buscar_por_id' , { id : item . id } ) ; } catch ( 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 ) ; }
// Fallback: buscar por uuid
if ( ! full && item . uuid ) {
try { full = await window . _ _TAURI _ _ . core . invoke ( 'sb_buscar_por_uuid ' , { uuid : item . uuid } ) ; } catch ( e ) { }
}
// Último fallback: carregar por loja+data
if ( ! full ) {
const loja = val ( 'f-loja' ) || 'União' ;
full = await window . _ _TAURI _ _ . core . invoke ( 'sb_carregar_rascunho' , { loja , data : item . data } ) ;
}
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 ( ! full ) { alert ( 'Registro não encontrado.' ) ; return ; }
// Aplica dados ao estado
if ( full . data ) { setVal ( 'f-data' , full . data ) ; estado . data = full . data ; }
if ( full . operador ) { setVal ( 'f-operador' , full . operador ) ; estado . operador = full . operador ; }
if ( full . turno ) { setVal ( 'f-turno' , full . turno ) ; estado . turno = full . turno ; }
if ( full . saldo _troco != null ) setVal ( 'sys-troco' , fmtNum ( full . saldo _troco ) ) ;
if ( full . saldo _esperado != null ) setVal ( 'f-saldo-esperado' , fmtNum ( full . saldo _esperado ) ) ;
if ( full . observacoes ) { setVal ( 'f-obs' , full . observacoes ) ; estado . observacoes = full . observacoes ; }
// Tenta carregar campos aninhados do campo "dados" (jsonb)
const dados = full . dados || full ;
if ( dados . despesas && Array . isArray ( dados . despesas ) ) {
estado . despesas = dados . despesas . map ( d => ( { desc : d . desc || d . descricao || '' , obs : d . obs || '' , valor : parseNum ( d . valor ) || 0 } ) ) ;
}
if ( dados . sangrias && Array . isArray ( dados . sangrias ) ) {
estado . sangrias = dados . sangrias . map ( s => ( { hora : s . hora || '' , retirada : parseNum ( s . retirada ) || 0 , gerente : s . gerente || '' } ) ) ;
}
if ( dados . pixcnpj && Array . isArray ( dados . pixcnpj ) ) {
estado . pixcnpj = dados . pixcnpj . map ( p => ( { nome : p . nome || '' , valor : parseNum ( p . valor ) || 0 } ) ) ;
}
if ( dados . vales && Array . isArray ( dados . vales ) ) {
estado . vales = dados . vales . map ( v => ( { nome : v . nome || '' , obs : v . obs || '' , valor : parseNum ( v . valor ) || 0 } ) ) ;
}
if ( dados . receber && Array . isArray ( dados . receber ) ) {
estado . receber = dados . receber . map ( r => ( { nome : r . nome || '' , valor : parseNum ( r . valor ) || 0 } ) ) ;
}
if ( dados . cancelamentos && Array . isArray ( dados . cancelamentos ) ) {
estado . cancelamentos = dados . cancelamentos . map ( c => ( { numero : c . numero || '' , valor : parseNum ( c . valor ) || 0 , motivo : c . motivo || '' } ) ) ;
}
if ( dados . cartoes ) {
const norm = arr => Array . isArray ( arr ) ? arr . map ( v => ( { valor : ( typeof v === 'object' ) ? ( parseNum ( v . valor ) || 0 ) : ( parseNum ( v ) || 0 ) } ) ) : [ ] ;
estado . cartoes . credito = norm ( dados . cartoes . credito ) ;
estado . cartoes . debito = norm ( dados . cartoes . debito ) ;
estado . cartoes . alimentacao = norm ( dados . cartoes . alimentacao ) ;
estado . cartoes . pix = norm ( dados . cartoes . pix ) ;
}
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' , '' ) ;
}
estado . sync _status = rascunho ? 'loaded' : 'local' ;
// Marca como modo de edição — ao clicar Enviar vai fazer PATCH, não POST
estado . uuid = full . uuid || item . uuid || estado . uuid ;
estado . id _local = full . id || item . id || null ;
estado . sync _status = 'editing '; // flag: editar registro existente
PRE _FILL _TABLES . forEach ( ensureOneRow ) ;
Object . keys ( TABLES ) . forEach ( renderTable ) ;
updateTotals ( ) ;
syncUI ( ) ;
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 ( ) ; } ) ;
alert ( ` ✅ Registro carregado! \n \n 📅 ${ item . data } \n 👤 ${ item . operador || '?' } — ${ item . turno || '?' } \n \n Ao clicar "Enviar Fechamento", o registro será ATUALIZADO no Supabase (não criado novamente). ` ) ;
} catch ( e ) {
console . error ( 'Erro ao carregar registro:' , e ) ;
alert ( 'Erro ao carregar registro: ' + ( e ? . message || e ) ) ;
}
} ;
// Intercepta o envio — se sync_status=editing, faz PATCH
const _originalBtnEnviarHandler = ( ) => {
const operador = val ( 'f-operador' ) . trim ( ) ;
const turno = val ( 'f-turno' ) . trim ( ) ;
const loja = val ( 'f-loja' ) . trim ( ) ;
if ( ! operador || ! turno || ! loja ) {
alert ( '⚠️ Preencha Operador, Turno e Loja antes de enviar.' ) ;
return ;
}
openConfirmModal ( ) ;
} ;
// Modifica btn-enviar para detectar modo editing
document . getElementById ( 'btn-enviar' ) . replaceWith (
Object . assign ( document . createElement ( 'button' ) , {
id : 'btn-enviar' , className : 'btn' , innerHTML : '💾 Enviar Fechamento' ,
onclick ( ) {
const operador = val ( 'f-operador' ) . trim ( ) ;
const turno = val ( 'f-turno' ) . trim ( ) ;
const loja = val ( 'f-loja' ) . trim ( ) ;
if ( ! operador || ! turno || ! loja ) {
alert ( '⚠️ Preencha Operador, Turno e Loja antes de enviar.' ) ;
return ;
}
if ( estado . sync _status === 'editing' ) {
// Confirmação antes de atualizar
if ( ! confirm ( ` ⚠️ ATENÇÃO: vai ATUALIZAR o registro existente no Supabase! \n \n UUID: ${ estado . uuid } \n \n Continuar? ` ) ) return ;
salvarEdicaoSupabase ( ) ;
} else {
openConfirmModal ( ) ;
}
}
} )
) ;
async function salvarEdicaoSupabase ( ) {
const hasTauri = ! ! ( window . _ _TAURI _ _ && window . _ _TAURI _ _ . core ) ;
if ( ! hasTauri ) { alert ( 'Tauri não disponível.' ) ; return ; }
const btn = document . getElementById ( 'btn-enviar' ) ;
if ( btn ) { btn . disabled = true ; btn . textContent = '⏳ Salvando...' ; }
try {
const data = buildReciboData ( ) ;
// Remove campos que não devem ir no PATCH
delete data . uuid ;
delete data . id _local ;
const result = await window . _ _TAURI _ _ . core . invoke ( 'sb_atualizar_fechamento' , {
uuid : estado . uuid ,
updates : JSON . stringify ( data )
} ) ;
console . log ( '[DEBUG] sb_atualizar_fechamento result:' , result ) ;
estado . sync _status = 'synced' ;
saveState ( ) ;
alert ( '✅ Registro atualizado com sucesso no Supabase!' ) ;
} catch ( e ) {
console . error ( 'Erro ao atualizar:' , e ) ;
alert ( '❌ Erro ao atualizar no Supabase:\n' + ( e ? . message || e ) ) ;
} finally {
if ( btn ) { btn . disabled = false ; btn . textContent = '💾 Enviar Fechamento' ; }
}
}
// ─── Boot ─────────────────────────────────────────────────────────────────
document . addEventListener ( 'DOMContentLoaded' , ( ) => {