@@ -1022,7 +1022,9 @@ function init() {
const today = new Date ( ) . toISOString ( ) . split ( 'T' ) [ 0 ] ;
setVal ( 'f-data' , today ) ;
estado . data = today ;
setVal ( 'f-loja' , estado . loja ) ;
setVal ( 'f-loja' , estado . loja || 'União' ) ;
setVal ( 'f-operador' , estado . operador || '' ) ;
setVal ( 'f-turno' , estado . turno || '' ) ;
syncHeader ( ) ;
PRE _FILL _TABLES . forEach ( ensureOneRow ) ;
Object . keys ( TABLES ) . forEach ( renderTable ) ;
@@ -1034,7 +1036,7 @@ const LS_KEY = 'fc_v7';
const CFG _KEY = 'fc_cfg' ;
function buildEstadoForBackend ( ) {
// Monta o objeto que vai para o Supabase — inclui TUDO, todos os detalhes
// Monta o objeto exactly as o backend espera (storage.rs → salvar_fechamento)
return {
uuid : estado . uuid ,
id _local : estado . id _local ,
@@ -1042,22 +1044,20 @@ function buildEstadoForBackend() {
data : estado . data ,
operador : estado . operador ,
turno : estado . turno ,
saldo _troco : parseNum ( val ( 'sys-troco' ) ) ,
saldo _troco : parseNum ( val ( 'sys-troco' ) ) , // troco do sistema
saldo _esperado : parseNum ( val ( 'f-saldo-esperado' ) ) ,
fechamento : parseNum ( val ( 'f-fech-dinheiro' ) ) + parseNum ( val ( 'f-fech-cartoes' ) ) + parseNum ( val ( 'f-fech-receber' ) ) ,
diferenca : parseNum ( val ( 'f-saldo-esperado' ) ) - ( parseNum ( val ( 'f-fech-dinheiro' ) ) + parseNum ( val ( 'f-fech-cartoes' ) ) + parseNum ( val ( 'f-fech-receber' ) ) ) ,
despesas : estado . despesas . map ( r => ( { desc : r . desc || '' , obs : r . obs || '' , valor : r . valor || 0 } ) ) ,
sangrias : estado . sangrias . map ( r => ( { hora : r . hora || '' , retirada : r . retirada || 0 , gerente : r . gerente || '' } ) ) ,
pixcnpj : estado . pixcnpj . map ( r => ( { nome : r . nome || '' , valor : r . valor || 0 } ) ) ,
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 || '' } ) ) ,
// Cartões com detalhes completos (não só totais)
cartoes : {
credito : estado . cartoes . credito . map ( r => ( { valor : r . valor || 0 , obs : r . obs || '' } ) ) ,
debito : estado . cartoes . debito . map ( r => ( { valor : r . valor || 0 , obs : r . obs || '' } ) ) ,
alimentacao : estado . cartoes . alimentacao . map ( r => ( { valor : r . valor || 0 , obs : r . obs || '' } ) ) ,
pix : estado . cartoes . pix . map ( r => ( { valor : r . valor || 0 , obs : r . obs || '' } ) ) ,
credito : estado . cartoes . credito . map ( r => r . valor || 0 ) ,
debito : estado . cartoes . debito . map ( r => r . valor || 0 ) ,
alimentacao : estado . cartoes . alimentacao . map ( r => r . valor || 0 ) ,
pix : estado . cartoes . pix . map ( r => r . valor || 0 ) ,
} ,
observacoes : estado . observacoes ,
sync _status : estado . sync _status ,
@@ -1084,8 +1084,44 @@ function loadState() {
const raw = localStorage . getItem ( LS _KEY ) ;
if ( ! raw ) return false ;
const saved = JSON . parse ( raw ) ;
// Merge carefully (cartoes may be in old format)
Object . assign ( estado , saved ) ;
// Restore simple fields
estado . uuid = saved . uuid || estado . uuid ;
estado . id _local = saved . id _local !== undefined ? saved . id _local : null ;
estado . loja = saved . loja || estado . loja ;
estado . data = saved . data || estado . data ;
estado . operador = saved . operador || estado . operador ;
estado . turno = saved . turno || estado . turno ;
estado . saldo _troco = saved . saldo _troco || 0 ;
estado . saldo _esperado = saved . saldo _esperado || 0 ;
estado . fechamento = saved . fechamento || 0 ;
estado . observacoes = saved . observacoes || '' ;
estado . sync _status = saved . sync _status || 'local' ;
// Restore tables (always as arrays of objects)
estado . despesas = Array . isArray ( saved . despesas ) ? saved . despesas : [ ] ;
estado . sangrias = Array . isArray ( saved . sangrias ) ? saved . sangrias : [ ] ;
estado . pixcnpj = Array . isArray ( saved . pixcnpj ) ? saved . pixcnpj : [ ] ;
estado . vales = Array . isArray ( saved . vales ) ? saved . vales : [ ] ;
estado . receber = Array . isArray ( saved . receber ) ? saved . receber : [ ] ;
estado . cancelamentos = Array . isArray ( saved . cancelamentos ) ? saved . cancelamentos : [ ] ;
// Restore cartoes — each entry must be { valor: number }
if ( saved . cartoes ) {
estado . cartoes . credito = Array . isArray ( saved . cartoes . credito )
? saved . cartoes . credito . map ( v => ( { valor : typeof v === 'object' && v !== null ? ( v . valor || 0 ) : ( parseNum ( v ) || 0 ) } ) )
: [ ] ;
estado . cartoes . debito = Array . isArray ( saved . cartoes . debito )
? saved . cartoes . debito . map ( v => ( { valor : typeof v === 'object' && v !== null ? ( v . valor || 0 ) : ( parseNum ( v ) || 0 ) } ) )
: [ ] ;
estado . cartoes . alimentacao = Array . isArray ( saved . cartoes . alimentacao )
? saved . cartoes . alimentacao . map ( v => ( { valor : typeof v === 'object' && v !== null ? ( v . valor || 0 ) : ( parseNum ( v ) || 0 ) } ) )
: [ ] ;
estado . cartoes . pix = Array . isArray ( saved . cartoes . pix )
? saved . cartoes . pix . map ( v => ( { valor : typeof v === 'object' && v !== null ? ( v . valor || 0 ) : ( parseNum ( v ) || 0 ) } ) )
: [ ] ;
}
// Restore fechamento_contado
if ( saved . fechamento _contado ) {
estado . fechamento _contado = saved . fechamento _contado ;
}
setVal ( 'f-data' , estado . data || '' ) ;
setVal ( 'f-operador' , estado . operador || '' ) ;
setVal ( 'f-turno' , estado . turno || '' ) ;
@@ -1093,9 +1129,10 @@ function loadState() {
setVal ( 'f-obs' , estado . observacoes || '' ) ;
setVal ( 'f-saldo-esperado' , estado . saldo _esperado === 0 ? '' : fmtNum ( estado . saldo _esperado ) ) ;
setVal ( 'sys-troco' , estado . saldo _troco === 0 ? '' : fmtNum ( estado . saldo _troco ) ) ;
setVal ( 'f-fech-dinheiro' , estado . fechamento _contado && estado . fechamento _contado . dinheiro ? fmtNum ( estado . fechamento _contado . dinheiro ) : '' ) ;
setVal ( 'f-fech-cartoes' , estado . fechamento _contado && estado . fechamento _contado . cartoes ? fmtNum ( estado . fechamento _contado . cartoes ) : '' ) ;
setVal ( 'f-fech-receber ' , estado . fechamento _contado && estado . fechamento _contado . receber ? fmtNum ( estado . fechamento _contado . receber ) : '' ) ;
const fc = estado . fechamento _contado || { } ;
setVal ( 'f-fech-dinheiro' , fc . dinheiro ? fmtNum ( fc . dinheiro ) : '' ) ;
setVal ( 'f-fech-cartoes ' , fc . cartoes ? fmtNum ( fc . cartoes ) : '' ) ;
setVal ( 'f-fech-receber' , fc . receber ? fmtNum ( fc . receber ) : '' ) ;
syncHeader ( ) ;
PRE _FILL _TABLES . forEach ( ensureOneRow ) ;
Object . keys ( TABLES ) . forEach ( renderTable ) ;
@@ -1192,40 +1229,22 @@ async function tauriEnviar() {
}
}
// ─── Print Preview Window ──────────────────────────────────────────────────
function buildPrintHTML ( data ) {
function buildConfirmHTML ( data ) {
const fmt = v => 'R$ ' + ( v || 0 ) . toFixed ( 2 ) . replace ( '.' , ',' ) ;
const vSangrias = ( data . sangrias || [ ] ) . reduce ( ( s , r ) => s + ( parseFloat ( r . retirada ) || 0 ) , 0 ) ;
const vDespesas = ( data . despesas || [ ] ) . reduce ( ( s , r ) => s + ( parseFloat ( r . valor ) || 0 ) , 0 ) ;
const vVales = ( data . vales || [ ] ) . reduce ( ( s , r ) => s + ( parseFloat ( r . valor ) || 0 ) , 0 ) ;
const vReceber = ( data . receber || [ ] ) . reduce ( ( s , r ) => s + ( parseFloat ( r . valor ) || 0 ) , 0 ) ;
const vPixCnpj = ( data . pixcnpj || [ ] ) . reduce ( ( s , r ) => s + ( parseFloat ( r . valor ) || 0 ) , 0 ) ;
const vCredito = data . cartoes ? . credito ? . reduce ? . ( ( s , r ) => s + ( parseFloat ( r ) || 0 ) , 0 ) || data . credito || 0 ;
const vDebito = data . cartoes ? . debito ? . reduce ? . ( ( s , r ) => s + ( parseFloat ( r ) || 0 ) , 0 ) || data . debito || 0 ;
const vAliment = data . cartoes ? . alimentacao ? . reduce ? . ( ( s , r ) => s + ( parseFloat ( r ) || 0 ) , 0 ) || data . alimentacao || 0 ;
const vPixCartao = data . cartoes ? . pix ? . reduce ? . ( ( s , r ) => s + ( parseFloat ( r ) || 0 ) , 0 ) || 0 ;
const vCancel = ( data . cancelamentos || [ ] ) . reduce ( ( s , r ) => s + ( parseFloat ( r . valor ) || 0 ) , 0 ) ;
// Diferença = Fechamento Total − Saldo Esperado
const vFechamento = ( data . fechamento || 0 ) ;
const diferenca = vFechamento - ( data . saldo _esperado || 0 ) ;
const diffStr = diferenca > = 0 ? ` + ${ fmt ( diferenca ) } ` : fmt ( diferenca ) ;
const diffColor = diferenca === 0 ? '#2e7d32' : ( diferenca > 0 ? '#e65100' : '#c0272d' ) ;
const fmtRow = ( label , valor ) =>
` <div class="row"><span> ${ label } </span><span> ${ fmt ( valor ) } </span></div> ` ;
const fmtDetail = ( label , valor , obs ) =>
` <div class="row sub"><span> ${ label } </span><span> ${ fmt ( valor ) } </span></div> ` ;
const mkDet = ( arr , fmtFn ) => {
if ( ! arr || arr . length === 0 ) return '' ;
return arr . map ( r => {
if ( ! r ) return '' ;
const v = fmtFn ( r ) ;
const nome = r . nome || r . desc || r . hora || r . numero || '' ;
const obs = r . obs || r . motivo || '' ;
return fmtDetail ( nome , v , obs ) ;
} ) . join ( '' ) ;
} ;
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>
@@ -1233,122 +1252,216 @@ function buildPrintHTML(data) {
<title>Relatório de Fechamento</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: 'Courier New', monospace; font-size: 12 px; width: 80mm; margin: 0 auto; padding: 6 px; }
.header { text-align: center; margin-bottom: 6 px; }
.header h1 { font-size: 16 px; }
.header h2 { font-size: 12 px; font-weight: normal; }
hr { border: none; border-top: 1px dashed #000 ; margin: 4 px 0; }
.row { display: flex; justify-content: space-between; padding: 1 px 0; }
.row.sub { padding-left: 8px; font-size: 11px; color: #444 ; }
.row strong { font-weight: bold ; }
.section-title { font-weight: bold; font-size: 12px; border-top: 1px dashed #000; padding-top: 3px; margin-top: 5px; }
.grand-total { font-size: 14px; font-weight: bold; text-align: center; padding: 5px; border: 2px solid #000; margin: 6px 0; }
.footer { text-align: center; font-size: 10px; color: #777; margin-top: 6px; }
@media print { body { margin: 0; width: 80mm; } }
body { font-family: 'Courier New', monospace; font-size: 13 px; width: 80mm; margin: 0 auto; padding: 8 px; }
.header { text-align: center; margin-bottom: 8 px; }
.header h1 { font-size: 18 px; }
.header h2 { font-size: 14 px; font-weight: normal; }
hr { border: none; border-top: 1px dashed #b3d9f7 ; margin: 6 px 0; }
.row { display: flex; justify-content: space-between; padding: 2 px 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>
<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 class="row"><span>DATA:</span><span> ${ data . data || '' } </span ></div>
<div class="row"><span>OPERADOR:</span><span> ${ data . operador || '' } </span ></div>
<div class="row"><span>TURNO:</span><span> ${ data . turno || '' } </span ></div>
<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 class="section-title">SANGRIAS </div>
${ mkDet ( data . sangrias , r => parseFloat ( r . retirada ) || 0 ) }
${ fmtRow ( 'TOTAL SANGRIAS' , vSangrias ) }
<div class="section-title">DESPESAS</div>
${ mkDet ( data . despesas , r => parseFloat ( r . valor ) || 0 ) }
${ fmtRow ( 'TOTAL DESPESAS' , vDespesas ) }
<div class="section-title">VALES</div>
${ mkDet ( data . vales , r => parseFloat ( r . valor ) || 0 ) }
${ fmtRow ( 'TOTAL VALES' , vVales ) }
<div class="section-title">A RECEBER</div>
${ mkDet ( data . receber , r => parseFloat ( r . valor ) || 0 ) }
${ fmtRow ( 'TOTAL A RECEBER' , vReceber ) }
<div class="section-title">PIX</div>
<div class="section-title" style="font-size:11px;font-weight:normal">— Cartão —</div>
${ fmtRow ( 'PIX CARTAO' , vPixCartao ) }
<div class="section-title" style="font-size:11px;font-weight:normal">— CNPJ —</div>
${ mkDet ( data . pixcnpj , r => parseFloat ( r . valor ) || 0 ) }
${ fmtRow ( 'TOTAL PIX CNPJ' , vPixCnpj ) }
<div class="section-title">CARTOES</div>
${ fmtRow ( 'CREDITO' , vCredito ) }
${ fmtRow ( 'DEBITO' , vDebito ) }
${ fmtRow ( 'VOUCHER' , vAliment ) }
<div class="section-title">CANCELAMENTOS</div>
${ mkDet ( data . cancelamentos , r => parseFloat ( r . valor ) || 0 ) }
${ fmtRow ( 'TOTAL CANCELAMENTOS' , vCancel ) }
<hr>
${ fmtRow ( 'SALDO ESPERADO' , data . saldo _esperado || 0 ) }
${ fmtRow ( 'FECHAMENTO' , vFechamento ) }
<div class="grand-total" style="color: ${ diffColor } ">DIFERENCA: ${ diffStr } </div>
<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> ` ;
}
function openPrintPreview ( data ) {
// Gera HTML e injeta no body para window.print()
const html = buildPrintHTML ( data ) ;
const style = document . createElement ( 'style' ) ;
style . id = '_print_style' ;
style . textContent = `
@media print {
body { width: 80mm; margin: 0 auto; font-family: 'Courier New', monospace; font-size: 13px; }
.no-print, .print-actions { display: none !important; }
hr { border-top: 1px dashed #000 !important; }
}
.print-actions { position: fixed; top: 10px; right: 10px; z-index: 9999; }
.print-actions button { padding: 8px 16px; cursor: pointer; background: #2563eb; color: #fff; border: none; border-radius: 4px; font-size: 14px; margin-left: 8px; }
.print-actions button:hover { background: #1d4ed8; }
.print-actions .btn-back { background: #6b7280; }
.print-actions .btn-back:hover { background: #4b5563; }
// ─── Print Preview Window ──────────────────────────────────────────────────
function buildPrintHTML ( data ) {
const fmt = v => 'R$ ' + ( v || 0 ) . toFixed ( 2 ) . replace ( '.' , ',' ) ;
// Usa a diferenca ja calculada pelo tauriRecibo (sys_total - saldo_esperado),
// igual ao modal de confirmacao — nao recalcula de fechamento.
const diferenca = data . diferenca !== undefined ? data . diferenca
: ( ( data . sys _total || 0 ) - ( data . saldo _esperado || 0 ) ) ;
const diffStr = diferenca >= 0 ? ` + ${ fmt ( diferenca ) } ` : fmt ( diferenca ) ;
const diffColor = diferenca === 0 ? '#2e7d32' : ( diferenca > 0 ? '#e65100' : '#c0272d' ) ;
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> ` ; } ) ;
}
// 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> ` ; } ) ;
}
// Vales
if ( data . vales && data . vales . length > 0 ) {
data . vales . forEach ( v => { if ( v . nome || v . valor ) rows += ` <tr><td> ${ v . nome || '' } </td><td> ${ fmt ( v . valor ) } </td><td> ${ v . obs || '' } </td></tr> ` ; } ) ;
}
// Receber
if ( data . receber && data . receber . length > 0 ) {
data . receber . forEach ( r => { if ( r . nome || r . valor ) rows += ` <tr><td> ${ r . nome || '' } </td><td> ${ fmt ( r . valor ) } </td><td></td></tr> ` ; } ) ;
}
// PIX CNPJ (flat total, not array)
if ( pixCnpjTotal > 0 ) {
rows += ` <tr><td>PIX CNPJ</td><td> ${ fmt ( pixCnpjTotal ) } </td><td></td></tr> ` ;
}
// Cancelamentos
if ( cancelTotal > 0 ) {
rows += ` <tr><td>Cancelamentos</td><td> ${ fmt ( cancelTotal ) } </td><td></td></tr> ` ;
}
return ` <!DOCTYPE html>
<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 #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; }
.grand-total { font-size: 16px; font-weight: bold; text-align: center; padding: 6px; border: 2px solid #000; margin: 8px 0; }
.obs { font-size: 11px; color: #555; margin-top: 6px; white-space: pre-wrap; }
.footer { text-align: center; font-size: 11px; color: #555; margin-top: 8px; }
@media print { body { margin: 0; } }
</style>
</head><body>
<div class="header">
<h1> ${ data . loja } </h1>
<h2>Fechamento de Caixa</h2>
</div>
<hr>
<div class="row"><span>Data:</span><span> ${ data . data } </span></div>
<div class="row"><span>Operador:</span><span> ${ data . operador } </span></div>
<div class="row"><span>Turno:</span><span> ${ data . turno } </span></div>
<div class="section-title">ENTRADAS</div>
<div class="row"><span>Saldo Troco:</span><span> ${ fmt ( data . saldo _troco ) } </span></div>
<div class="row"><span>Fechamento:</span><span> ${ fmt ( data . fechamento ) } </span></div>
<div class="row"><span>Saldo Esperado:</span><span> ${ fmt ( data . saldo _esperado ) } </span></div>
${ rows ? ` <div class="section-title">DETALHES</div><table><tbody> ${ rows } </tbody></table> ` : '' }
${ data . observacoes ? ` <div class="obs">Obs: ${ data . observacoes } </div> ` : '' }
<hr>
<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>
<hr>
<div class="grand-total">DIFERENÇA: ${ diffStr } </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 ) ;
// Remove preview anterior se existir
const existing = document . getElementById ( 'print-preview-overlay' ) ;
if ( existing ) document . body . removeChild ( existing ) ;
const overlay = document . createElement ( 'div' ) ;
overlay . id = 'print-preview-overlay' ;
overlay . style . cssText = `
position: fixed; top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0,0,0,0.85); z-index: 99999;
display: flex; flex-direction: column; align-items: center;
font-family: 'Courier New', monospace;
` ;
const actions = document . createElement ( 'div' ) ;
actions . className = 'print-actions no-print' ;
actions . innerHTML =
'<button class="btn-back" id="_btn_back_print">← Voltar ao App</button>' +
'<button onclick="window.print()">🖨️ Imprimir (Ctrl+P)</button>' ;
// Guarda originais
const originalBody = document . body . innerHTML ;
const originalOnAfterPrint = window . onafterprint ;
const previewStyle = `
<style>
body { width: 80mm; margin: 0 auto; font-family: 'Courier New', monospace; font-size: 13px; background: #fff; padding: 10px; }
.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 #000; 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; }
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; }
.grand-total { font-size: 16px; font-weight: bold; text-align: center; padding: 6px; border: 2px solid #000; margin: 8px 0; }
.obs { font-size: 11px; color: #555; margin-top: 6px; white-space: pre-wrap; }
.footer { text-align: center; font-size: 11px; color: #555; margin-top: 8px; }
@media print { body { background: #fff; } }
</style>
` ;
// Botão voltar restaura sem reload
document . addEventListener ( 'click' , function onBackClick ( e ) {
if ( e . target . id === '_btn_back_print' ) {
document . body . innerHTML = originalBody ;
const oldStyle = document . getElementById ( '_print_style' ) ;
if ( oldStyle ) oldStyle . remove ( ) ;
document . removeEventListener ( 'click' , onBackClick ) ;
window . onafterprint = originalOnAfterPrint ;
// Re-init app state — mínimo viable restore
initApp ( ) ;
}
} ) ;
const fullHtml = ` <!DOCTYPE html><html><head><meta charset="UTF-8"><title>Recibo</title> ${ previewStyle } </head><body> ${ html } </body></html> ` ;
document . body . innerHTML = html ;
document . head . appendChild ( style ) ;
document . body . insertBefore ( actions , document . body . firstChild ) ;
const iframe = document . createElement ( 'iframe' ) ;
iframe . style . cssText = 'width:360px; height:600px; border:2px solid #555; margin-top:16px; background:#fff;' ;
iframe . id = 'print-iframe' ;
window . onafterprint = ( ) => {
document . body . innerHTML = originalBody ;
const oldStyle = document . getElementById ( '_print_style' ) ;
if ( oldStyle ) oldStyle . remove ( ) ;
window . onafterprint = originalOnAfterPrint ;
document . removeEventListener ( 'click' , onBackClick ) ;
initApp ( ) ;
const btnFechar = document . createElement ( 'button' ) ;
btnFechar . textContent = '✕ Fechar' ;
btnFechar . style . cssText = `
margin-top: 12px; padding: 10px 32px;
background: #444; color: #fff; border: none; border-radius: 6px;
font-size: 15px; cursor: pointer;
` ;
btnFechar . onclick = ( ) => {
document . body . removeChild ( overlay ) ;
} ;
const btnImprimir = document . createElement ( 'button' ) ;
btnImprimir . textContent = '🖨️ Imprimir' ;
btnImprimir . style . cssText = `
margin-top: 8px; padding: 10px 32px;
background: #e65100; color: #fff; border: none; border-radius: 6px;
font-size: 15px; cursor: pointer;
` ;
btnImprimir . onclick = ( ) => {
iframe . contentWindow . focus ( ) ;
iframe . contentWindow . print ( ) ;
} ;
overlay . appendChild ( iframe ) ;
overlay . appendChild ( btnImprimir ) ;
overlay . appendChild ( btnFechar ) ;
document . body . appendChild ( overlay ) ;
const iframeDoc = iframe . contentWindow . document ;
iframeDoc . open ( ) ;
iframeDoc . write ( fullHtml ) ;
iframeDoc . close ( ) ;
}
function triggerWindowPrint ( ) {
@@ -1371,8 +1484,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' ) ) ;
// 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 ,
@@ -1380,18 +1493,12 @@ 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 : sysTroco - saldoEsp ,
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 || '' } ) ) ,
cartoes : { credito : cartCred , debito : cartDeb , alimentacao : cartAli , pix : cartPix } ,
// Flat totals for ReciboData struct (Rust)
diferenca : sysTotal - saldoEsp ,
sys _total : sysTotal ,
// Flat totals for ReciboData struct (Rust) — must be numbers
credito : cartCred ,
debito : cartDeb ,
alimentacao : cartAli ,
@@ -1402,13 +1509,21 @@ async function tauriRecibo() {
vales : valesTotal ,
areceber : receberTotal ,
cancelamentos : cancelTotal ,
// Full arrays for backend/storage
despesas _arr : estado . despesas . map ( r => ( { desc : r . desc || '' , obs : r . obs || '' , valor : r . valor || 0 } ) ) ,
sangrias _arr : estado . sangrias . map ( r => ( { hora : r . hora || '' , retirada : parseNum ( r . retirada ) || 0 , gerente : r . gerente || '' } ) ) ,
pixcnpj _arr : estado . pixcnpj . map ( r => ( { nome : r . nome || '' , valor : r . valor || 0 } ) ) ,
vales _arr : estado . vales . map ( r => ( { nome : r . nome || '' , obs : r . obs || '' , valor : r . valor || 0 } ) ) ,
receber _arr : estado . receber . map ( r => ( { nome : r . nome || '' , valor : r . valor || 0 } ) ) ,
cancelamentos _arr : estado . cancelamentos . map ( r => ( { numero : r . numero || '' , valor : r . valor || 0 , motivo : r . motivo || '' } ) ) ,
cartoes : { credito : cartCred , debito : cartDeb , alimentacao : cartAli , pix : cartPix } ,
observacoes : estado . observacoes ,
sync _status : estado . sync _status ,
} ;
// 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 ) ;
@@ -1427,13 +1542,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 ) ;
@@ -1447,7 +1566,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 = ( parseNum ( val ( 'sys-troco' ) ) || 0 ) - 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 } <strong>👤 Operador:</strong> ${ operador } <strong>⏰ Turno:</strong> ${ turno } <strong>🏪 Loja:</strong> ${ loja } </div>
@@ -1461,11 +1581,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' ) ;
}
@@ -1477,30 +1599,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 ( ) ;
// Reconstrói os mesmos dados do tauriRecibo e abre preview
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 data = {
loja : estado . loja , data : estado . data , operador : estado . operador , turno : estado . turno ,
saldo _troco : parseNum ( val ( 'sys-troco' ) ) || 0 ,
saldo _esperado : parseNum ( val ( 'f-saldo-esperado' ) ) || 0 ,
fechamento : parseNum ( val ( 'f-fech-dinheiro' ) ) + parseNum ( val ( 'f-fech-cartoes' ) ) + parseNum ( val ( 'f-fech-receber' ) ) ,
despesas : estado . despesas . map ( r => ( { desc : r . desc || '' , obs : r . obs || '' , valor : r . valor || 0 } ) ) ,
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 || '' } ) ) ,
saldo _esperado : saldoEsp ,
fechamento : fechamentoCounted ,
diferenca : fechamentoCounted - saldoEsp ,
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 ,
cartoes : { pix : cartPix } ,
voucher : cartPix ,
observacoes : estado . observacoes ,
} ;
openPrintPreview ( data ) ;
openPrintPreview ( data , 'confirm' ) ;
} ) ;
// Confirm send
@@ -1532,6 +1656,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 ( ) ;
} ) ;
@@ -1554,105 +1683,157 @@ 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 ) ;
location . reload ( ) ;
// Reseta estado e re-renderiza sem reload
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 data s recentes do Supabase
let datas = [ ] ;
try {
const lista = await window . _ _TAURI _ _ . core . invoke ( 'sb_listar_recentes' , { loja , limite : 20 } ) ;
if ( lista && lista . length > 0 ) {
// Extrai datas únicas e já ordenadas
datas = [ ... new Set ( lista . map ( l => l . data ) . filter ( Boolean ) ) ] . sort ( ) . reverse ( ) ;
}
} catch ( e ) {
console . warn ( 'sb_listar_recentes falhou:' , e ) ;
}
let selectedDate = null ;
if ( datas . length > 0 ) {
// Mostra seletor com as datas disponíveis
const msg = datas . 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 < datas . length ) selectedDate = datas [ idx ] ;
}
}
// Se não escolheu data, usa dia anterior automático
if ( ! selectedDate ) {
const d = new Date ( estado . data ) ; d . setDate ( d . getDate ( ) - 1 ) ;
selectedDate = d . toISOString ( ) . split ( 'T' ) [ 0 ] ;
}
await tauriSaveLocal ( ) ;
setVal ( 'f-data' , selectedDate ) ;
estado . data = selectedDate ;
estado . uuid = crypto . randomUUID ? crypto . randomUUID ( ) : Date . now ( ) . toString ( ) ;
estado . id _local = null ;
syncHeader ( ) ;
// Tenta Supabase primeiro, senão local
let rascunho = null ;
try {
rascunho = await window . _ _TAURI _ _ . core . invoke ( 'sb_carregar_rascunho' , { loja , data : selectedDate } ) ;
} catch ( e ) { console . warn ( 'sb_carregar_rascunho falhou:' , e ) ; }
if ( ! rascunho ) {
// Carrega lista de fechamento s recentes do Supabase com uuid
let recentList = [ ] ;
try {
rascunho = await window . _ _TAURI _ _ . core . invoke ( 'c arregar_rascunho ' , { loja , data : selectedDate } ) ;
} catch ( e ) { console . warn ( 'carregar_rascunho local falhou:' , e ) ; }
}
recentList = await window . _ _TAURI _ _ . core . invoke ( 'sb_list ar_ recentes ' , { loja , limite : 20 } ) ;
console . log ( '[DEBUG] sb_listar_recentes retornou:' , recentList ? . length , 'itens' ) ;
} catch ( e ) {
console . warn ( 'sb_listar_recentes 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 } ) ) ;
// 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 ( ) ;
}
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' , '' ) ;
}
console . log ( '[DEBUG] dateList:' , dateList ) ;
// Marcar que este é um rascunho carregado (não novo)
estado . sync _status = rascunho ? 'loaded' : 'local' ;
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 ] ;
}
}
}
PRE _FILL _TABLES . forEach ( ensureOneRow ) ;
Object . keys ( TABLES ) . forEach ( renderTable ) ;
updateTotals ( ) ;
scheduleSave ( ) ;
} ) ;
// 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 ] ;
}
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 ( ) ;
// 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 } ) ;
console . log ( '[DEBUG] carregar_rascunho local retornou:' , ! ! rascunho ) ;
} catch ( e ) { console . warn ( 'carregar_rascunho local falhou:' , e ) ; }
}
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' ;
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 ( ) ; } ) ;