Compare commits

..

135 Commits

Author SHA1 Message Date
root acb90f093d fix: diferenca calculada sempre a partir da array de cancelamentos (nao do escalar)
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-04 23:34:56 +00:00
root 6eb08b6bd4 fix: cancelamentos — sempre recalcular da array em buildReportHTML/buildPrintHTML/buildConfirmHTML
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-04 14:35:39 +00:00
root 5a0126417c fix: always recalculate diferenca from sys_total (ignore precomputed data.diferenca)
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-04 13:20:54 +00:00
root 733519e1d3 fix: Rust to_escpos — diferença = sys_total - saldo_esperado - cancelamentos (não fechamento)
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-04 03:39:08 +00:00
root 6440b262cc fix: buildReportHTML — diferença = sysTotal - saldoEsp - cancelamentos (não fechamento)
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-04 03:29:11 +00:00
root 420e104df7 fix: buildConfirmHTML e buildPrintHTML usavam sysTotal/cancelamentos em vez de sys_total/total_cancelamentos
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-04 03:21:11 +00:00
root b04e5acfd6 fix: .main min-height:0 (footer invisivel em resolucoes menores); confirm-print mantem modal aberto; openConfirmModal usa estado.loja
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-04 03:06:47 +00:00
root 65c444ad09 fix: seletor loja药业 - forçar valor após init(), esconder label pelo texto
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-03 13:02:32 +00:00
root 2bfbf29ccb fix: values das options de loja sem acento (Uniao/Alianca) — alinhado com LOJA_FIXA do Rust
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-03 12:41:59 +00:00
root cbe2c22440 docs: WebView2Loader.dll requerida em todas as builds
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-02 22:43:08 +00:00
root 1dc278dbbd docs: README completo com Tauri, downloads, stack
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-02 22:28:55 +00:00
root c2c9a29247 docs: adicionar SPEC, SUPABASE_SCHEMA, API_INTEGRATION, BUILDS_FARM
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-02 22:26:57 +00:00
root 659d9779b3 feat: DOBRADO_LOJA env injetado no build — builds separadas Uniao/Alianca/Ambos
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
- app.rs: LOJA_FIXA via option_env!(DOBRADO_LOJA), get_loja_fixa command
- app.rs: set_loja bloqueado se build fixa
- index.html: DOMContentLoaded detecta loja fixa e esconde seletor
- 3 executáveis: fc-uniao.exe / fc-alianca.exe / fc-ambos.exe
2026-07-02 20:53:40 +00:00
root 5c9cb1905b fix: cartoes table 12px sem R$, headers curtos
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-02 20:35:20 +00:00
root a2d3dcc366 fix: openPrintPreview 6px bold titulo 18px
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-02 20:28:59 +00:00
root 12e3d081a7 fix: openPrintPreview 9px bold titulo 20px
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-02 20:23:13 +00:00
root ad967bf0a1 fix: openPrintPreview 12px bold titulo 20px
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-02 20:18:51 +00:00
root d39cc7518b fix: openPrintPreview 15px bold titulo 20px
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-02 20:13:10 +00:00
root 52bec03988 fix: botao Editar na tabela agora pede senha antes de abrir
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-02 20:07:05 +00:00
root 9639048bf7 fix: askPassword aceita config.dynamic_password
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-02 19:38:33 +00:00
root b1585af3f9 fix: fonte 18px bold nos relatorios
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-02 19:31:56 +00:00
root 155782f50a fix: fonte 20px bold nos relatorios
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-02 19:25:09 +00:00
root 16972f4734 fix: fonte 22px bold em todos os relatórios e preview
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-02 19:19:23 +00:00
root d3d5c4fbca fix: fonte grossa 18px bold no preview e impressao
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-02 19:10:42 +00:00
root 5f1d9a4e2f fix: reverte senha btn-anterior + fonte maior no resumo de envio
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-02 18:57:56 +00:00
root cc45ed9df7 fix: btn-anterior senha + fonte impressao maior
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-02 18:48:33 +00:00
root 378a589572 fix: btn-anterior agora exige senha
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-02 18:46:12 +00:00
root 50b0ba4498 fix: modal limpar proprio + remove hint senha dinamica
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-02 15:49:32 +00:00
root 7154901ce3 feat: senha dinamica (dia+mes) + confirm no limpar com try-catch
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-02 15:37:06 +00:00
root 8b24a06496 fix: limpar rascunho - arrays explícitos + Object.keys(TABLES).forEach(renderTable)
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-02 15:27:27 +00:00
root 3058908b37 fix: msg confirmacao limpar rascunho mais clara
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-02 14:56:50 +00:00
root 9b1d45ae50 feat: diferenca OK±4.50 + limpar rascunho completo
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-02 14:44:33 +00:00
root fbed0e57c2 fix: duplicacao saldo caixa — top-level tem prioridade sobre dados JSON
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-02 14:35:13 +00:00
root 362a76961b fix: saldo caixa = soma direta dos campos individuais (troco+credito+debito+alimentacao+vales+areceber+pixcnpj+pixcartao)
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-02 14:15:26 +00:00
root 7a3a4b221b fix: saldo caixa = fechamento_dinheiro+fechamento_cartoes+fechamento_areceber (total sistema)
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-02 14:06:41 +00:00
root 94bb8840ea fix: select completo no sb_listar + busca por periodo (de/ate/turno)
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-02 13:44:08 +00:00
root dba7eb3856 fix: saldo caixa via dados JSON + coluna cancelamento + diferenca correta
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-02 13:31:00 +00:00
root 7b3b7a9f1d feat: modal consulta com busca por data, ultimos 5 dias, coluna diferenca
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-01 20:23:11 +00:00
root 5641de21f8 feat: modal edicao com saldo caixa e diferenca em tempo real + oninput nos campos-chave
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-01 20:10:54 +00:00
root d77ad55fbd feat: modal consulta com busca (data/operador/turno) + filtro 2 dias + data-item nos tr
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-01 19:57:37 +00:00
root 4a5cae8f2f fix: enviar diferenca e total_cartao_pix no PATCH do Supabase
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-01 17:58:01 +00:00
root 313c740470 fix: buildReciboData lê do estado quando DOM vazio (registro anterior) + totaisInfo calcula dos dados reais no modal de edição
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-01 16:21:29 +00:00
root d21cb0a98d fix: tabela consulta saldo completo (pixcartão incluído) + preencher formulário principal ao abrir registro anterior
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-01 15:52:27 +00:00
root 16bee1eacb fix: estado alimentado ao abrir registro anterior (buildReciboData correto) + coluna SALDO CAIXA na tabela de consulta
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-01 15:38:57 +00:00
root c8016f076d fix: dados merge completo (fc,fd,fr,fechamento,saldo_troco,total_*,obs) + fechamento_dinheiro/cartoes/areceber no PATCH
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-01 15:25:11 +00:00
root b1a9f94231 fix: dados objeto (não string) + merge com existing dados parseado
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-01 15:13:29 +00:00
root 42aadcca2c fix: dados JSONB no PATCH, total_cancelamentos, corrige duplicados total_*
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-01 14:50:35 +00:00
root a8319d96a6 fix: parseNum strip 'R$', fmtMR/fmtBR mostram R$ 0,00 para zero
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-01 14:38:32 +00:00
root bf878545b3 fix: denylist移除arrays/objetos — 解决PATCH成功但字段不保存
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-01 14:17:38 +00:00
root 22cda312cf fix: remove whitelist do PATCH — Supabase valida colunas; adiciona edited_at/edited_by no payload
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-01 13:40:37 +00:00
root d112ad061e fix: whitelist de colunas no PATCH — ignora campos inexistentes na tabela; corrige JSON.stringify removido
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-01 13:24:19 +00:00
root be917f8fb7 fix: remove JSON.stringify em updates do sb_atualizar_fechamento — Rust espera objeto JS, não string
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-01 13:02:49 +00:00
root cb48afeb9c fix: abrirEditaFechamento usa modal openEditModal (Tauri context) em vez de popup sem Tauri; _recalcEditTotals atualiza ambos prefixes de IDs
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-01 12:45:30 +00:00
root 2b8ff711ed feat: window.abrirEditaFechamento(full) — abre janela separada para edição completa de fechamento
- Nova função window.abrirEditaFechamento(full) via window.open com HTML inline
  pré-preenchido com todos os campos do registro
- Botão Salvar chama sb_atualizar_fechamento com updates completos
- Botão Cancelar fecha a janela (window.close)
- Coluna 'Editar' adicionada à tabela de dias anteriores com botão ✏️
- Nova helper window.abrirEditaFechamentoIndex(idx) busca registro e abre popup
- Fluxo existente (relatório + modal de edição) mantido intacto
2026-07-01 12:26:34 +00:00
root 9a4ccabd1b fix: ler resposta real do PATCH e logar body enviado/recebido para debug
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-01 12:11:35 +00:00
root 32728eec37 fix: rename_all snake_case no comando sb_atualizar_fechamento (Tauri converte id_fechamento pra idFechamento na interface JS)
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-01 11:53:46 +00:00
root 905ca253d7 fix: PATCH usa id_fechamento (não id/uuid); inclui id_fechamento no updates; Rust sb_atualizar_fechamento usa id_fechamento como chave
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-01 01:46:01 +00:00
root d89b79a3f8 fix: _editingRecord preserves full object; dados.vales etc (not full.vales_arr)
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-01 01:32:11 +00:00
root c4dda7e2e5 debug: add console.log to salvarEdicaoModal to trace where it hangs
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-07-01 01:25:01 +00:00
root 662f3681a5 Revert "fix: fórmula diferença = sys_total - saldo_esperado - cancelamentos (todas as 3 functions de print); aumenta fonte print para 17px + ajustes de padding/margens"
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
This reverts commit a56d7188f2.
2026-06-30 22:26:52 +00:00
root a56d7188f2 fix: fórmula diferença = sys_total - saldo_esperado - cancelamentos (todas as 3 functions de print); aumenta fonte print para 17px + ajustes de padding/margens
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-30 22:03:12 +00:00
root 7e1ee6bc04 fix: diff=sysTotal-esperado; toggleEdit fecha relatorio; saldo=soma carts; print diff=sysTotal
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-30 20:54:02 +00:00
root 6a8881d41d fix: cartoes do dados JSONB no print; listar_recentes com dados; table-layout fixed; modal conf font 14px
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-30 20:00:07 +00:00
root 3b3eb1a4cc fix: fonte impressao 13→15px, cartoes 11→13px, tudo bold !important, bordas 1px dashed→2px solid
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-30 19:30:42 +00:00
root c7109b1017 fix: uuid/id restaurados ao editar dia anterior; calcAll→updateTotals; invoke usa id; senha sempre pedida
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-30 19:00:01 +00:00
root f45d1e8170 fix: somatório atualiza após add linha; saldo caixa só troco editável; printRelatorio fixed; salvamento via uuid; edited_at/by; totales completos
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-30 18:33:38 +00:00
root 8ea5ba771a feat: impressao bold; JSON vales/sangrias/despesas/pixcnpj/receber/cancelamentos; recalc on add row
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-30 18:08:19 +00:00
root 84578fe43d feat: _recalcEditTotals oninput; fmtBR onblur; IDs nos spans de totais
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-30 14:58:42 +00:00
root 3e94789cb0 fix: editTableHTML has local fmtNum; removes all fmtCell calls from openEditModal
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-30 14:45:36 +00:00
root 292d0b85d9 fix: fmtCell inline money formatting (remove fmtMoney ref)
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-30 14:34:23 +00:00
root a8edab029d feat: modal edicao completo com todas as grades editaveis — cartoes, vales, despesas, sangrias, pixcnPJ, receber, cancelamentos
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-30 14:27:01 +00:00
root cbaef8772d fix: askPassword closure — pw declared inside function (was global ref)
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-30 14:06:45 +00:00
root 80740afbf0 fix: parseNum vendas; screen: edit modal separado c/ senha, edited_at/by, fmtBR inputs
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-30 13:57:03 +00:00
root 7134c85bb7 fix: carregar dia anterior - cartoes como numeros crus, NaN makeRow, campos sistema populados
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-30 13:35:13 +00:00
root 4a157d27cb fix: vendas no buildPrintHTML e consultaToPrintData, fmtMoneyInput R$, auto-migration no Rust
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-30 02:52:18 +00:00
root a0f2f91131 fix: IIFE closure no botao Editar caixa completo; feat: campo Vendas em todo lugar
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-30 00:33:58 +00:00
root 15f141c191 feat: botao Editar caixa completo - restaura todos os lancamentos detalhados
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-29 23:30:58 +00:00
root 96a84ec194 fix: parseNum bug, todos campos do caixa editaveis no modal
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-29 13:58:01 +00:00
root 7d0c416248 fix: id do registro em campo oculto, fallback chain no salvar
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-29 12:51:35 +00:00
root 60ec60bf29 fix: sb_atualizar_fechamento aceita id numerico ou UUID string
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-29 12:39:50 +00:00
root 6d9e28a6f2 fix: salvar usa _currentConsultaId (id numerico), nao full.id
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-29 12:06:32 +00:00
root ecb9ce3926 fix: sb_buscar_por_id chama metodo correto (id numerico), tenta id primeiro no openConsultaRegistro
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-28 13:54:45 +00:00
root de5d5aaafb fix: salvar edicao com id numerico, relatorio cartoes detalhado
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-28 13:44:08 +00:00
root a11db54ffc fix: fmtDiff hoisted, idFechamento snake_case
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-28 12:56:30 +00:00
root 674caf2b7a fix: senha admin com asteriscos, imprimir via iframe, relatorio detalhado (80mm)
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-28 12:49:37 +00:00
root bdfd54e0cf fix: corrige uuid→id nas queries do Supabase (coluna inexistente)
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-28 12:36:55 +00:00
root 9c64c06ed9 fix: normaliza loja (sem acento + lowercase) em listar_recentes
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-28 02:55:22 +00:00
root 3cf1864af9 v1.3.31: Dia Anterior — modal consulta com lista do Supabase, relatorio para impressao, edicao com senha admin (saldo_esperado + observacoes)
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-28 02:27:42 +00:00
root aa147885a8 v1.3.30: diferença tela lançamento usa sysTotal (troco+credito+debito+alimentacao+vales+receber+pix+pixcnpj) - saldo_esperado - cancelamentos
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-28 01:52:50 +00:00
root 00fa17fa52 v1.3.29: ignora HTTP 409 duplicate (ja existe = ok)
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-27 23:54:07 +00:00
root 5e6f8b09a9 v1.3.28: diferença = fechamento - saldo_esperado - cancelamentos (正确 formula); clientes/frango no ESC/POS
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-27 23:26:01 +00:00
root 9ac4eb7042 v1.3.27: buildEstadoForBackend envia TODOS totais pro Postgres (colunas SQL)
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-26 21:55:44 +00:00
root 2b8fa98ed7 v1.3.26: sem_acento também no turno (manhã→manha)
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-26 21:42:43 +00:00
root 77e927fef0 v1.3.25: add sem_acento() - remove acentos de loja antes de enviar (União→uniao)
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-26 19:22:10 +00:00
root 3f6b2ae6e4 v1.3.23: embed SUPABASE_ANON_KEY e SERVICE_KEY no binario (fallback) - resolve 401 em qualquer PC
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-26 16:45:36 +00:00
root 35dcd27dda v1.3.23: Rust salvar_fechamento envia TODAS colunas numeric + clientes/frango; ADD COLUMN clientes/frango no Postgres
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-26 15:43:30 +00:00
root bd867e8419 v1.3.21: PDF/envio - CONTADORES no final do buildConfirmHTML
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-26 14:41:43 +00:00
root da861ef964 v1.3.20: Diferença tela = SaldoCaixa(Sistema) - SaldoEsperado - Cancelamentos; CONTADORES no final do Recibo 80mm
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-26 14:32:25 +00:00
root 8e642f2e0c v1.3.18: SaldoCaixa(Sistema) agora inclui TROCO. Diferença = SaldoCaixa(Sistema) - SaldoEsperado - Cancelamentos
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-26 14:02:41 +00:00
root 7473db6b1f Revert "v1.3.16: Diferença=SaldoCaixa(Sistema)-SaldoEsperado-Cancelamentos; nomes padronizados"
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
This reverts commit 13489d58cf.
2026-06-26 13:55:23 +00:00
root 13489d58cf v1.3.16: Diferença=SaldoCaixa(Sistema)-SaldoEsperado-Cancelamentos; nomes padronizados
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-26 13:52:34 +00:00
root 5ad8c1f395 v1.3.15: diferença=fechamento-saldo_esperado-cancelamentos; ESCPOS 80mm+HTML com clientes+frango
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-26 13:33:28 +00:00
root 103cd31e50 v1.3.14b: campos Clientes e Frango Assado no formulário, reports e modal
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-26 13:21:02 +00:00
root 06783de4e2 v1.3.14: dia anterior com modal+senha, editing mode PATCH, sys_total sem troco, fórmulas corrigidas, reportHTML
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-26 12:57:39 +00:00
root 11abac8daf v1.3.13: fórmulas corrigidas — diferença=(fechamento-(saldo_esp-cancel)), sys_total sem troco, reportHTML separado
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-26 12:45:46 +00:00
root fea957dfb9 v1.3.12: dia anterior robusto — hasTauri check em todas as chamadas
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-26 03:44:53 +00:00
root 130b2b6da7 v1.3.11: 3 bugs corrigidos — preview sempre abre, tabela cartoes com traços, variaveis ausentes
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-26 03:40:48 +00:00
root b47d7766ad v1.3.10: relatório completo idêntico ao PDF — todas seções, tabela cartões 4 colunas, saldo sistema, fechamento contado, diff sys_total
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-26 03:24:27 +00:00
root 78fa778197 v1.3.9: dif ESC/POS usa self.diferenca (sys_total - saldo_esperado)
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-26 03:11:16 +00:00
root 5e4340adca v1.3.7: só nomenclatura — Fechamento de Caixa / Saldo de Caixa
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-26 02:57:34 +00:00
root c01133f39c v1.3.6: nomenclatura trocada; relatórios com todos os lançamentos detalhados por seção
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-26 02:52:08 +00:00
root a6b295a6d0 v1.3.5: to_escpos completo — totais por seção, diferenca via self.diferenca; confirm antes de dia anterior; sem senha
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-26 02:41:37 +00:00
root f850831e98 fix: buildPrintHTML usa data.diferenca (sys_total - saldo_esperado); btn-anterior null-check robusto
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-26 02:23:57 +00:00
root e70f31e00f debug: btn-anterior logs; cartoes.norm guard
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-26 01:44:15 +00:00
root 79e1dbd645 fix: capabilities Tauri 2 dialog; imprimir_recibo {data}; cartoes.map guard; debug logs
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-25 17:44:22 +00:00
root cce8f3df60 fix: newEstado is not defined — reset estado inline; clear form fields on limpar
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-25 17:30:07 +00:00
root 318d44e934 fix: modal confirm usa sysTotal pra diferenca, nao so sys-troco
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-25 16:38:26 +00:00
root bfbcd89be9 fix: diferenca usa sys_total; novo layout 80mm sem ENTRADAS; confirm modal
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-25 14:32:05 +00:00
root 1848c1aa35 fix: diferenca no recibo - TOTAL agora usa total_operado, diferenca = fechamento - saldo_esperado
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
- Adicionado campo total_operado (credito+debito+alimentacao+voucher+pixcnpj)
- WHITE section: Fechamento mostra total_operado (era fechamento contado)
- BLACK section: diferenca usa fechamento - saldo_esperado (nao mais self.diferenca)
- Confirm modal: diferenca usava sysTroco - saldoEsp (errado), corrigido
- buildPrintHTML: diferenca usa total_operado
2026-06-25 13:22:03 +00:00
root 9176b764fd feat: alinha recibo ESC/POS com formato do screenshot
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
ReciboData agora recebe arrays (sangrias_arr, despesas_arr, etc.)
to_escpos() gera: SANGRIAS → ENTRADAS → DESPESAS → PIX CNPJ → VALES → A RECEBER → CARTÕES → CANCELAMENTOS → TROCO → OBSERVAÇÕES → TOTAL SALDO CAIXA → SALDO ESPERADO → DIFERENÇA

Preview impressao: modal com iframe 360x600px + botao Fechar + botao Imprimir — usuario consegue voltar ao app
2026-06-24 18:31:03 +00:00
root 5cab38a27c chore: clean Rust warnings and remove unused deps
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
- Remove serde_yaml and tokio from Cargo.toml (unused)
- Add #[allow(dead_code)] to utility code kept for future use
  (CUT_FULL, ALIGN_RIGHT, line(), PrinterError, SupabaseError,
   atualizar_sync, remover, Lock variant)
- Zero compiler warnings on release build
2026-06-24 14:51:56 +00:00
root 5b1f3a2691 chore: adiciona README, .gitignore, remove binarios do repo
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
- Adiciona .gitignore com *.exe, *.tar.gz, *.dll, *.log
- Remove binários commitados do tracking (58MB节省)
- Adiciona README.md com instruções de setup, estrutura, comandos Tauri
- Substitui location.reload() na impressão por iframe (preserva estado)
- Substitui location.reload() no btn-limpar por reset de estado in-memory
- Nota: security — service_role_key ainda no cliente desktop
2026-06-24 14:30:26 +00:00
root 7e57d30863 v1.2.7: conserta 4 bugs — impressao, enviar, dia anterior, localStorage
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-24 13:38:00 +00:00
root 7a93a30c22 v1.2.6: init() restaura operador+turno; tar inclui WebView2Loader.dll
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-24 13:11:43 +00:00
root 230304e878 v1.2.5 — fixes: diferenca=fechamento-esperado; btn-enviar func; mapas tabela Supabase
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
MAPA SUPABASE:
Tabela: fechamentos_web
  uuid | loja | data | operador | turno
  saldo_troco | saldo_esperado | fechamento | diferenca
  sync_status | observacoes | dados (JSON com tudo)
  atualizado_em

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

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

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

BOTÃO DIA ANTERIOR: usa sb_listar_recentes → tabela fechamentos_web
  Seleciona por numero (1=N Mais Recente, 2=2o Mais Recente...)
  ou automatico: data-1dia
2026-06-24 01:32:18 +00:00
root d185f82bd5 Revert "v1.2.3: btn voltar no preview; relatorio corrigido (fechamento-esperado); cartoes com detalhes p/ Supabase"
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
This reverts commit 2253d45d00.
2026-06-24 01:18:57 +00:00
root 2253d45d00 v1.2.3: btn voltar no preview; relatorio corrigido (fechamento-esperado); cartoes com detalhes p/ Supabase
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-24 01:10:43 +00:00
root 60e15dbed3 fix impressão: window.print() in-app em vez de window.open(); alert com tabela destino no envio
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-23 18:28:58 +00:00
root 53767df80c preview+print: janela preview 80mm abre no navegador; btn-recibo e confirm-print abrem preview; ESC/POS fallback automático
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-23 17:07:54 +00:00
root 208eede526 v1.2.0 — campos texto livre vs datalist; diferença abaixo saldo esperado + badge OK/sobra; btn-anterior com datas do Supabase; ESC/POS copy fallback; modal com PIX+diferença+print; debug Supabase
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-23 16:46:53 +00:00
root fa1e973f8c fix: remove AsRawFd import (unix-only, não existe no Windows)
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-23 16:13:06 +00:00
root d623d11e23 fix: 5 problemas recorrentes
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
1. PIX CNPJ: linha separada no Saldo de Caixa + incluído na soma sys-total
2. Casas decimais: fmtNum sempre 2 casas (padrão dinheiro)
3. btn-anterior: agora busca do Supabase via sb_carregar_rascunho e repovoa todas as tabelas
4. Impressão: reimplementado usb_windows (WriteFile no Windows); adicionado modo teste (grava .bin)
5. Supabase: feedback de erro com console.error; storage salvar_agora persiste TODAS as tabelas (sangrias, pixcnpj, vales, cancelamentos)
2026-06-23 14:22:06 +00:00
root f13b059ce7 fix: header sticky; makeRow formats valor cells with fmtNum; fmtNum shows .00 only when needed
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-23 12:56:57 +00:00
root 8660ac4755 fix: fmtNum shows .00 only when needed; Diferença=SaldoCaixa-SaldoEsperado; sys-total already includes pixCnpj
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-23 12:55:30 +00:00
root 72c534a813 fix: parseNum receives numbers not strings (was treating JS numbers' dot as thousand sep); updateTotals/confirm-summary use parseNum consistently
Build Fechamento de Caixa / build (macos-14, app) (push) Has been cancelled
Build Fechamento de Caixa / build (ubuntu-22.04, deb) (push) Has been cancelled
Build Fechamento de Caixa / build (windows-2022, msi) (push) Has been cancelled
2026-06-23 01:51:37 +00:00
16 changed files with 3982 additions and 292 deletions
+13
View File
@@ -7,3 +7,16 @@ src-tauri/gen/
*.msi
*.dmg
.DS_Store
# Binarios compilados
*.exe
*.tar.gz
*.zip
fechamento-caixa.exe
fechamento-caixa-latest.tar.gz
WebView2Loader.dll
src-tauri/target/x86_64-pc-windows-gnu/release/*.exe
src-tauri/target/x86_64-pc-windows-gnu/release/*.dll
# Logs
*.log
+139
View File
@@ -0,0 +1,139 @@
# Integração Frontend ↔ Tauri (Rust) ↔ Supabase
## Visão geral da comunicação
```
┌─────────────────┐ ┌─────────────────┐ ┌──────────────────┐
│ JavaScript │ invoke │ Tauri/Rust │ HTTP │ Supabase │
│ (index.html) │────────▶│ commands │────────▶│ (cloud) │
│ │◀────────│ │◀────────│ │
└─────────────────┘ response└─────────────────┘ response└──────────────────┘
```
O frontend **nunca faz requisições HTTP direto** para o Supabase. Toda comunicação passa pelos comandos Tauri (Rust).
---
## Comandos Tauri disponíveis
### App
| Comando | O que faz |
|---------|-----------|
| `get_loja` | Retorna a loja configurada |
| `get_loja_fixa` | Retorna `"Uniao"`, `"Alianca"` ou `null` (se build fixa) |
| `set_loja(loja)` | Define a loja (bloqueado se build fixa) |
| `get_config` | Retorna config completo `{ loja, pdv_nome, operador_default, ... }` |
| `set_config(cfg)` | Salva config |
### Storage (SQLite local)
| Comando | O que faz |
|---------|-----------|
| `salvar_fechamento(estado)` | Salva estado completo no SQLite local |
| `carregar_rascunho(loja, data)` | Retorna último rascunho para loja+data |
| `listar_fechamentos(loja, limite)` | Lista fechamentos de uma loja |
| `buscar_fechamento_por_id(id)` | Busca por ID local |
| `pendentes_sync` | Retorna fechamentos que ainda não foram syncados |
### Supabase
| Comando | O que faz |
|---------|-----------|
| `sb_salvar_fechamento(estado)` | Upsert no Supabase via service_role JWT |
| `sb_carregar_rascunho(loja, data)` | Busca rascunho mais recente |
| `sb_listar_recentes(loja, limite)` | Lista fechamentos recentes |
| `sb_buscar_por_id(id)` | Busca por ID |
| `sb_buscar_por_uuid(uuid)` | Busca por UUID |
| `sb_buscar_por_id_fechamento(id_fechamento)` | Busca por chave natural |
| `sb_atualizar_fechamento(id_fechamento, updates)` | PATCH campos específicos |
| `sb_salvar_listas(loja, listas)` | Salva operadores/gerentes customizados |
| `sb_carregar_listas(loja)` | Carrega listas salvas |
| `sb_online` | Retorna `true`/`false` se Supabase está acessível |
### Printer
| Comando | O que faz |
|---------|-----------|
| `detectar_impressora` | Detecta EPSON TM-T20 USB |
| `configurar_impressora(caminho)` | Define caminho da impressora |
| `caminho_impressora` | Retorna caminho configurado |
| `imprimir_recibo(html)` | Envia HTML puro para impressora |
| `teste_impressora` | Imprime página de teste |
| `ativar_modo_teste_impressao` | Ativa modo teste (salva PDF em vez de imprimir) |
---
## Fluxo de salvar um fechamento
```javascript
// No frontend (index.html), quando operador clica "Salvar":
async function salvarFechamento() {
const estado = buildEstado(); // coleta todos os campos do formulário
// 1. Salva localmente (sempre funciona offline)
await invoke('salvar_fechamento', { estado });
// 2. Tenta sync com Supabase
try {
await invoke('sb_salvar_fechamento', { estado });
toast('✅ Salvo e sincronizado');
} catch(e) {
// Supabase offline — fica pendente no SQLite
toast('💾 Salvo localmente (sync pendente)');
}
}
```
---
## Como o Rust conversa com o Supabase
Em `supabase.rs`, o plugin usa `reqwest` (HTTP client) com:
```rust
fn headers(&self, use_service: bool) -> reqwest::header::HeaderMap {
let key = if use_service { &self.service_role_key } else { &self.anon_key };
let mut headers = reqwest::header::HeaderMap::new();
headers.insert(
reqwest::header::AUTHORIZATION,
format!("Bearer {}", key).parse().unwrap(),
);
headers
}
```
- **`use_service = true`:** para INSERT/UPDATE (salvar fechamento)
- **`use_service = false`:** para GET (listar, buscar)
---
## Upsert — salvar sem duplicar
O Supabase não tem UPSERT nativo via REST. O plugin usa um truque:
```http
POST /fechamentos_web HTTP/1.1
Prefer: resolution=merge-duplicates
```
Se o `id_fechamento` já existe, o PostgREST faz um UPDATE em vez de INSERT. Se não existe, faz INSERT.
---
## UUID vs id_fechamento
| Campo | O que é | Quem gera |
|-------|---------|-----------|
| `id` | ID sequencial do PostgreSQL | Supabase (auto) |
| `uuid` | UUID único do registro local | Frontend JS (`crypto.randomUUID()`) |
| `id_fechamento` | Chave natural: `{loja}_{data}_{operador}` | Frontend JS (construído na hora) |
O `id_fechamento` é a chave de negócio. O `uuid` é para rastrear registros entre local e cloud.
---
## Offline first — como funciona
1. **Operador/edita**`salvar_fechamento` → SQLite (sempre funciona)
2. **Operador/edita**`sb_salvar_fechamento` → Supabase (só se online)
3. **Se offline:** o fechamento fica no SQLite com `sync_status = "local"`
4. **Quando conecta:** `pendentes_sync` retorna os pendentes → sync automático
O sync automático é controlado por `sync_automatico` na config da app.
+209
View File
@@ -0,0 +1,209 @@
# Builds — Como gerar fc-uniao.exe, fc-alianca.exe, fc-ambos.exe
## Conceito
O mesmo código gera 3 binários diferentes. A diferença entre eles é apenas o valor da variável de ambiente `DOBRADO_LOJA` passado na hora da compilação Rust.
- **Build fixa (União ou Aliança):** o operador **não consegue trocar a loja** no formulário
- **Build livre (Ambós):** o operador escolhe entre União e Aliança no dropdown
---
## Mecanismo: `option_env!` em Rust
Em `src-tauri/src/plugins/app.rs`:
```rust
/// DOBRADO_LOJA é uma variável de ambiente lida em TEMPO DE COMPILAÇÃO.
/// Se não definida, retorna None.
pub const LOJA_FIXA: Option<&'static str> = option_env!("DOBRADO_LOJA");
```
O `option_env!` é uma macro built-in do Rust que avalia expressões const em tempo de compilação. Se `DOBRADO_LOJA` estiver definida como env var, `LOJA_FIXA` contém o valor. Se não, `None`.
### Por que compile-time?
Se fosse runtime (`std::env::var`), o valor estaría hardcoded no binário da mesma forma, mas compile-time é mais limpo porque o Rust elimina código morto (dead code elimination). Se `LOJA_FIXA` é `None`, o Rust pode optimizar o código de "loja fixa" para fora do binário final.
---
## Comportamento por build
| Build | `DOBRADO_LOJA` | Seletor loja | `set_loja` | `get_loja_fixa` |
|-------|----------------|-------------|-------------|-----------------|
| União | `Uniao` | **Escondido** | Bloqueado com erro | `"Uniao"` |
| Aliança | `Alianca` | **Escondido** | Bloqueado com erro | `"Alianca"` |
| Ambos | _(não definido)_ | Visível + funcional | Permitido | `null` |
---
## Como buildar (Linux → Windows cross-compile)
### Pré-requisitos
```bash
# Instalar target cross-compile
rustup target add x86_64-pc-windows-gnu
# Ou via apt (Ubuntu/Debian)
sudo apt install mingw-w64
```
### Comandos
```bash
cd src-tauri/
# ── UNIÃO ─────────────────────────────────────────────
DOBRADO_LOJA=Uniao \
cargo build --release --target x86_64-pc-windows-gnu
# Binário gerado:
# src-tauri/target/x86_64-pc-windows-gnu/release/fechamento-caixa.exe
# ── ALIANÇA ──────────────────────────────────────────
DOBRADO_LOJA=Alianca \
cargo build --release --target x86_64-pc-windows-gnu
# ── AMBOS ────────────────────────────────────────────
cargo build --release --target x86_64-pc-windows-gnu
# (sem DOBRADO_LOJA → loja livre)
```
### Local do binário
```
src-tauri/target/x86_64-pc-windows-gnu/release/fechamento-caixa.exe
```
Renomear para `fc-uniao.exe`, `fc-alianca.exe`, `fc-ambos.exe` antes de distribuir.
### ⚠️ Distribuição: WebView2Loader.dll
O binário **precisa da `WebView2Loader.dll` na mesma pasta** para funcionar. Esta DLL é o loader do WebView2 (o navegador embutido do Tauri).
```
Pasta de distribuição (cada build):
├── fc-uniao.exe
├── WebView2Loader.dll ← copiar junto!
```
A DLL está em:
```
src-tauri/target/x86_64-pc-windows-gnu/release/WebView2Loader.dll
```
Para copiar junto ao buildar:
```bash
DLL="src-tauri/target/x86_64-pc-windows-gnu/release/WebView2Loader.dll"
cp ${DLL} /tmp/fc-uniao/
cp ${DLL} /tmp/fc-alianca/
cp ${DLL} /tmp/fc-ambos/
```
---
## O que acontece no frontend quando a loja é fixa
Em `src/index.html`, `DOMContentLoaded`:
```javascript
document.addEventListener('DOMContentLoaded', async () => {
try {
const fixa = await window.__TAURI__.core.invoke('get_loja_fixa');
if (fixa) {
// Build fixa: esconde o <select id="f-loja">
const sel = document.getElementById('f-loja');
sel.style.display = 'none';
sel.disabled = true;
// Esconde o label "Loja"
const label = document.querySelector('.meta-label');
if (label && label.textContent.trim() === 'Loja') {
label.style.display = 'none';
}
estado.loja = fixa; // define sem precisar do select
}
} catch(e) {
console.warn('get_loja_fixa não disponível:', e);
}
// ... resto do init
});
```
---
## O que acontece no Rust quando a loja é fixa
```rust
// set_loja retorna erro se a build tem loja fixa
#[tauri::command]
pub fn set_loja(state: State<'_, AppState>, loja: String) -> Result<(), String> {
if LOJA_FIXA.is_some() {
return Err("Loja fixa em tempo de build — não é possível trocar.".to_string());
}
state.config.lock().unwrap().loja = loja;
state.save()
}
```
---
## Testar localmente (dev mode)
```bash
cd src-tauri/
# Com loja fixa (dev)
DOBRADO_LOJA=Uniao cargo tauri dev
# Com loja livre (dev)
cargo tauri dev
```
---
## Criar release no Gitea
```bash
# 1. Buildar as 3 versões e copiar para /tmp/
cp src-tauri/target/x86_64-pc-windows-gnu/release/fechamento-caixa.exe /tmp/fc-uniao.exe
cp src-tauri/target/x86_64-pc-windows-gnu/release/fechamento-caixa.exe /tmp/fc-alianca.exe
cp src-tauri/target/x86_64-pc-windows-gnu/release/fechamento-caixa.exe /tmp/fc-ambos.exe
# 2. Criar releases via Gitea API
GITEA_TOKEN="seu_token_aqui"
REPO="filipe/fechamento-caixa"
for build in uniao alianca ambos; do
TAG="v2.0.0-${build}"
FILE="/tmp/fc-${build}.exe"
# Criar release
RESP=$(curl -s -H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"tag_name\":\"${TAG}\",\"name\":\"${TAG}\",\"body\":\"Build ${build}\"}" \
"https://git.ofrangao.com.br/api/v1/repos/${REPO}/releases")
ID=$(echo $RESP | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
# Upload asset
curl -s -H "Authorization: token ${GITEA_TOKEN}" \
-H "Content-Type: application/octet-stream" \
--data-binary @${FILE} \
"https://git.ofrangao.com.br/api/v1/repos/${REPO}/releases/${ID}/assets?name=fc-${build}.exe"
done
```
---
## Arquivos que mudam entre builds
**Nenhum arquivo fonte muda.** O binário é identico exceto pelo valor de `LOJA_FIXA` hardcoded.
Para verificar o valor hardcoded no binário:
```bash
strings fc-uniao.exe | grep -E "Uniao|Alianca"
```
O binário `fc-uniao.exe` contém a string `Uniao` no lugar de `LOJA_FIXA`, enquanto `fc-ambos.exe` não contém nada (ou contém `None` ou não tem referência).
+192
View File
@@ -0,0 +1,192 @@
# Fechamento de Caixa — O Frangão
## O que é
App desktop nativo para registrar o fechamento de caixa diário das lojas **União** e **Aliança** do O Frangão. Cada fechamento registra: saldo em dinheiro, cartões de crédito/débito/alimentação/PIX, vales, sangrias, despesas, PIX CNPJ, e itens a receber.
## Ideia central
O app é um **formulário inteligente** que:
1. Captura todos os dados de fechamento de caixa (dinheiro, cartões, despesas, etc.)
2. Salva localmente em **SQLite** (funciona offline)
3. Envia automaticamente para o **Supabase** (cloud) quando há conexão
4. Gera um **recibo térmico 80mm** para impressão
O objetivo é substituir planilhas manuais e webhooks frágeis, dando uma fonte única de verdade para os dados de caixa.
---
## Arquitetura geral
```
┌─────────────────┐ ┌──────────────────┐ ┌────────────────────┐
│ HTML/JS │────▶│ Tauri (Rust) │────▶│ SQLite (local) │
│ (frontend) │◀────│ plugins: │◀────│ fechamentos.db │
│ │ │ app, storage, │ │ - funciona offline │
│ - formulário │ │ supabase, │ │ - sync automático │
│ - impressão │ │ printer │ └────────────────────┘
│ - estado JS │ └────────┬─────────┘ │
└─────────────────┘ │ ▼
│ ┌────────────────────┐
│ │ Supabase Cloud │
│ │ (supabase. │
└─────────────▶│ ofrangao.com.br) │
│ - PostgreSQL │
│ - RLS + JWT auth │
│ - API REST │
└────────────────────┘
```
---
## Stack técnica
| Camada | Tecnologia |
|--------|-----------|
| Frontend | HTML5 + CSS + JavaScript vanilla (single-file `src/index.html`) |
| Runtime | [Tauri 2](https://tauri.app/) (Rust + WebView2) |
| Dados local | SQLite via `rusqlite` |
| Dados cloud | [Supabase](https://supabase.com/) — PostgreSQL + REST API |
| Impressão | HTML → `window.print()` com CSS @media print (80mm) |
| Build | Cross-compile Linux → Windows (`x86_64-pc-windows-gnu`) |
---
## Repositório
```
https://git.ofrangao.com.br/filipe/fechamento-caixa
```
### Estrutura de arquivos
```
fechamento-caixa/
├── src/
│ └── index.html ← Frontend completo (HTML + CSS + JS)
├── src-tauri/
│ ├── src/
│ │ ├── main.rs ← Entry point + plugin initialization
│ │ └── plugins/
│ │ ├── app.rs ← Config da app (loja, PDV, sync)
│ │ ├── storage.rs ← SQLite local
│ │ ├── supabase.rs ← Sync com Supabase
│ │ ├── printer.rs ← Impressão
│ │ └── escpos.rs ← Helpers ESCPOS
│ └── tauri.conf.json ← Config Tauri (janela, bundle, etc.)
├── SPEC.md ← Este arquivo
├── SUPABASE_SCHEMA.md ← Schema do banco de dados
├── API_INTEGRATION.md ← Como o frontend se comunica com Supabase
└── BUILS_FARM.md ← Como gerar fc-uniao.exe, fc-alianca.exe, fc-ambos.exe
```
---
## Builds — 3 versões do app
O mesmo código gera 3 binários diferentes conforme a variável de ambiente `DOBRADO_LOJA`:
## Downloads
| Binário | Loja | Download |
|---------|------|----------|
| `fc-uniao.exe` | **Travada: UNIÃO** | [Baixar](https://git.ofrangao.com.br/filipe/fechamento-caixa/releases/download/v2.0.0-uniao/fc-uniao.exe) |
| `fc-alianca.exe` | **Travada: ALIANÇA** | [Baixar](https://git.ofrangao.com.br/filipe/fechamento-caixa/releases/download/v2.0.0-alianca/fc-alianca.exe) |
| `fc-ambos.exe` | **Seletor livre** | [Baixar](https://git.ofrangao.com.br/filipe/fechamento-caixa/releases/download/v2.0.0-ambos/fc-ambos.exe) |
Quando a loja é fixa, o seletor de loja no formulário é **escondido via JavaScript** e tentativas de mudar via API retornam erro.
---
## Fluxo de um fechamento
```
1. Operador abre o app
2. Seleciona Data, Operador, Turno, Loja (se não for fixa)
3. Preenche os campos:
- Saldo de Troy
- Cartões (crédito, débito, alimentação, PIX) — um valor por vez
- Despesas (descrição + valor)
- Sangrias (hora + gerente + valor)
- PIX CNPJ (nome + valor)
- Vales (nome + observação + valor)
- A Receber (nome + valor)
- Cancelamentos (número + motivo + valor)
4. App calcula:
TOTAL SISTEMA = soma de todos os campos acima
DIFERENÇA = TOTAL SISTEMA Saldo Esperado Cancelamentos
5. Operador clica "💾 Salvar / Sincronizar"
→ Salva em SQLite local
→ Envia para Supabase (se online)
6. Operador clica "🖨️ Imprimir"
→ Abre preview do recibo 80mm
→ window.print() para impressora térmica
```
---
## Tauri — o runtime desktop
O app roda como **Tauri 2**, um runtime que embute um navegador WebView2 (Windows) dentro de um processo Rust.
### O que o Tauri fornece
| Recurso | Como é usado |
|---------|-------------|
| **Janela nativa** | A janela do app com barra de título, controls de redimensionar, ícone |
| **Sistema de arquivos** | Lê/escreve `config.json` e `fechamento.db` em `%LOCALAPPDATA%/FechamentoCaixa/` |
| **Invocação de comandos Rust** | Frontend JS chama `invoke('nome_comando', args)` → Rust executa e retorna |
| **HTTP client** | Plugin `supabase.rs` usa `reqwest` para falar com o Supabase |
| **Shell** | Abre diálogo de impressoras, executa `cmd /C copy` para impressão |
| **Diálogos nativos** | `tauri_plugin_dialog` para escolher arquivos, pastas, impressora |
### Comandos Tauri (Rust → JS)
O frontend **nunca acessa** Supabase, SQLite ou impressora diretamente. Tudo passa por `invoke`:
```javascript
// Salvar fechamento (SQLite + Supabase)
await invoke('salvar_fechamento', { estado });
// Sync com Supabase
await invoke('sb_salvar_fechamento', { estado });
// Detectar impressora
await invoke('detectar_impressora');
// Imprimir
await invoke('imprimir_recibo', { html: reciboHTML });
```
### Estrutura dos plugins Rust
```
src-tauri/src/plugins/
├── app.rs — Config da app (loja, operador default, sync)
├── storage.rs — SQLite local (fonte primária offline)
├── supabase.rs — Cliente HTTP para Supabase (service_role JWT)
├── printer.rs — Detecção EPSON TM-T20, fallback para window.print()
└── escpos.rs — Helpers ESCPOS (alinhamento, negrito, corte)
```
### Onde os dados ficam
| Dado | Local |
|------|-------|
| Config (SUPABASE keys, loja default) | `%LOCALAPPDATA%/FechamentoCaixa/config.json` |
| Banco SQLite (fechamentos) | `%LOCALAPPDATA%/FechamentoCaixa/fechamento.db` |
| Rascunho auto-save | `%LOCALAPPDATA%/FechamentoCaixa/fechamento_rascunho.db` |
---
## Autores & Contexto
- **Desenvolvedor:** Filipe.Tavares
- **Dono das lojas:** O Frangão — União e Aliança
- **Objetivo:** Substituir planilhas manuais e webhooks n8n por um sistema de fechamento de caixa confiável e offline-first
+142
View File
@@ -0,0 +1,142 @@
# Schema do Supabase — Fechamento de Caixa
## Servidor
- **URL:** `https://supabase.ofrangao.com.br`
- **Instância:** O Frangão — PostgreSQL hospedado
---
## Tabela principal: `fechamentos_web`
```sql
CREATE TABLE public.fechamentos_web (
id SERIAL PRIMARY KEY,
id_fechamento TEXT UNIQUE NOT NULL, -- chave natural: {loja}_{data}_{operador}
loja TEXT NOT NULL, -- "Uniao" | "Alianca"
data DATE NOT NULL, -- data do fechamento (YYYY-MM-DD)
operador TEXT NOT NULL, -- nome do operador
turno TEXT NOT NULL, -- "manha" | "tarde"
observacoes TEXT DEFAULT 'rascunho', -- "rascunho" | "finalizado"
-- Totais de fechamento (campos escalares)
fechamento_dinheiro REAL DEFAULT 0, -- dinheiro no caixa
fechamento_cartoes REAL DEFAULT 0, -- total cartões
fechamento_receber REAL DEFAULT 0, -- a receber
fechamento REAL DEFAULT 0, -- TOTAL: dinheiro + cartões + receber
-- Totais do sistema (valores lidos do sistema PAF-ECF)
sys_total REAL DEFAULT 0, -- soma: troco+crédito+débito+alimentação+vales+receber+pixCartao+pixCnpj
saldo_esperado REAL DEFAULT 0, -- quanto deveria ter (informado pelo operador)
-- Controle
criado_em TIMESTAMPTZ DEFAULT NOW(),
atualizado_em TIMESTAMPTZ DEFAULT NOW(),
enviado_por TEXT,
ip_origem TEXT,
user_agent TEXT,
-- Dados variáveis em formato livre (JSONB)
dados JSONB DEFAULT '{}'::jsonb
);
```
### Comentários sobre colunas
| Coluna | O que guarda |
|--------|-------------|
| `id_fechamento` | Chave natural única. Ex: `uniao_2026-06-19_filipe`. Formato: `{loja}_{data}_{operador}` em minúsculas, espaços → `_` |
| `sys_total` | Total calculado pelo sistema — é a soma de **todos** os campos de cartão + vales + despesas + sangrias + etc |
| `saldo_esperado` | Valor informado pelo operador — o quanto ele espera ter em caixa |
| `dados` | JSONB com todos os arrays: `cartoes_credito[]`, `cartoes_debito[]`, `cartoes_alimentacao[]`, `cartoes_pix[]`, `despesas[]`, `sangrias[]`, `pixcnpj[]`, `vales[]`, `receber[]`, `cancelamentos[]` |
| `observacoes` | `rascunho` enquanto.edita, `finalizado` quando o operador confirma |
---
## Fórmula de diferença
```
DIFERENÇA = sys_total saldo_esperado total_cancelamentos
```
Se `DIFERENÇA = 0` → ✅ OK (caixa fecha perfeito)
Se `DIFERENÇA > 0` → ⚠️ Sobrou dinheiro
Se `DIFERENÇA < 0` → 🔴 Faltou dinheiro
---
## Colunas dentro do JSONB `dados`
```json
{
"cartoes_credito": [152.00, 123.50, ...],
"cartoes_debito": [15.45, 20.00, ...],
"cartoes_alimentacao": [12.55, ...],
"cartoes_pix": [112.50, ...],
"despesas": [{"desc": "luz", "valor": 150.00}, ...],
"sangrias": [{"hora": "14:30", "gerente": "João", "retirada": 50.00, "valor": 50.00}, ...],
"pixcnpj": [{"nome": "João Silva", "valor": 80.00}, ...],
"vales": [{"nome": "Maria", "obs": "vale transporte", "valor": 30.00}, ...],
"receber": [{"nome": "Pedro", "valor": 45.00}, ...],
"cancelamentos": [{"numero": "001", "motivo": "erro digitação", "valor": 25.00}, ...]
}
```
---
## Row Level Security (RLS)
A tabela usa RLS com duas políticas:
1. **Anon (chave pública):** pode ler registros
2. **Service role:** pode ler + escrever registros (usado pelo app com JWT service_role)
### Chave anon (pública)
Usada para consultasreadonly pelo frontend.
### Chave service_role
Usada pelo app Tauri para INSERT/UPDATE via plugin `supabase.rs`. Esta chave **nunca deve ser exposta publicamente** — está embedada no binário compilado.
---
## Endpoints da API REST
Base: `https://supabase.ofrangao.com.br/rest/v1/`
| Método | Endpoint | Uso |
|--------|----------|-----|
| `GET` | `/fechamentos_web?loja=eq.uniao&data=eq.2026-06-19&select=*` | Lista fechamentos |
| `GET` | `/fechamentos_web?id_fechamento=eq.uniao_2026-06-19_filipe` | Busca por chave |
| `POST` | `/fechamentos_web` | Cria/atualiza (upsert via `Prefer: resolution=merge-duplicates`) |
| `PATCH` | `/fechamentos_web?id_fechamento=eq.uniao_2026-06-19_filipe` | Atualiza campos |
---
## Header de autenticação
```http
Authorization: Bearer {SUPABASE_SERVICE_ROLE_JWT}
apikey: {SUPABASE_ANON_KEY}
Content-Type: application/json
Prefer: resolution=merge-duplicates (para upsert no POST)
```
---
## Supabase — Configuração do projeto
- **Projeto:** O Frangão
- **Host:** `https://supabase.ofrangao.com.br`
- **Banco:** PostgreSQL 15+
- **API:** REST via PostgREST
- **Auth:** JWT (anon key + service role key)
O app busca a service_role key via `config.json` local (em `%LOCALAPPDATA%/FechamentoCaixa/config.json`). O fallback é a chave embedada no binário.
```json
// config.json (opcional — sobrepõe a embedada)
{
"SUPABASE_ANON_KEY": "sb_publishable_...",
"SUPABASE_SERVICE_KEY": "sb_secret_..."
}
```
-35
View File
@@ -900,14 +900,12 @@ dependencies = [
"rusqlite",
"serde",
"serde_json",
"serde_yaml",
"tauri",
"tauri-build",
"tauri-plugin-dialog",
"tauri-plugin-fs",
"tauri-plugin-shell",
"thiserror 2.0.18",
"tokio",
"uuid",
]
@@ -3281,19 +3279,6 @@ dependencies = [
"syn 2.0.118",
]
[[package]]
name = "serde_yaml"
version = "0.9.34+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
dependencies = [
"indexmap 2.14.0",
"itoa",
"ryu",
"serde",
"unsafe-libyaml",
]
[[package]]
name = "serialize-to-javascript"
version = "0.1.2"
@@ -4070,25 +4055,11 @@ dependencies = [
"bytes",
"libc",
"mio",
"parking_lot",
"pin-project-lite",
"signal-hook-registry",
"socket2",
"tokio-macros",
"windows-sys 0.61.2",
]
[[package]]
name = "tokio-macros"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.118",
]
[[package]]
name = "tokio-native-tls"
version = "0.3.1"
@@ -4399,12 +4370,6 @@ version = "1.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
[[package]]
name = "unsafe-libyaml"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
[[package]]
name = "untrusted"
version = "0.9.0"
-2
View File
@@ -15,8 +15,6 @@ serde = { version = "1", features = ["derive"] }
serde_json = "1"
rusqlite = { version = "0.32", features = ["bundled"] }
reqwest = { version = "0.12", features = ["json", "blocking"] }
tokio = { version = "1", features = ["full"] }
serde_yaml = "0.9"
uuid = { version = "1", features = ["v4"] }
chrono = { version = "0.4", features = ["serde"] }
log = "0.4"
+23
View File
@@ -0,0 +1,23 @@
{
"identifier": "default",
"description": "Default capabilities for Fechamento de Caixa",
"windows": ["main"],
"permissions": [
"core:default",
"shell:allow-open",
"dialog:allow-message",
"dialog:allow-confirm",
"dialog:allow-ask",
"dialog:allow-save",
"dialog:allow-open",
"fs:default",
"fs:allow-app-read",
"fs:allow-app-write",
"fs:allow-appdata-read",
"fs:allow-appdata-write",
"fs:allow-appconfig-read",
"fs:allow-appconfig-write",
"fs:allow-applocaldata-read",
"fs:allow-applocaldata-write"
]
}
+8 -3
View File
@@ -36,10 +36,10 @@ fn load_config() -> (String, String) {
}
}
}
// Fallback: tenta variáveis de ambiente
// Fallback 3: chaves embedadas no binário (funciona em qualquer PC sem config)
(
std::env::var("SUPABASE_ANON_KEY").unwrap_or_default(),
std::env::var("SUPABASE_SERVICE_KEY").unwrap_or_default(),
"sb_publishable_NCUGwstslOvVt5JtGxKB9A_p-BERG57".to_string(),
"sb_secret_T6eHIKJDGaH53HMTG6UUKa_6ou6KfEW".to_string(),
)
}
@@ -96,6 +96,9 @@ fn main() {
plugins::supabase::sb_carregar_rascunho,
plugins::supabase::sb_listar_recentes,
plugins::supabase::sb_buscar_por_id,
plugins::supabase::sb_buscar_por_uuid,
plugins::supabase::sb_buscar_por_id_fechamento,
plugins::supabase::sb_atualizar_fechamento,
plugins::supabase::sb_salvar_listas,
plugins::supabase::sb_carregar_listas,
plugins::supabase::sb_online,
@@ -105,8 +108,10 @@ fn main() {
plugins::printer::caminho_impressora,
plugins::printer::imprimir_recibo,
plugins::printer::teste_impressora,
plugins::printer::ativar_modo_teste_impressao,
// App
plugins::app::get_loja,
plugins::app::get_loja_fixa,
plugins::app::set_loja,
plugins::app::get_config,
plugins::app::set_config,
+27 -1
View File
@@ -1,5 +1,8 @@
//! Plugin de configuração da aplicação (loja, preferências)
//! Persistido em config.json ao lado do banco SQLite.
//!
//! LOJA_FIXA: definido em tempo de build via env DOBRADO_LOJA (Uniao | Alianca).
//! Quando definido, a loja é travada e o seletor do formulário fica desabilitado.
use serde::{Deserialize, Serialize};
use std::fs;
@@ -7,6 +10,9 @@ use std::path::PathBuf;
use std::sync::Mutex;
use tauri::State;
/// Indica se a loja foi fixada em tempo de build.
pub const LOJA_FIXA: Option<&'static str> = option_env!("DOBRADO_LOJA");
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct AppConfig {
pub loja: String,
@@ -15,17 +21,28 @@ pub struct AppConfig {
pub turno_default: String,
pub sync_automatico: bool,
pub auto_open_printer: bool,
/// Verdadeiro quando a build tem loja fixa (não permite trocar no formulário)
#[serde(skip_serializing_if = "Option::is_none")]
pub loja_fixa: Option<bool>,
}
impl AppConfig {
/// Loja padrão: o env DOBRADO_LOJA se existir, senão "Uniao"
pub fn default_loja() -> String {
LOJA_FIXA.unwrap_or("Uniao").to_string()
}
}
impl Default for AppConfig {
fn default() -> Self {
Self {
loja: "Uniao".to_string(),
loja: Self::default_loja(),
pdv_nome: "PDV-01".to_string(),
operador_default: String::new(),
turno_default: String::new(),
sync_automatico: true,
auto_open_printer: false,
loja_fixa: LOJA_FIXA.map(|_| true),
}
}
}
@@ -65,8 +82,17 @@ pub fn get_loja(state: State<'_, AppState>) -> String {
state.config.lock().unwrap().loja.clone()
}
#[tauri::command]
pub fn get_loja_fixa() -> Option<String> {
LOJA_FIXA.map(|s| s.to_string())
}
#[tauri::command]
pub fn set_loja(state: State<'_, AppState>, loja: String) -> Result<(), String> {
// Se loja_fixa está ativa, não permite alterar
if LOJA_FIXA.is_some() {
return Err("Loja fixa em tempo de build — não é possível trocar.".to_string());
}
state.config.lock().unwrap().loja = loja;
state.save()
}
+3
View File
@@ -8,6 +8,7 @@ pub const LF: &[u8] = b"\x0A";
/// Cortar papel (parcial)
pub const CUT: &[u8] = b"\x1D\x56\x01";
/// Cortar papel (total)
#[allow(dead_code)]
pub const CUT_FULL: &[u8] = b"\x1D\x56\x00";
/// Alimentar 3 linhas antes de cortar
pub const FEED_CUT: &[u8] = b"\x1B\x64\x03";
@@ -17,6 +18,7 @@ pub const BOLD_OFF: &[u8] = b"\x1B\x45\x00";
/// Alinhamento
pub const ALIGN_LEFT: &[u8] = b"\x1B\x61\x00";
pub const ALIGN_CENTER: &[u8] = b"\x1B\x61\x01";
#[allow(dead_code)]
pub const ALIGN_RIGHT: &[u8] = b"\x1B\x61\x02";
/// Fonte normal / dupla
pub const FONT_NORMAL: &[u8] = b"\x1B\x21\x00";
@@ -38,6 +40,7 @@ pub fn title(text: &str) -> Vec<u8> {
}
/// Monta linha normal (alinhada à esquerda)
#[allow(dead_code)]
pub fn line(text: &str) -> Vec<u8> {
let mut out = Vec::new();
out.extend_from_slice(ALIGN_LEFT);
+385 -76
View File
@@ -5,6 +5,7 @@ use std::sync::Mutex;
use thiserror::Error;
#[derive(Error, Debug)]
#[allow(dead_code)]
pub enum PrinterError {
#[error("Impressora não encontrada. Verifique se está conectada via USB e ligada.")]
NotFound,
@@ -30,6 +31,50 @@ use crate::plugins::escpos;
// ── Struct de dados do recibo ────────────────────────────────────────────────
/// Linha de uma tabela no recibo (despesas, sangrias, vales, etc.)
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct ReciboLinha {
#[serde(default)]
pub desc: Option<String>,
#[serde(rename = "hora", default)]
pub hora: Option<String>,
#[serde(rename = "nome", default)]
pub nome: Option<String>,
#[serde(default)]
pub retirada: Option<f64>,
#[serde(default)]
pub valor: Option<f64>,
#[serde(default)]
pub obs: Option<String>,
#[serde(default)]
pub gerente: Option<String>,
#[serde(default)]
pub numero: Option<String>,
#[serde(default)]
pub motivo: Option<String>,
}
impl ReciboLinha {
fn fmt_v(&self) -> String {
let v = self.retirada.or(self.valor).unwrap_or(0.0);
format!("R$ {:.2}", v).replace('.', ",")
}
fn label(&self) -> String {
self.desc
.clone()
.or(self.nome.clone())
.or(self.hora.clone().map(|h| format!("{}h", h)))
.unwrap_or_default()
}
fn obs(&self) -> String {
self.gerente
.clone()
.or(self.obs.clone())
.or(self.motivo.clone())
.unwrap_or_default()
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ReciboData {
pub loja: String,
@@ -37,18 +82,55 @@ pub struct ReciboData {
pub operador: String,
pub turno: String,
pub saldo_troco: f64,
/// Valor contado no fechamento (dinheiro + cartões + receber)
pub fechamento: f64,
/// Saldo esperado = total operações sangrias despesas
pub saldo_esperado: f64,
/// Diferença = fechamento saldo_esperado
pub diferenca: f64,
pub sangrias: f64,
pub despesas: f64,
pub vales: f64,
pub areceber: f64,
/// 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,
pub debito: f64,
pub alimentacao: f64,
pub voucher: f64,
// Totais diversos (mantidos para compatibilidade com JSON antigo)
#[serde(default)]
pub sangrias: f64,
#[serde(default)]
pub despesas: f64,
#[serde(default)]
pub vales: f64,
#[serde(default)]
pub areceber: f64,
#[serde(default)]
pub pixcnpj: f64,
#[serde(default)]
pub cancelamentos: f64,
// Arrays para linhas detalhadas (recebidos do frontend)
#[serde(default)]
pub sangrias_arr: Vec<ReciboLinha>,
#[serde(default)]
pub despesas_arr: Vec<ReciboLinha>,
#[serde(default)]
pub pixcnpj_arr: Vec<ReciboLinha>,
#[serde(default)]
pub vales_arr: Vec<ReciboLinha>,
#[serde(default)]
pub receber_arr: Vec<ReciboLinha>,
#[serde(default)]
pub cancelamentos_arr: Vec<ReciboLinha>,
#[serde(default)]
pub observacoes: Option<String>,
#[serde(default)]
pub clientes: i32,
#[serde(default)]
pub frango: i32,
}
impl ReciboData {
@@ -57,6 +139,7 @@ impl ReciboData {
}
/// Gera todos os bytes ESC/POS do recibo 80mm
/// 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();
@@ -76,39 +159,193 @@ impl ReciboData {
out.extend(escpos::kv("Turno:", &self.turno));
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());
// ── SANGRIAS ──
if !self.sangrias_arr.is_empty() {
out.extend(escpos::line_bold("SANGRIAS"));
for linha in &self.sangrias_arr {
if linha.retirada.unwrap_or(0.0) > 0.0 || linha.valor.unwrap_or(0.0) > 0.0 {
let label = linha.label();
let obs = linha.obs();
out.extend(escpos::kv(&label, &linha.fmt_v()));
if !obs.is_empty() {
out.extend(escpos::line_center(&format!(" {}", obs)));
}
}
}
// Total sangrias
let total_sangrias: f64 = self.sangrias_arr.iter()
.map(|l| l.retirada.unwrap_or(0.0) + l.valor.unwrap_or(0.0))
.sum();
if total_sangrias > 0.0 {
out.extend(escpos::kv("Total:", &self.fmt_money(total_sangrias)));
}
out.extend(escpos::divider());
}
// Débitos
out.extend(escpos::line_bold("DÉBITOS"));
out.extend(escpos::kv("Sangrias:", &self.fmt_money(self.sangrias)));
out.extend(escpos::kv("Despesas:", &self.fmt_money(self.despesas)));
out.extend(escpos::kv("Vales:", &self.fmt_money(self.vales)));
out.extend(escpos::kv("A Receber:", &self.fmt_money(self.areceber)));
out.extend(escpos::divider());
// ── DESPESAS ──
if !self.despesas_arr.is_empty() {
out.extend(escpos::line_bold("DESPESAS"));
for linha in &self.despesas_arr {
if linha.valor.unwrap_or(0.0) > 0.0 {
let label = linha.label();
let obs = linha.obs();
out.extend(escpos::kv(&label, &linha.fmt_v()));
if !obs.is_empty() {
out.extend(escpos::line_center(&format!(" {}", obs)));
}
}
}
// Total despesas
let total_despesas: f64 = self.despesas_arr.iter()
.map(|l| l.valor.unwrap_or(0.0))
.sum();
out.extend(escpos::kv("Total:", &self.fmt_money(total_despesas)));
out.extend(escpos::divider());
}
// Cartões
out.extend(escpos::line_bold("CARTÕES"));
out.extend(escpos::kv("Crédito:", &self.fmt_money(self.credito)));
out.extend(escpos::kv("Débito:", &self.fmt_money(self.debito)));
out.extend(escpos::kv("Alimentação:", &self.fmt_money(self.alimentacao)));
out.extend(escpos::kv("Voucher:", &self.fmt_money(self.voucher)));
// ── PIX CNPJ ──
if !self.pixcnpj_arr.is_empty() {
out.extend(escpos::line_bold("PIX CNPJ"));
for linha in &self.pixcnpj_arr {
if linha.valor.unwrap_or(0.0) > 0.0 {
let label = linha.nome.clone().unwrap_or_else(|| "PIX CNPJ".to_string());
out.extend(escpos::kv(&label, &linha.fmt_v()));
}
}
// Total PIX CNPJ
let total_pixcnpj: f64 = self.pixcnpj_arr.iter()
.map(|l| l.valor.unwrap_or(0.0))
.sum();
if total_pixcnpj > 0.0 {
out.extend(escpos::kv("Total:", &self.fmt_money(total_pixcnpj)));
}
out.extend(escpos::divider());
}
// ── VALES ──
if !self.vales_arr.is_empty() {
out.extend(escpos::line_bold("VALES"));
for linha in &self.vales_arr {
if linha.valor.unwrap_or(0.0) > 0.0 {
let label = linha.label();
let obs = linha.obs();
out.extend(escpos::kv(&label, &linha.fmt_v()));
if !obs.is_empty() {
out.extend(escpos::line_center(&format!(" {}", obs)));
}
}
}
// Total vales
let total_vales: f64 = self.vales_arr.iter()
.map(|l| l.valor.unwrap_or(0.0))
.sum();
if total_vales > 0.0 {
out.extend(escpos::kv("Total:", &self.fmt_money(total_vales)));
}
out.extend(escpos::divider());
}
// ── A RECEBER ──
if !self.receber_arr.is_empty() {
out.extend(escpos::line_bold("A RECEBER"));
for linha in &self.receber_arr {
if linha.valor.unwrap_or(0.0) > 0.0 {
let label = linha.label();
out.extend(escpos::kv(&label, &linha.fmt_v()));
}
}
// Total a receber
let total_receber: f64 = self.receber_arr.iter()
.map(|l| l.valor.unwrap_or(0.0))
.sum();
if total_receber > 0.0 {
out.extend(escpos::kv("Total:", &self.fmt_money(total_receber)));
}
out.extend(escpos::divider());
}
// ── CANCELAMENTOS ──
if !self.cancelamentos_arr.is_empty() {
out.extend(escpos::line_bold("CANCELAMENTOS"));
for linha in &self.cancelamentos_arr {
if linha.numero.is_some() || linha.valor.unwrap_or(0.0) > 0.0 {
let num = linha.numero.clone().unwrap_or_default();
out.extend(escpos::kv(&num, &linha.fmt_v()));
if let Some(ref m) = linha.motivo {
if !m.is_empty() {
out.extend(escpos::line_center(&format!(" {}", m)));
}
}
}
}
// Total cancelamentos
let total_cancel: f64 = self.cancelamentos_arr.iter()
.map(|l| l.valor.unwrap_or(0.0))
.sum();
if total_cancel > 0.0 {
out.extend(escpos::kv("Total:", &self.fmt_money(total_cancel)));
}
out.extend(escpos::divider());
}
// ── 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());
// Diferença
let diff_str = if self.diferenca >= 0.0 {
format!("+{}", self.fmt_money(self.diferenca))
// ── 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 ──
// Diferença = TOTAL SISTEMA - saldo_esperado - cancelamentos
out.extend(escpos::line_bold("DIFERENCA"));
let diff = self.sys_total - self.saldo_esperado - self.cancelamentos;
let diff_str = if diff >= 0.0 {
format!("+{}", self.fmt_money(diff))
} else {
self.fmt_money(self.diferenca)
self.fmt_money(diff)
};
out.extend(escpos::line_bold("DIFERENÇA:"));
out.extend(escpos::title(&diff_str));
out.extend(escpos::blank_lines(2));
// ── CONTADORES ──
out.extend(escpos::line_bold("CONTADORES"));
out.extend(escpos::kv("Clientes:", &self.clientes.to_string()));
out.extend(escpos::kv("Frango Assado:", &self.frango.to_string()));
out.extend(escpos::divider());
// ── OBSERVAÇÕES ──
if let Some(ref obs) = self.observacoes {
if !obs.trim().is_empty() {
out.extend(escpos::line_bold("OBSERVACOES"));
out.extend(escpos::line_center(obs.trim()));
out.extend(escpos::divider());
}
}
// Rodapé
out.extend(escpos::divider());
@@ -126,82 +363,144 @@ impl ReciboData {
}
}
// ── USB Printer (stub for cross-compilation) ─────────────────────────────────
// Real implementation requires native Windows build with correct windows crate API.
#[cfg(windows)]
pub mod usb_windows {
pub fn find_printer_device() -> Option<String> {
// STUB: returns None during cross-compilation
// Native Windows build will enumerate USB ports
None
}
pub fn open_printer(_path: &str) -> Result<isize, String> {
Err("Cross-compilation stub: printing needs native Windows build".into())
}
pub fn write_to(_handle: isize, _data: &[u8]) -> Result<usize, String> {
Err("Cross-compilation stub".into())
}
pub fn close_handle(_h: isize) {}
}
#[cfg(not(windows))]
pub mod usb_windows {
pub fn find_printer_device() -> Option<String> {
None
}
pub fn open_printer(_path: &str) -> Result<isize, String> {
Err("Not supported on this platform".into())
}
pub fn write_to(_handle: isize, _data: &[u8]) -> Result<usize, String> {
Err("Not supported on this platform".into())
}
pub fn close_handle(_h: isize) {}
}
// ── Printer Manager ─────────────────────────────────────────────────────────
pub struct PrinterManager {
device_path: Mutex<Option<String>>,
/// Modo teste: grava bytes num arquivo ao invés de enviar pra USB
test_mode: Mutex<bool>,
}
impl Default for PrinterManager {
fn default() -> Self {
Self {
device_path: Mutex::new(None),
test_mode: Mutex::new(false),
}
}
}
impl PrinterManager {
/// Detecta impressora USB conectada
pub fn detectar(&self) -> Result<String, String> {
usb_windows::find_printer_device()
.ok_or_else(|| "Nenhuma impressora USB encontrada".into())
.map(|p| {
*self.device_path.lock().unwrap() = Some(p.clone());
p
})
#[cfg(windows)]
{
// Tenta encontrar porta USB virtual via WMI/registry
// Common TM-T20 USB port names on Windows
let candidates = [
r"\\.\USB001", // TM-T20 USB Printing Support
r"\\.\USB002",
r"\\.\GPR1",
r"\\.\COM3",
r"\\.\COM4",
];
for port in &candidates {
if std::path::Path::new(port).exists() || self.test_device(port) {
*self.device_path.lock().unwrap() = Some(port.to_string());
return Ok(port.to_string());
}
}
Err("Nenhuma impressora USB encontrada. Verifique o cabo e se a impressora está ligada.".into())
}
#[cfg(not(windows))]
{
Err("Deteccao USB s-para apenas no Windows.".into())
}
}
#[cfg(windows)]
fn test_device(&self, path: &str) -> bool {
use std::process::Command;
let out = Command::new("cmd")
.args(["/C", &format!("type {} < nul 2>&1", path)])
.output();
out.map(|o| o.status.success()).unwrap_or(false)
}
pub fn configurar(&self, path: String) -> Result<(), String> {
if path.is_empty() {
return Err("Caminho vazio".into());
}
*self.device_path.lock().unwrap() = Some(path);
*self.device_path.lock().unwrap() = Some(path.clone());
log::info!("Impressora configurada: {}", path);
Ok(())
}
/// Ativa modo teste: grava os bytes ESC/POS num arquivo em vez de imprimir
pub fn set_test_mode(&self, enabled: bool) {
*self.test_mode.lock().unwrap() = enabled;
log::info!("Printer test mode: {}", enabled);
}
pub fn imprimir_bytes(&self, data: &[u8]) -> Result<usize, String> {
// Modo teste: grava arquivo
if *self.test_mode.lock().unwrap() {
let test_file = std::env::temp_dir().join("fechamento_recibo_escpos.bin");
std::fs::write(&test_file, data)
.map_err(|e| format!("Falha ao gravar arquivo de teste: {}", e))?;
log::info!("Bytes ESC/POS gravados em: {:?}", test_file);
return Ok(data.len());
}
let path = self
.device_path
.lock()
.unwrap()
.clone()
.ok_or_else(|| "Impressora nao configurada".to_string())?;
let handle = usb_windows::open_printer(&path)?;
let n = usb_windows::write_to(handle, data)?;
usb_windows::close_handle(handle);
Ok(n)
.ok_or_else(|| "Impressora nao configurada. Use o botao de detectar ou configure manualmente.".to_string())?;
#[cfg(windows)]
{
self.windows_write(&path, data)
}
#[cfg(not(windows))]
{
let _ = data;
Err("Impressao USB disponivel apenas no Windows.".into())
}
}
#[cfg(windows)]
fn windows_write(&self, path: &str, data: &[u8]) -> Result<usize, String> {
use std::io::Write;
// Tenta via File primeiro (mais simples)
{
let file = std::fs::OpenOptions::new()
.write(true)
.create(false)
.open(path);
if let Ok(mut f) = file {
let r = f.write_all(data);
let _ = f.flush();
if r.is_ok() {
return Ok(data.len());
}
}
}
// Fallback: usa API Windows direta via std::process::Command + > arquivo
// Isso绕过 problemas de locking do OpenOptions
let tmp = std::env::temp_dir().join("escpos_tmp.bin");
std::fs::write(&tmp, data)
.map_err(|e| format!("Falha ao criar arquivo temporario: {}", e))?;
let status = std::process::Command::new("cmd")
.args(["/C", &format!("copy /B {} {} /Y > nul 2>&1", tmp.display(), path)])
.status();
let _ = std::fs::remove_file(&tmp);
if status.map(|s| s.success()).unwrap_or(false) {
log::info!("Escritos {} bytes para {}", data.len(), path);
Ok(data.len())
} else {
Err(format!(
"Falha ao enviar dados para {}. Verifique se a impressora USB está \
conectada e ligada. Tente configurar manualmente em Config > Impressora.",
path
))
}
}
}
@@ -209,6 +508,7 @@ impl PrinterManager {
static PRINTER: PrinterManager = PrinterManager {
device_path: Mutex::new(None),
test_mode: Mutex::new(false),
};
#[tauri::command]
@@ -231,22 +531,31 @@ pub fn imprimir_recibo(data: String) -> Result<usize, String> {
let r: ReciboData =
serde_json::from_str(&data).map_err(|e| format!("Erro ao analisar dados: {}", e))?;
let bytes = r.to_escpos();
log::info!("Imprimindo recibo: {} bytes ESC/POS", bytes.len());
PRINTER.imprimir_bytes(&bytes)
}
#[tauri::command]
pub fn teste_impressora() -> Result<usize, String> {
// Simple test pattern
let mut test = Vec::new();
test.extend(escpos::INIT);
test.extend(escpos::blank_lines(1));
test.extend(escpos::title("TESTE DE IMPRESSORA"));
test.extend(escpos::line_center("O Frangao"));
test.extend(escpos::line_center(&chrono::Local::now().format("%d/%m/%Y %H:%M").to_string()));
test.extend(escpos::blank_lines(2));
test.extend(escpos::FEED_CUT);
test.extend(escpos::CUT);
log::info!("Teste impressora: {} bytes", test.len());
PRINTER.imprimir_bytes(&test)
}
#[tauri::command]
pub fn ativar_modo_teste_impressao() {
PRINTER.set_test_mode(true);
}
/// Called by Tauri on plugin init (no-op for this plugin)
pub fn init() {}
pub fn init() {
log::info!("Printer plugin ready. Use detectar_impressora() ou configure manualmente.");
}
+78 -17
View File
@@ -335,6 +335,7 @@ impl DbManager {
}
/// Atualiza sync_status de um registro.
#[allow(dead_code)]
pub fn atualizar_sync(&self, uuid: &str, status: SyncStatus) -> Result<(), StorageError> {
let conn = self.conn.lock().map_err(|_| StorageError::Lock)?;
let now = Utc::now().format("%Y-%m-%dT%H:%M:%S%.3fZ").to_string();
@@ -346,6 +347,7 @@ impl DbManager {
}
/// Remove um registro (para debugging).
#[allow(dead_code)]
pub fn remover(&self, uuid: &str) -> Result<bool, StorageError> {
let conn = self.conn.lock().map_err(|_| StorageError::Lock)?;
let n = conn.execute("DELETE FROM fechamentos WHERE uuid = ?1", params![uuid])?;
@@ -383,39 +385,98 @@ pub fn salvar_fechamento(
let saldo_troco = estado.get("saldo_troco").and_then(|v| v.as_f64()).unwrap_or(0.0);
let fechamento = estado.get("fechamento").and_then(|v| v.as_f64()).unwrap_or(0.0);
let saldo_esperado = saldo_troco + fechamento;
let diferenca = estado.get("diferenca").and_then(|v| v.as_f64()).unwrap_or(0.0);
// Constrói TableData a partir de despesas/receber arrays
let despesas: Vec<serde_json::Value> = estado.get("despesas")
// Constrói TableData a partir de todas as tabelas do frontend
let despesas: Vec<serde_json::Value> = estado
.get("despesas")
.and_then(|v| v.as_array())
.map(|arr| arr.to_vec())
.unwrap_or_default();
let receber: Vec<serde_json::Value> = estado.get("receber")
let sangrias: Vec<serde_json::Value> = estado
.get("sangrias")
.and_then(|v| v.as_array())
.map(|arr| arr.to_vec())
.unwrap_or_default();
let pixcnpj: Vec<serde_json::Value> = estado
.get("pixcnpj")
.and_then(|v| v.as_array())
.map(|arr| arr.to_vec())
.unwrap_or_default();
let vales: Vec<serde_json::Value> = estado
.get("vales")
.and_then(|v| v.as_array())
.map(|arr| arr.to_vec())
.unwrap_or_default();
let receber: Vec<serde_json::Value> = estado
.get("receber")
.and_then(|v| v.as_array())
.map(|arr| arr.to_vec())
.unwrap_or_default();
let cancelamentos: Vec<serde_json::Value> = estado
.get("cancelamentos")
.and_then(|v| v.as_array())
.map(|arr| arr.to_vec())
.unwrap_or_default();
let cartoes_json = estado.get("cartoes");
let to_table_row = |v: &serde_json::Value| -> TableRow {
let to_table_row = |v: &serde_json::Value, _tipo: &str| -> TableRow {
TableRow {
descricao: v.get("desc").and_then(|s| s.as_str()).map(String::from),
descricao: v.get("desc").or_else(|| v.get("nome")).and_then(|s| s.as_str()).map(String::from),
retirada: v.get("retirada").and_then(|n| n.as_f64()),
valor: v.get("valor").and_then(|n| n.as_f64()),
motivo: v.get("motivo").and_then(|s| s.as_str()).map(String::from),
cliente: v.get("nome").and_then(|s| s.as_str()).map(String::from),
// hora, gerente, obs, numero vão para extra via flatten
extra: std::collections::HashMap::new(),
..Default::default()
}
};
let tabelas = TableData {
despesas: despesas.iter().map(to_table_row).collect(),
areceber: receber.iter().map(to_table_row).collect(),
..Default::default()
despesas: despesas.iter().map(|v| to_table_row(v, "despesas")).collect(),
sangrias: sangrias.iter().map(|v| to_table_row(v, "sangrias")).collect(),
pixcnpj: pixcnpj.iter().map(|v| to_table_row(v, "pixcnpj")).collect(),
vales: vales.iter().map(|v| to_table_row(v, "vales")).collect(),
areceber: receber.iter().map(|v| to_table_row(v, "receber")).collect(),
cancelamentos: cancelamentos.iter().map(|v| to_table_row(v, "cancelamentos")).collect(),
};
// Cartoes como scalars
let cartoes_json = estado.get("cartoes");
let cartoes = CartaoData {
credito: vec![TableRow { valor: cartoes_json.and_then(|c| c.get("credito")).and_then(|v| v.as_f64()), ..Default::default() }],
debito: vec![TableRow { valor: cartoes_json.and_then(|c| c.get("debito")).and_then(|v| v.as_f64()), ..Default::default() }],
alimentacao: vec![TableRow { valor: cartoes_json.and_then(|c| c.get("alimentacao")).and_then(|v| v.as_f64()), ..Default::default() }],
voucher: vec![TableRow { valor: cartoes_json.and_then(|c| c.get("voucher")).and_then(|v| v.as_f64()), ..Default::default() }],
credito: cartoes_json
.and_then(|c| c.get("credito"))
.and_then(|v| v.as_array())
.map(|arr| {
arr.iter()
.map(|v| TableRow { valor: v.as_f64(), ..Default::default() })
.collect()
})
.unwrap_or_default(),
debito: cartoes_json
.and_then(|c| c.get("debito"))
.and_then(|v| v.as_array())
.map(|arr| {
arr.iter()
.map(|v| TableRow { valor: v.as_f64(), ..Default::default() })
.collect()
})
.unwrap_or_default(),
alimentacao: cartoes_json
.and_then(|c| c.get("alimentacao"))
.and_then(|v| v.as_array())
.map(|arr| {
arr.iter()
.map(|v| TableRow { valor: v.as_f64(), ..Default::default() })
.collect()
})
.unwrap_or_default(),
voucher: cartoes_json
.and_then(|c| c.get("pix"))
.and_then(|v| v.as_array())
.map(|arr| {
arr.iter()
.map(|v| TableRow { valor: v.as_f64(), ..Default::default() })
.collect()
})
.unwrap_or_default(),
};
let estado_rust = FechamentoEstado {
@@ -449,7 +510,7 @@ pub fn carregar_rascunho(
// Simplifica para formato que o frontend espera
let total_sangrias: f64 = e.tabelas.sangrias.iter().filter_map(|r| r.valor).sum();
let total_despesas: f64 = e.tabelas.despesas.iter().filter_map(|r| r.valor).sum();
let total_receber: f64 = e.tabelas.areceber.iter().filter_map(|r| r.valor).sum();
let _total_receber: f64 = e.tabelas.areceber.iter().filter_map(|r| r.valor).sum();
let despesa_rows: Vec<_> = e.tabelas.despesas.iter().filter_map(|r| {
r.descricao.as_ref().map(|d| serde_json::json!({"desc": d, "valor": r.valor}))
}).collect();
+267 -42
View File
@@ -7,7 +7,31 @@ use std::time::Duration;
use tauri::State;
use thiserror::Error;
/// Remove acentos de uma string (ex: "União" → "Uniao")
fn sem_acento(s: &str) -> String {
s.replace("ã", "a")
.replace("á", "a")
.replace("à", "a")
.replace("â", "a")
.replace("é", "e")
.replace("è", "e")
.replace("ê", "e")
.replace("í", "i")
.replace("ì", "i")
.replace("î", "i")
.replace("õ", "o")
.replace("ó", "o")
.replace("ò", "o")
.replace("ô", "o")
.replace("ú", "u")
.replace("ù", "u")
.replace("û", "u")
.replace("ç", "c")
.replace("ñ", "n")
}
#[derive(Error, Debug)]
#[allow(dead_code)]
pub enum SupabaseError {
#[error("HTTP error: {0}")]
Http(#[from] reqwest::Error),
@@ -68,58 +92,76 @@ impl SupabaseClient {
/// Salva/atualiza um fechamento na tabela `fechamentos_web`.
pub fn salvar_fechamento(&self, estado: &serde_json::Value) -> Result<Value, SupabaseError> {
let uuid = estado
.get("uuid")
// Normaliza loja e turno para minúsculo (CHECK constraints do Postgres)
let loja = estado
.get("loja")
.and_then(|v| v.as_str())
.unwrap_or("");
.map(|s| sem_acento(&s.to_lowercase()).replace(" ", "_"))
.unwrap_or_else(|| "uniao".to_string());
let data = estado.get("data").and_then(|v| v.as_str()).unwrap_or("");
let operador = estado.get("operador").and_then(|v| v.as_str()).unwrap_or("");
let id_fechamento = format!("{}_{}_{}", loja, data, operador)
.to_lowercase()
.replace(" ", "_");
let turno = estado
.get("turno")
.and_then(|v| v.as_str())
.map(|s| sem_acento(&s.to_lowercase()))
.unwrap_or_else(|| "manha".to_string());
let payload = serde_json::json!({
"uuid": uuid,
"loja": estado["loja"],
"data": estado["data"],
"operador": estado["operador"],
"turno": estado["turno"],
"saldo_troco": estado["saldo_troco"],
"saldo_esperado": estado["saldo_esperado"],
"fechamento": estado["fechamento"],
"id_fechamento": id_fechamento,
"loja": loja,
"data": data,
"operador": operador,
"turno": turno,
"saldo_troco": estado.get("saldo_troco").and_then(|v| v.as_f64()).unwrap_or(0.0),
"saldo_credito": estado.get("credito").and_then(|v| v.as_f64()).unwrap_or(0.0),
"saldo_debito": estado.get("debito").and_then(|v| v.as_f64()).unwrap_or(0.0),
"saldo_alimentacao": estado.get("alimentacao").and_then(|v| v.as_f64()).unwrap_or(0.0),
"saldo_vales": estado.get("vales").and_then(|v| v.as_f64()).unwrap_or(0.0),
"saldo_areceber": estado.get("areceber").and_then(|v| v.as_f64()).unwrap_or(0.0),
"saldo_pixcnpj": estado.get("pixcnpj").and_then(|v| v.as_f64()).unwrap_or(0.0),
"fechamento_dinheiro": estado.get("fd").and_then(|v| v.as_f64()).unwrap_or(0.0),
"fechamento_cartoes": estado.get("fc").and_then(|v| v.as_f64()).unwrap_or(0.0),
"fechamento_areceber": estado.get("fr").and_then(|v| v.as_f64()).unwrap_or(0.0),
"total_sangrias": estado.get("total_sangrias").and_then(|v| v.as_f64()).unwrap_or(0.0),
"total_despesas": estado.get("total_despesas").and_then(|v| v.as_f64()).unwrap_or(0.0),
"total_cancelamentos": estado.get("total_cancelamentos").and_then(|v| v.as_f64()).unwrap_or(0.0),
"total_vales": estado.get("total_vales").and_then(|v| v.as_f64()).unwrap_or(0.0),
"total_pixcnpj": estado.get("total_pixcnpj").and_then(|v| v.as_f64()).unwrap_or(0.0),
"total_areceber": estado.get("total_areceber").and_then(|v| v.as_f64()).unwrap_or(0.0),
"total_cartao_credito": estado.get("total_cartao_credito").and_then(|v| v.as_f64()).unwrap_or(0.0),
"total_cartao_debito": estado.get("total_cartao_debito").and_then(|v| v.as_f64()).unwrap_or(0.0),
"total_cartao_alimentacao": estado.get("total_cartao_alimentacao").and_then(|v| v.as_f64()).unwrap_or(0.0),
"total_cartao_pix": estado.get("total_cartao_pix").and_then(|v| v.as_f64()).unwrap_or(0.0),
"saldo_esperado": estado.get("saldo_esperado").and_then(|v| v.as_f64()).unwrap_or(0.0),
"dados": estado,
"observacoes": "rascunho",
"observacoes": estado.get("observacoes").and_then(|v| v.as_str()).unwrap_or(""),
"clientes": estado.get("clientes").and_then(|v| v.as_i64()).unwrap_or(0),
"frango": estado.get("frango").and_then(|v| v.as_i64()).unwrap_or(0),
"vendas": estado.get("vendas").and_then(|v| v.as_f64()).unwrap_or(0.0),
});
let _url = format!(
"{}/rest/v1/fechamentos_web?uuid=eq.{}&select=id",
self.base_url, uuid
);
// Upsert via POST com Prefer: resolution=merge-duplicates
// Primeiro tenta INSERT (upsert via Prefer)
let resp = self
.http
.post(&format!("{}/rest/v1/rpc/upsert_fechamento", self.base_url))
.post(&format!("{}/rest/v1/fechamentos_web", self.base_url))
.headers(self.headers(true))
.header("Prefer", "resolution=merge-duplicates")
.json(&payload)
.send()?;
if resp.status().is_success() {
Ok(serde_json::json!({ "ok": true, "uuid": uuid }))
let status = resp.status();
if status.is_success() || status.as_u16() == 409 {
Ok(serde_json::json!({ "ok": true, "id_fechamento": id_fechamento }))
} else {
let body = resp.text().unwrap_or_default();
// Fallback: tenta POST direto na tabela
let resp2 = self
.http
.post(&format!("{}/rest/v1/fechamentos_web", self.base_url))
.headers(self.headers(true))
.header("Prefer", "resolution=merge-duplicates")
.json(&payload)
.send()?;
if resp2.status().is_success() {
Ok(serde_json::json!({ "ok": true, "uuid": uuid }))
} else {
Err(SupabaseError::Api(format!(
"HTTP {}: {}",
resp2.status().as_u16(),
body
)))
}
Err(SupabaseError::Api(format!(
"HTTP {}: {}",
status.as_u16(),
body
)))
}
}
@@ -152,12 +194,127 @@ impl SupabaseClient {
}
}
/// Lista fechamentos recentes de uma loja.
pub fn listar_recentes(&self, loja: &str, limite: i64) -> Result<Vec<Value>, SupabaseError> {
/// Busca um fechamento pelo campo "id" (UUID externo).
pub fn buscar_por_uuid(&self, uuid: &str) -> Result<Option<Value>, SupabaseError> {
let url = format!(
"{}/rest/v1/fechamentos_web?loja=eq.{}&order=data.desc,atualizado_em.desc&limit={}&select=id,uuid,data,operador,turno,saldo_troco,saldo_esperado,fechamento,sync_status,observacoes,atualizado_em",
"{}/rest/v1/fechamentos_web?id=eq.{}&select=*",
self.base_url, uuid
);
let resp = self
.http
.get(&url)
.headers(self.headers(false))
.send()?;
if resp.status().is_success() {
let arr: Vec<Value> = resp.json()?;
Ok(arr.into_iter().next())
} else {
let status = resp.status();
let body = resp.text().unwrap_or_default();
Err(SupabaseError::Api(format!(
"HTTP {}: {}",
status.as_u16(),
body
)))
}
}
/// Busca um fechamento pelo id_fechamento (ex: uniao_2026-06-24_silanea).
pub fn buscar_por_id_fechamento(&self, id_fechamento: &str) -> Result<Option<Value>, SupabaseError> {
let url = format!(
"{}/rest/v1/fechamentos_web?id_fechamento=eq.{}&select=*",
self.base_url, urlencoding::encode(id_fechamento)
);
let resp = self
.http
.get(&url)
.headers(self.headers(false))
.send()?;
if resp.status().is_success() {
let arr: Vec<Value> = resp.json()?;
Ok(arr.into_iter().next())
} else {
let status = resp.status();
let body = resp.text().unwrap_or_default();
Err(SupabaseError::Api(format!(
"HTTP {}: {}",
status.as_u16(),
body
)))
}
}
/// Atualiza campos específicos de um fechamento pelo id_fechamento.
pub fn atualizar_fechamento(&self, id_fechamento: &str, updates: &serde_json::Value) -> Result<Value, SupabaseError> {
// Campos que o frontend envia mas NÃO existem como colunas na tabela.
// Arrays e objetos são guardados no JSONB `dados` — não em colunas escalar.
static BLOCKED: &[&str] = &[
"id", "uuid", "criado_em", "atualizado_em", "enviado_por",
"ip_origem", "user_agent", "id_fechamento",
// arrays e objetos que existem dentro do JSONB `dados`
"vales", "despesas", "sangrias", "cancelamentos",
"pixcnpj", "receber",
"cartoes_credito", "cartoes_debito",
"cartoes_alimentacao", "cartoes_pix",
];
let map = updates.as_object().cloned().unwrap_or_default();
// Remove campos bloqueados
let filtered: serde_json::Map<String, serde_json::Value> = map
.into_iter()
.filter(|(k, _)| !BLOCKED.contains(&k.as_str()))
.collect();
if filtered.is_empty() {
return Err(SupabaseError::Api("Nenhum campo para atualizar".into()));
}
let url = format!(
"{}/rest/v1/fechamentos_web?id_fechamento=eq.{}",
self.base_url,
urlencoding::encode(loja),
urlencoding::encode(id_fechamento)
);
log::info!("[PATCH] url={} body={:#?}", url, filtered);
let resp = self
.http
.patch(&url)
.headers(self.headers(true))
.header("Prefer", "return=representation")
.json(&filtered)
.send()?;
let status = resp.status();
if status.is_success() {
// Ler a resposta real do Supabase (retorna o registro atualizado)
if let Ok(ret) = resp.json::<serde_json::Value>() {
log::info!("[PATCH] sucesso — resposta: {}", ret);
} else {
log::warn!("[PATCH] sucesso mas não conseguiu ler resposta");
}
Ok(serde_json::json!({ "ok": true, "id_fechamento": id_fechamento }))
} else {
let body = resp.text().unwrap_or_default();
log::error!("[PATCH] HTTP {} body: {}", status.as_u16(), body);
Err(SupabaseError::Api(format!(
"HTTP {}: {}",
status.as_u16(),
body
)))
}
}
pub fn listar_recentes(&self, loja: &str, limite: i64) -> Result<Vec<Value>, SupabaseError> {
let loja_normalized = sem_acento(&loja.to_lowercase());
let url = format!(
"{}/rest/v1/fechamentos_web?loja=eq.{}&order=data.desc,atualizado_em.desc&limit={}&select=id,id_fechamento,data,operador,turno,saldo_troco,saldo_credito,saldo_debito,saldo_alimentacao,saldo_vales,saldo_areceber,saldo_esperado,total_pixcnpj,total_cartao_credito,total_cartao_debito,total_cartao_alimentacao,total_cartao_pix,total_cancelamentos,fechamento_dinheiro,fechamento_cartoes,fechamento_areceber,diferenca,status_diferenca,observacoes,atualizado_em,dados",
self.base_url,
urlencoding::encode(&loja_normalized),
limite,
);
@@ -273,6 +430,48 @@ impl SupabaseClient {
}
}
/// Tenta forçar PostgREST a recarregar schema com novas colunas (vendas, edited_at, edited_by).
/// Estratégia: INSERT com novas colunas → se PGRST204, espera 1.5s → retry.
/// Fire-and-forget — falha não bloqueia.
pub fn run_migration(&self) {
let payload = serde_json::json!({
"id_fechamento": "_mig_probe_1900",
"loja": "_probe_",
"data": "1900-01-01",
"operador": "_probe_",
"vendas": 0,
"edited_at": null,
"edited_by": null
});
let r1 = self.http
.post(&format!("{}/rest/v1/fechamentos_web", self.base_url))
.headers(self.headers(true))
.header("Prefer", "resolution=merge-duplicates")
.json(&payload)
.send();
if let Ok(resp) = r1 {
if resp.status().as_u16() == 400 {
std::thread::sleep(std::time::Duration::from_millis(1500));
let _ = self.http
.post(&format!("{}/rest/v1/fechamentos_web", self.base_url))
.headers(self.headers(true))
.header("Prefer", "resolution=merge-duplicates")
.json(&serde_json::json!({
"id_fechamento": "_mig_probe_1900",
"vendas": 0,
"edited_at": null,
"edited_by": null
}))
.send();
}
}
// Limpa probe
let _ = self.http
.delete(&format!("{}/rest/v1/fechamentos_web?id_fechamento=eq._mig_probe_1900", self.base_url))
.headers(self.headers(true))
.send();
}
/// Verifica se está online.
pub fn health_check(&self) -> bool {
let url = format!("{}/rest/v1/?limit=0", self.base_url);
@@ -321,6 +520,32 @@ pub fn sb_buscar_por_id(
sb.buscar_por_id(id)
}
#[tauri::command]
pub fn sb_buscar_por_uuid(
sb: State<'_, SupabaseClient>,
uuid: String,
) -> Result<Option<serde_json::Value>, SupabaseError> {
sb.buscar_por_uuid(&uuid)
}
/// Busca um fechamento pelo id_fechamento (ex: uniao_2026-06-24_silanea).
#[tauri::command]
pub fn sb_buscar_por_id_fechamento(
sb: State<'_, SupabaseClient>,
id_fechamento: String,
) -> Result<Option<serde_json::Value>, SupabaseError> {
sb.buscar_por_id_fechamento(&id_fechamento)
}
#[tauri::command(rename_all = "snake_case")]
pub fn sb_atualizar_fechamento(
sb: State<'_, SupabaseClient>,
id_fechamento: String,
updates: serde_json::Value,
) -> Result<serde_json::Value, SupabaseError> {
sb.atualizar_fechamento(&id_fechamento, &updates)
}
#[tauri::command]
pub fn sb_salvar_listas(
sb: State<'_, SupabaseClient>,
+11 -4
View File
@@ -11,7 +11,7 @@
"app": {
"windows": [
{
"title": "Fechamento de Caixa O Frangão",
"title": "Fechamento de Caixa \u2014 O Frang\u00e3o",
"width": 1100,
"height": 780,
"minWidth": 900,
@@ -22,13 +22,18 @@
}
],
"security": {
"csp": null
"csp": null,
"capabilities": [
"default"
]
},
"withGlobalTauri": true
},
"bundle": {
"active": true,
"targets": ["nsis"],
"targets": [
"nsis"
],
"icon": [
"icons/32x32.png",
"icons/128x128.png",
@@ -37,7 +42,9 @@
"icons/icon.ico"
],
"windows": {
"webviewInstallMode": { "type": "embedBootstrapper" }
"webviewInstallMode": {
"type": "embedBootstrapper"
}
},
"resources": {
"../src/*": "./"
+2485 -112
View File
File diff suppressed because it is too large Load Diff