docs: adicionar SPEC, SUPABASE_SCHEMA, API_INTEGRATION, BUILDS_FARM
This commit is contained in:
@@ -1,140 +1,136 @@
|
||||
# Fechamento de Caixa — O Frangão
|
||||
|
||||
App desktop nativo para fechamento de caixa com impressão em impressora térmica 80mm.
|
||||
## O que é
|
||||
|
||||
## Stack
|
||||
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.
|
||||
|
||||
- **Backend:** Tauri 2 + Rust
|
||||
- **Frontend:** HTML + CSS + JavaScript (vanilla, ~1700 linhas)
|
||||
- **Storage local:** SQLite (rusqlite) — funciona offline
|
||||
- **Sync:** Supabase REST API
|
||||
- **Impressão:** ESC/POS via USB (TM-T20 e compatíveis, Windows)
|
||||
## Ideia central
|
||||
|
||||
## Estrutura do Projeto
|
||||
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 inline)
|
||||
│ └── index.html ← Frontend completo (HTML + CSS + JS)
|
||||
├── src-tauri/
|
||||
│ ├── Cargo.toml # Dependências Rust
|
||||
│ ├── tauri.conf.json # Config do app Tauri
|
||||
│ └── src/
|
||||
│ ├── main.rs # Entry point — registra comandos Tauri
|
||||
│ └── plugins/
|
||||
│ ├── supabase.rs # Integração Supabase (REST API)
|
||||
│ ├── printer.rs # Geração ESC/POS + envio USB
|
||||
│ ├── storage.rs # SQLite local
|
||||
│ ├── app.rs # Config da app (loja, operador default)
|
||||
│ └── escpos.rs # Helpers ESC/POS (INIT, CUT, divider, etc.)
|
||||
└── README.md
|
||||
│ ├── 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
|
||||
```
|
||||
|
||||
## Pré-requisitos
|
||||
---
|
||||
|
||||
- Rust 1.70+ (`rustup install stable`)
|
||||
- Node.js 18+ (para build do frontend Tauri)
|
||||
- Windows 10/11 (impressão USB só funciona no Windows)
|
||||
## Builds — 3 versões do app
|
||||
|
||||
## Setup
|
||||
O mesmo código gera 3 binários diferentes conforme a variável de ambiente `DOBRADO_LOJA`:
|
||||
|
||||
### 1. Clonar o repositório
|
||||
| Binário | Loja | Como buildar |
|
||||
|---------|------|-------------|
|
||||
| `fc-uniao.exe` | **Travada: UNIÃO** | `DOBRADO_LOJA=Uniao cargo build --release --target x86_64-pc-windows-gnu` |
|
||||
| `fc-alianca.exe` | **Travada: ALIANÇA** | `DOBRADO_LOJA=Alianca cargo build --release --target x86_64-pc-windows-gnu` |
|
||||
| `fc-ambos.exe` | **Livre (União / Aliança)** | `cargo build --release --target x86_64-pc-windows-gnu` (sem env) |
|
||||
|
||||
```bash
|
||||
git clone https://git.ofrangao.com.br/filipe/fechamento-caixa.git
|
||||
cd fechamento-caixa
|
||||
```
|
||||
Quando a loja é fixa, o seletor de loja no formulário é **escondido via JavaScript** e tentativas de mudar via API retornam erro.
|
||||
|
||||
### 2. Configurar chaves do Supabase
|
||||
---
|
||||
|
||||
Crie o arquivo `config.json` em `%LOCALAPPDATA%\FechamentoCaixa\config.json` (Windows):
|
||||
|
||||
```json
|
||||
{
|
||||
"SUPABASE_ANON_KEY": "sua_chave_anon_aqui",
|
||||
"SUPABASE_SERVICE_KEY": "sua_chave_service_role_aqui"
|
||||
}
|
||||
```
|
||||
|
||||
O app procura esse arquivo na inicialização. Sem ele, tenta ler das variáveis de ambiente `SUPABASE_ANON_KEY` e `SUPABASE_SERVICE_KEY`.
|
||||
|
||||
### 3. Build
|
||||
|
||||
```bash
|
||||
cd src-tauri
|
||||
cargo build --release --target x86_64-pc-windows-gnu
|
||||
```
|
||||
|
||||
O executável fica em:
|
||||
```
|
||||
src-tauri/target/x86_64-pc-windows-gnu/release/fechamento-caixa.exe
|
||||
```
|
||||
|
||||
Para gerar o pacote de distribuição:
|
||||
```bash
|
||||
cp src-tauri/target/x86_64-pc-windows-gnu/release/fechamento-caixa.exe .
|
||||
cp src-tauri/target/x86_64-pc-windows-gnu/release/WebView2Loader.dll .
|
||||
tar -czf fechamento-caixa-vX.X.X.tar.gz fechamento-caixa.exe WebView2Loader.dll
|
||||
```
|
||||
|
||||
## Códigos de comando Tauri
|
||||
|
||||
O frontend chama esses comandos via `window.__TAURI__.core.invoke()`:
|
||||
|
||||
| Comando | Arquivo | Descrição |
|
||||
|---------|---------|-----------|
|
||||
| `salvar_fechamento` | storage.rs | Salva no SQLite local |
|
||||
| `carregar_rascunho` | storage.rs | Busca rascunho por loja+data |
|
||||
| `listar_fechamentos` | storage.rs | Lista histórico do SQLite |
|
||||
| `buscar_fechamento_por_id` | storage.rs | Busca por ID no SQLite |
|
||||
| `sb_salvar_fechamento` | supabase.rs | Envia para Supabase |
|
||||
| `sb_carregar_rascunho` | supabase.rs | Busca rascunho no Supabase |
|
||||
| `sb_listar_recentes` | supabase.rs | Lista fechamentos recentes |
|
||||
| `sb_buscar_por_id` | supabase.rs | Busca por ID no Supabase |
|
||||
| `sb_salvar_listas` | supabase.rs | Salva operadores/gerentes |
|
||||
| `sb_carregar_listas` | supabase.rs | Carrega listas do servidor |
|
||||
| `sb_online` | supabase.rs | Verifica conectividade |
|
||||
| `imprimir_recibo` | printer.rs | Envia bytes ESC/POS pra USB |
|
||||
| `detectar_impressora` | printer.rs | Detecta porta USB |
|
||||
| `configurar_impressora` | printer.rs | Define porta manual |
|
||||
| `teste_impressora` | printer.rs | Imprime página de teste |
|
||||
| `get_loja` / `set_loja` | app.rs | Loja atual |
|
||||
| `get_config` / `set_config` | app.rs | Config completo |
|
||||
|
||||
## Fluxo de dados
|
||||
## Fluxo de um fechamento
|
||||
|
||||
```
|
||||
[Usuário preenche formulário]
|
||||
│
|
||||
▼ (auto-save a cada 1.5s)
|
||||
SQLite local (fechamento.db)
|
||||
│
|
||||
├── [Botão Enviar] ──► Supabase (fechamentos_web)
|
||||
│
|
||||
└── [Botão Recibo] ──► printer.rs ──► to_escpos() ──► USB
|
||||
│
|
||||
├── Sucesso: imprime
|
||||
└── Falha: openPrintPreview() ──► iframe ──► window.print()
|
||||
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
|
||||
```
|
||||
|
||||
## Campos do formulário
|
||||
---
|
||||
|
||||
| Campo | Tabela Supabase | Notas |
|
||||
|-------|----------------|-------|
|
||||
| uuid, loja, data, operador, turno | fechamentos_web | PK composto |
|
||||
| saldo_troco, saldo_esperado, fechamento, diferenca | fechamentos_web | diferenca = fechamento - saldo_esperado |
|
||||
| despesas[], sangrias[], vales[], receber[], pixcnpj[], cancelamentos[] | dados (JSON) | Arrays de {desc/nome, valor, ...} |
|
||||
| cartoes{credito[],debito[],alimentacao[],pix[]} | dados (JSON) | Arrays de {valor} |
|
||||
| operadores[], gerentes[], despesas_custom[], clientes[] | listas_personalizadas | Listas por loja |
|
||||
## Autores & Contexto
|
||||
|
||||
## Problemas conhecidos
|
||||
|
||||
- Impressão USB só funciona no Windows
|
||||
- `service_role_key` no cliente desktop é risco de segurança — usar apenas anon_key + RLS
|
||||
- Sem testes automatizados
|
||||
|
||||
## Version History
|
||||
|
||||
- **v1.2.7** — 2026-06-24: 4 bugs críticos concertados (impressão, enviar, dia anterior, localStorage)
|
||||
- **v1.2.6** — 2026-06-24: WebView2Loader.dll incluso; init() restaura operador+turno
|
||||
- **v1.2.5** — 2026-06-24: diferenca = fechamento - esperado (não mais saldo_troco - esperado)
|
||||
- **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
|
||||
|
||||
Reference in New Issue
Block a user