← Projetos Leonardo Borges Ramos

Case · PDV offline

O caixa que não para quando a internet cai.

Como o ponto de venda do Develop continua vendendo sem rede e sincroniza depois, sem perder nem cobrar duas vezes uma venda.

Papel
Desenho e implementação, do comando em Rust à rota no servidor
Quando
Agosto de 2026, em duas fases
Stack
Tauri · Rust · JavaScript · Node.js · Express · MySQL
A tela do caixa do Develop, pronta para a primeira venda

O problema

Em loja, a internet cai. Um caixa que depende do servidor para fechar a venda para junto, com fila de cliente na frente. E a falha mais cara nem é parar: é cobrar duas vezes quando a conexão volta e a mesma venda é enviada de novo.

O caminho de uma venda

  1. O caixa fecha a vendanada nesse passo depende da rede
  2. A venda é gravada em disco primeirodiário só de acréscimo, com fsync, por comandos em Rust
  3. Ela entra na fila de saídaum selo mostra ao operador o que ainda não subiu
  4. Um agente envia a cada 90 segundosquando a rede volta, na ordem em que as vendas aconteceram
  5. O servidor grava pela mesma função da venda onlineuma regra de venda só, para os dois caminhos

Quatro decisões

1. Disco antes da rede

A venda só existe depois de escrita e confirmada em disco. Se o computador desligar no meio, ela está lá quando ele voltar. O catálogo de produtos também fica no caixa, para dar para vender sem consultar o servidor.

2. Retomada que não duplica

A fila guarda o número oficial que o servidor deu à venda e os itens que já foram gravados. Antes de fechar de novo, o caixa confere se a venda já foi finalizada; sem isso, o pagamento entraria duas vezes.

3. Uma função de gravação só

A gravação da venda saiu das rotas e virou um serviço compartilhado: a venda online e a que chega pela fila passam pelo mesmo código. O arquivo de rotas do PDV encolheu cerca de 240 linhas.

4. O servidor escolhe o caminho

O caixa pergunta ao servidor qual caminho de sincronização usar. Com isso, ligar ou desligar o robô do servidor muda o comportamento de todos os caixas sem publicar uma versão nova do app.

O bug que o teste pegou

No Sequelize, um INSERT devolve [insertId, affectedRows]. Lido do jeito errado, toda venda nova respondia "duplicada" e nunca entraria na fila. O teste pegou antes da publicação.

O que ainda falta

A busca de produtos do caixa offline devolve no máximo 20 resultados por consulta; para lojas com catálogo grande, a próxima etapa é uma busca local com índice.

Quer conversar sobre este projeto?