08-caixa-despesas

star 0

💰 MÓDULO CAIXA DIÁRIO E DESPESAS. Obrigatório consultar antes de mexer no CaixaDiario, Despesas do Motorista, conferência de caixa, relatório de prestação de contas ou classificação debitaCaixa.

clark-neitzel By clark-neitzel schedule Updated 3/5/2026

name: 08-caixa-despesas description: "💰 MÓDULO CAIXA DIÁRIO E DESPESAS. Obrigatório consultar antes de mexer no CaixaDiario, Despesas do Motorista, conferência de caixa, relatório de prestação de contas ou classificação debitaCaixa."

08 MÓDULO CAIXA DIÁRIO E DESPESAS

⚠️ Este módulo cobre o controle financeiro diário do motorista: adiantamento, despesas de rota, recebimentos por entrega e prestação de contas.


1. CONCEITOS FUNDAMENTAIS

O que é o Caixa Diário

Cada motorista tem um CaixaDiario por dia (vendedorId + dataReferencia UNIQUE). Este caixa:

  • Registra o adiantamento recebido ao sair
  • Agrega todas as despesas da rota do dia
  • Agrega todos os pagamentos recebidos nas entregas
  • Calcula o valor a prestar ao voltar
Valor a Prestar = Adiantamento + Total Recebido em Caixa - Total Despesas

Status do CaixaDiario

Status Descrição
ABERTO Dia em andamento, motorista pode editar despesas
FECHADO Motorista fechou — snapshot dos totais gerado
CONFERIDO Admin financeiro conferiu e aprovou

2. SISTEMA DE CLASSIFICAÇÃO debitaCaixa (CRÍTICO)

O que define se um pagamento "debita o caixa"

O campo debitaCaixa na TabelaPreco define se um tipo de pagamento é recebido pelo motorista em espécie (entra no caixa dele) ou não:

Tipo de Pagamento debitaCaixa Lógica
Dinheiro, PIX true Motorista recebeu — deve prestar contas
Boleto, Prazo false Cobrado depois — não passa pelo caixa
Fiado Escritório false Escritório assume — não vai para o caixa
Fiado Vendedor true Vendedor responsável — entra no caixa dele

Lógica de Classificação (Hierarquia)

// 1. Escritório assumindu? → NÃO debita
if (pagamento.escritorioResponsavel) { debitaCaixa = false; }
// 2. Vendedor responsável? → DEBITA
else if (pagamento.vendedorResponsavelId) { debitaCaixa = true; }
// 3. Nome do pagamento real na TabelaPreco? → Usar campo debitaCaixa da tabela
else if (mapaCondicoesPorNome[pagamento.formaPagamentoNome] !== undefined) {
    debitaCaixa = mapaCondicoesPorNome[pagamento.formaPagamentoNome];
}
// 4. Fallback: condição original do pedido
else { debitaCaixa = condicaoInfo?.debitaCaixa || false; }

IMPORTANTE: A classificação deve ser feita pelo nome do pagamento REAL que o motorista registrou no checkout (formaPagamentoNome), nunca pela condição original do pedido, pois o cliente pode ter pagado diferente do previsto.


3. DESPESAS

Categorias de Despesa

MERCADORIA_EMPRESA   — Compra de mercadoria para venda pelo escritório
COMBUSTIVEL          — Abastecimento (campos extras: litros, kmNoAbastecimento, veiculoId)
PEDAGIO_BALSA        — Pedágio ou balsa
HOTEL_HOSPEDAGEM     — Hospedagem durante a rota
MANUTENCAO_VEICULO   — Reparo/troca de peças (campo extra: tipoManutencao)
OUTRO                — Outros

Campos Especiais por Categoria

// COMBUSTIVEL
{ veiculoId: uuid, litros: 40.5, kmNoAbastecimento: 150000 }

// MANUTENCAO_VEICULO
{ tipoManutencao: 'PNEU' | 'LAMPADA' | 'OLEO' | 'FILTRO' | 'OUTRO' }

Regras de Edição

  • Motorista pode editar despesas enquanto o caixa estiver ABERTO
  • Admin (Pode_Editar_Caixa) pode editar qualquer despesa a qualquer momento

4. PERMISSÕES DO MÓDULO

{
  "Pode_Acessar_Caixa": false,   // Acessar, visualizar e lançar próprias despesas
  "Pode_Editar_Caixa": false     // Auditor: ver caixas alheios, editar, conferir
}

Acesso por Perfil

Ação Motorista (Pode_Acessar_Caixa) Auditor (Pode_Editar_Caixa) Admin
Ver próprio caixa
Lançar despesas próprias
Ver caixa de outros
Editar despesas alheias
Conferir/Fechar caixa
Marcar entrega como conferida

5. ENDPOINTS (/api/caixa e /api/despesas)

Caixa

Método Path Permissão Descrição
GET /api/caixa/resumo?data=&vendedorId= Acesso Resumo completo do dia (cria se não existir)
PATCH /api/caixa/adiantamento Acesso Define adiantamento do dia
POST /api/caixa/fechar Acesso Fecha o caixa com snapshot dos totais
POST /api/caixa/conferir Editor Admin confere e aprova o caixa
PATCH /api/caixa/entrega-conferir Editor Marca entrega individual como conferida
GET /api/caixa/relatorio?data=&vendedorId= Acesso Dados completos para relatório A4

Despesas

Método Path Descrição
GET /api/despesas?data=&vendedorId= Lista despesas do dia
POST /api/despesas Cria nova despesa
PUT /api/despesas/:id Edita despesa (regras de caixa aberto)
DELETE /api/despesas/:id Exclui despesa (regras de caixa aberto)

6. ESTRUTURA DO SCHEMA PRISMA

model Despesa {
  id             String   @id @default(uuid())
  vendedorId     String
  dataReferencia String   // YYYY-MM-DD
  categoria      String
  descricao      String?
  valor          Decimal  @db.Decimal(12, 2)
  
  // Combustivel
  veiculoId         String?
  litros            Decimal? @db.Decimal(10, 3)
  kmNoAbastecimento Int?
  
  // Manutencao
  tipoManutencao    String?
  
  criadoPor  String
  // ... timestamps
}

model CaixaDiario {
  id             String   @id @default(uuid())
  vendedorId     String
  dataReferencia String   // YYYY-MM-DD
  
  adiantamento   Decimal  @default(0)
  status         String   @default("ABERTO") // ABERTO | FECHADO | CONFERIDO
  
  // Snapshots ao fechar
  totalDespesas       Decimal?
  totalRecebidoCaixa  Decimal?    // Só o que debita caixa
  totalRecebidoOutros Decimal?    // Boleto, prazo, etc.
  valorAPrestar       Decimal?    // adiantamento + recebidoCaixa - despesas
  
  conferidoPor String?
  conferidoEm  DateTime?
  obsAdmin     String?
  
  @@unique([vendedorId, dataReferencia])
}

model CaixaEntregaConferida {
  caixaDiarioId String
  pedidoId      String
  conferido     Boolean
  conferidoPor  String?
  conferidoEm   DateTime?
  
  @@unique([caixaDiarioId, pedidoId])
}

Migrações SQL a adicionar em migrationService.js

CREATE TABLE IF NOT EXISTS "despesas" (
  "id" TEXT PRIMARY KEY,
  "vendedor_id" TEXT NOT NULL,
  "data_referencia" TEXT NOT NULL,
  "categoria" TEXT NOT NULL,
  "descricao" TEXT,
  "valor" DECIMAL(12,2) NOT NULL,
  "veiculo_id" TEXT,
  "litros" DECIMAL(10,3),
  "km_no_abastecimento" INTEGER,
  "tipo_manutencao" TEXT,
  "criado_por" TEXT NOT NULL,
  "created_at" TIMESTAMP DEFAULT NOW(),
  "updated_at" TIMESTAMP DEFAULT NOW()
);

CREATE TABLE IF NOT EXISTS "caixa_diario" (
  "id" TEXT PRIMARY KEY,
  "vendedor_id" TEXT NOT NULL,
  "data_referencia" TEXT NOT NULL,
  "adiantamento" DECIMAL(12,2) DEFAULT 0,
  "status" TEXT DEFAULT 'ABERTO',
  "total_despesas" DECIMAL(12,2),
  "total_recebido_caixa" DECIMAL(12,2),
  "total_recebido_outros" DECIMAL(12,2),
  "valor_a_prestar" DECIMAL(12,2),
  "conferido_por" TEXT,
  "conferido_em" TIMESTAMP,
  "obs_admin" TEXT,
  "created_at" TIMESTAMP DEFAULT NOW(),
  "updated_at" TIMESTAMP DEFAULT NOW(),
  UNIQUE("vendedor_id", "data_referencia")
);

CREATE TABLE IF NOT EXISTS "caixa_entrega_conferida" (
  "id" TEXT PRIMARY KEY,
  "caixa_diario_id" TEXT NOT NULL,
  "pedido_id" TEXT NOT NULL,
  "conferido" BOOLEAN DEFAULT FALSE,
  "conferido_por" TEXT,
  "conferido_em" TIMESTAMP,
  "created_at" TIMESTAMP DEFAULT NOW(),
  "updated_at" TIMESTAMP DEFAULT NOW(),
  UNIQUE("caixa_diario_id", "pedido_id")
);

ALTER TABLE "tabela_precos" ADD COLUMN IF NOT EXISTS "debita_caixa" BOOLEAN DEFAULT FALSE;

7. CÁLCULO DO RESUMO DO CAIXA

O endpoint GET /api/caixa/resumo retorna:

{
  "caixa": { "id", "status", "adiantamento", "dataReferencia", "obsAdmin" },
  "diario": { "veiculoId", "placa", "kmInicial", "kmFinal", "totalKm" },
  "mediaCombustivel3Meses": 12.5,
  "despesas": [...],
  "totalDespesas": 150.00,
  "entregas": [...],
  "contagens": { "totalEntregas": 10, "entregues": 7, "parciais": 2, "devolvidos": 1 },
  "totalRecebidoCaixa": 800.00,   // Só o que debita caixa (dinheiro, pix)
  "totalRecebidoOutros": 200.00,  // Boletos, prazo
  "totalRecebido": 1000.00,
  "detalhamentoCaixa": [{ "condicao", "valor", "debitaCaixa" }],
  "valorAPrestar": 750.00
}

8. MÉDIA DE CONSUMO DE COMBUSTÍVEL

O sistema calcula automaticamente a média de consumo do veículo (km/L) dos últimos 3 meses usando os registros de despesa do tipo COMBUSTIVEL:

// Lógica: calcular km percorridos entre abastecimentos
// (km do abastecimento N - km do abastecimento N-1) / litros do abastecimento N
totalKm / totalLitros = km/L

Esta métrica é exibida no resumo do caixa para ajudar na identificação de anomalias (ex: veículo com consumo muito alto pode indicar roubo ou problema mecânico).


9. PÁGINAS DO MODULO (FRONTEND)

Arquivo Rota Descrição
CaixaDiarioPage.jsx /caixa Dashboard principal do caixa do dia
DespesasPage.jsx /despesas Lançamento de despesas
RelatorioCaixaPrint.jsx /caixa/impressao Relatório A4 para impressão
Install via CLI
npx skills add https://github.com/clark-neitzel/cahardt --skill 08-caixa-despesas
Repository Details
star Stars 0
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator
clark-neitzel
clark-neitzel Explore all skills →