10 KiB
10 KiB
Contexto do Projeto - Gastrobar PDV (Café em Saturno)
Visão Geral
Sistema de PDV (Ponto de Venda) e gerenciamento para gastrobar/restaurante. Gerencia comandas de clientes, pedidos, cardápio de produtos, usuários com diferentes perfis de acesso e fluxo de preparo na cozinha/bar.
Stack
Backend (src/)
- Runtime: Node.js + TypeScript (CommonJS)
- Framework: Fastify v4.27 (porta 3333)
- ORM: Prisma v5.14 com SQLite (
prisma/dev.db) - Validação: Zod v3.23
- Auth: JWT (
@fastify/jwt) + bcrypt - Dev: tsx watch (
npm run dev)
Frontend (web/)
- Bundler: Vite v8.1
- Linguagem: TypeScript vanilla (sem framework, SPA)
- Dev server: porta 5173
- API URL:
http://localhost:3333/api - Token storage:
localStorage('@gastrobar:token')elocalStorage('@gastrobar:user')
Estrutura do Projeto
app_ces/
├── .env # JWT_SECRET
├── package.json # Backend dependencies
├── tsconfig.json
├── prisma/
│ ├── schema.prisma # Schema do banco (SQLite)
│ ├── seed.ts # Seed: admin@gastrobar.com / 123456
│ └── dev.db # Banco SQLite (gerado)
├── src/ # Backend API
│ ├── server.ts # Entrypoint Fastify, porta 3333
│ ├── lib/
│ │ ├── prisma.ts # Instância PrismaClient
│ │ └── timezone.ts # Helpers UTC-3 (dateStart, dateEnd, dateTime, nowUTC3)
│ ├── middlewares/
│ │ └── auth.middleware.ts # authenticate + requireRoles
│ ├── controllers/
│ │ ├── auth.controller.ts
│ │ ├── category.controller.ts
│ │ ├── product.controller.ts
│ │ ├── order.controller.ts
│ │ ├── user.controller.ts
│ │ └── report.controller.ts
│ ├── services/
│ │ ├── auth.service.ts
│ │ ├── category.service.ts
│ │ ├── product.service.ts
│ │ ├── order.service.ts
│ │ ├── user.service.ts
│ │ └── report.service.ts
│ ├── schemas/ # Validação Zod
│ │ ├── auth.schema.ts
│ │ ├── user.schema.ts
│ │ ├── product.schema.ts
│ │ ├── category.schema.ts
│ │ └── order.schema.ts
│ └── routes/
│ ├── auth.routes.ts
│ ├── category.routes.ts
│ ├── product.routes.ts
│ ├── order.routes.ts
│ ├── user.routes.ts
│ └── report.routes.ts
└── web/ # Frontend SPA
├── package.json # Vite + TypeScript
├── tsconfig.json
└── src/
├── main.ts # Router SPA + navegação
├── api.ts # Cliente HTTP centralizado
├── utils.ts # renderNavbar, toast, helpers, toDisplayDate, parseDisplayDate
├── style.css # Dark theme + glassmorphism
└── views/
├── login.ts
├── kitchen.ts # Painel de preparo
├── orders.ts # Gerenciamento de comandas
├── checkout.ts # Caixa/pagamento
├── menu.ts # Cardápio CRUD
├── users.ts # Gestão de equipe
└── dashboard.ts # KPIs admin
Banco de Dados (Schema Prisma)
Models
Usuario
id(UUID),nome,email(unique),senha(hash bcrypt),role(ADMIN|CAIXA|GARCOM|COZINHA|BARMAN),ativo,criadoEm- Relations:
comandas(1:N Comanda),itensPedido(1:N ItemPedido)
Categoria
id(UUID),nome,produtos(relation 1:N Produto)
Produto
id(UUID),nome,descricao?,fotoUrl?,preco(Float),custo?,estoqueAtual,estoqueMinimo,itemCozinha(boolean: true=cozinha, false=bar),ativo,categoriaId(FK)
Comanda
id(UUID),identificador(mesa/código),nomeCliente?,status(ABERTA|FECHADA|PAGA|CANCELADA),total,valorPago,desconto,acrescimo,taxaServico(boolean, +10%),formaPagamento?,motivoCancelamento?,criadoEm,fechadoEm?usuarioId?(FK) - Usuário que abriu a comanda- Relations:
itens(1:N ItemPedido),pagamentos(1:N Pagamento),usuario(N:1 Usuario)
ItemPedido
id(UUID),quantidade,precoUnitario,observacao?,status(PENDENTE|PREPARANDO|PRONTO|ENTREGUE),produtoId(FK),comandaId(FK),criadoEmusuarioId?(FK) - Usuário que adicionou o item- Relations:
produto(N:1 Produto),comanda(N:1 Comanda),usuario(N:1 Usuario)
Pagamento
id(UUID),comandaId(FK),valor,formaPagamento(DINHEIRO|CARTAO_CREDITO|CARTAO_DEBITO|PIX),observacao?,estornado(boolean, default false),criadoEm
Rotas da API
Auth (/api/auth)
| Método | Rota | Permissão | Descrição |
|---|---|---|---|
| POST | /auth/login |
Público | Login, retorna JWT + user |
| PATCH | /auth/senha |
Autenticado | Alterar própria senha |
Categorias (/api)
| Método | Rota | Permissão |
|---|---|---|
| GET | /categorias |
Autenticado |
| GET | /categorias/:id |
Autenticado |
| POST | /categorias |
ADMIN |
| PUT | /categorias/:id |
ADMIN |
| DELETE | /categorias/:id |
ADMIN |
Produtos (/api)
| Método | Rota | Permissão |
|---|---|---|
| GET | /produtos |
Autenticado |
| GET | /produtos/mais-vendidos |
Autenticado |
| GET | /produtos/busca |
Autenticado |
| GET | /produtos/baixo-estoque |
ADMIN |
| GET | /produtos/:id |
Autenticado |
| POST | /produtos |
ADMIN |
| PUT | /produtos/:id |
ADMIN |
| DELETE | /produtos/:id |
ADMIN |
Comandas (/api)
| Método | Rota | Permissão |
|---|---|---|
| GET | /comandas |
Autenticado |
| GET | /pagamentos |
Autenticado |
| POST | /comandas |
GARCOM/CAIXA/ADMIN |
| POST | /comandas/:id/itens |
GARCOM/CAIXA/ADMIN |
| PATCH | /comandas/:id |
GARCOM/CAIXA/ADMIN |
| POST | /comandas/:id/pagar |
CAIXA/ADMIN |
| POST | /comandas/:id/pagamentos |
CAIXA/ADMIN |
| PATCH | /pagamentos/:id/estornar |
CAIXA/ADMIN |
| PATCH | /comandas/itens/:id/observacao |
GARCOM/CAIXA/ADMIN |
| DELETE | /comandas/itens/:id |
CAIXA/ADMIN |
Painel Cozinha/Bar (/api)
| Método | Rota | Permissão |
|---|---|---|
| GET | /painel/itens |
COZINHA/BARMAN/ADMIN |
| PATCH | /painel/itens/:id/status |
COZINHA/BARMAN/ADMIN |
Relatórios (/api)
| Método | Rota | Permissão |
|---|---|---|
| GET | /relatorios/vendas |
ADMIN |
| GET | /relatorios/dashboard |
ADMIN |
Usuários (/api)
| Método | Rota | Permissão |
|---|---|---|
| GET | /usuarios |
ADMIN |
| POST | /usuarios |
ADMIN |
| PATCH | /usuarios/:id |
ADMIN |
| PATCH | /usuarios/:id/senha |
ADMIN |
| DELETE | /usuarios/:id |
ADMIN |
Roles e Permissões
| Role | Descrição | Acesso Principal |
|---|---|---|
ADMIN |
Administrador | Acesso total ao sistema |
CAIXA |
Operador de caixa | Comandas, checkout, pagamento, exclusão de itens |
GARCOM |
Garçom | Criar/gerenciar comandas, adicionar itens |
COZINHA |
Cozinheiro | Painel de preparo (itens itemCozinha=true) |
BARMAN |
Bartender | Painel de preparo (itens itemCozinha=false) |
Lógica de Negócio Importante
Fluxo de Comanda
- Criar comanda (identificador = mesa/código) - registra usuário logado
- Adicionar itens (baixa estoque automaticamente, atualiza total) - registra usuário logado
- Itens aparecem no painel de cozinha/bar com status
PENDENTE(apenas últimos 12h) - Cozinha/bar avança:
PENDENTE → PREPARANDO → PRONTO → ENTREGUE - Caixa fecha com pagamento (integral ou parcial)
- Status da comanda:
ABERTA → PAGA
Pagamento
- Integral:
POST /comandas/:id/pagar- registra pagamento do restante e fecha comanda - Parcial:
POST /comandas/:id/pagamentos- registra pagamento parcial, pode selecionar itens específicos - Cálculo do total final:
total - desconto + acrescimo + (taxaServico ? 10% : 0) - Formas:
DINHEIRO,CARTAO_CREDITO,CARTAO_DEBITO,PIX - Estorno:
PATCH /pagamentos/:id/estornar- marca pagamento como estornado, subtrai valor decomanda.valorPago, se comanda eraPAGAvolta paraFECHADA. Pagamento continua visível com indicador visual (opacity + line-through + badge "ESTORNADO")
Estoque
- Decrementa ao adicionar item na comanda
- Incrementa ao remover item da comanda
- Permite estoque negativo (sem validação de insuficiência)
Frontend
- Rotas SPA manuais via
window.history.pushState - Auto-refresh no painel de cozinha a cada 10 segundos
- Notificações sonoras via Web Audio API para novos pedidos
- Navbar dinâmica baseada no role do usuário logado
- Filtros de data em formato
dd/mm/yyyy(inputstype="text", conversão viaparseDisplayDate)
Comandos Úteis
# Backend
npm run dev # Iniciar dev server (tsx watch)
npm run build # Compilar TypeScript
npm run db:push # Aplicar schema no banco
npm run db:seed # Popular banco com dados iniciais
# Frontend
cd web && npm run dev # Iniciar Vite dev server
cd web && npm run build # Build de produção
Credenciais Padrão (Seed)
- Email: admin@gastrobar.com
- Senha: 123456
- Role: ADMIN
Notas Técnicas
- CORS habilitado para qualquer origem (
origin: '*') - JWT secret definido em
.env(fallback:fallback_secret_change_me) - Error handler global trata
ZodError(400) e erros HTTP - Fuso horário UTC-3 configurado em
src/lib/timezone.ts- todas as datas no backend e frontend usam offset UTC-3 - Frontend não usa framework UI - renderiza HTML via template literals
- Utiliza
event delegationpara botões dinâmicos no painel - Cleanup de listeners ao navegar entre views