# 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