7.6 KiB
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:
- Captura todos os dados de fechamento de caixa (dinheiro, cartões, despesas, etc.)
- Salva localmente em SQLite (funciona offline)
- Envia automaticamente para o Supabase (cloud) quando há conexão
- 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 (Rust + WebView2) |
| Dados local | SQLite via rusqlite |
| Dados cloud | Supabase — 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 |
fc-alianca.exe |
Travada: ALIANÇA | Baixar |
fc-ambos.exe |
Seletor livre | Baixar |
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:
// 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