Tela 1Estoque por franquia
Rota /estoque · escrita liberada · visível para a categoria Franquia (travada na própria) e para Administrador (todas, com simulação pelo seletor da sidebar)
A tela mostra, item por item, quanto a franquia tem em mãos, quanto está na lavanderia, e compara esse total com o mínimo e o ideal calculados a partir dos imóveis que ela atende. É a única das duas telas onde alguém digita algo.
Por que duas posições e não uma Peça na lavanderia continua sendo da franquia, mas não está disponível para troca. Somar tudo numa coluna esconderia justamente o problema que a tela existe para mostrar.
Como o saldo é formado
Nada é gravado como “posição atual”. O que existe é um ledger — a tabela
enxoval.estoque_movimentacoes — e as posições saem da soma dos lançamentos.
Cada linha guarda o efeito que teve nas duas posições no momento em que foi feita.
| Lançamento | O que é | Atual | Lavanderia |
|---|---|---|---|
| Entrada | Compra ou reposição chegando na franquia | + qtd | — |
| Saída p/ lavanderia | Peças que saíram para uso ou lavagem | − qtd | + qtd |
| Retorno da lavanderia | Peças que voltaram para o estoque | + qtd | − qtd |
| Estragou / baixa | Dano, perda ou descarte definitivo | − resto | − o que houver |
Por que o efeito fica gravado na linha A baixa depende do estado do momento: se havia 5 peças na lavanderia e se dá baixa de 8, são 5 da lavanderia e 3 do estoque. Recalcular isso depois, de trás para frente, daria número diferente — então cada linha carrega o próprio efeito, e a gravação acontece em transação com trava por franquia e item.
Mínimo e ideal: de onde vêm os números
Não são digitados. O app roda o motor de regras por imóvel da franquia — um por um, não a partir de uma soma de camas — e acumula o resultado.
Mínimo = enxoval de 2 trocas · Ideal = enxoval de 3 trocas Somados imóvel por imóvel, porque jacuzzi conta presença (um ou quatro dá o mesmo acréscimo) e o cluster depende da cidade de cada imóvel.
Status de cada item
Item que não tem regra cadastrada em Configuração Itens aparece com mínimo e ideal em branco: dá para movimentar, mas o app não tem como dizer se está faltando.
O gatilho do kanban
Pedido que entra na fase Enviado vira entrada no estoque da franquia, sem ninguém lançar. A regra é declarativa, e é isso que a torna segura:
As entradas do pedido X existem se e somente se o pedido X está em Enviado. Mover duas vezes, mover em duas abas, editar quantidade, voltar de fase, excluir o card — tudo converge para o mesmo resultado, sem lançamento dobrado.
Um reconciliador roda a cada 15 minutos (e pode ser chamado à mão em
POST /api/estoque/reconciliar). Ele acerta o que estiver fora do lugar e
apaga linhas de sistema órfãs — de pedido que saiu de Enviado ou que não existe mais.
Franquia sem vínculo A franquia é casada com o Sapron por nome normalizado. Quando o vínculo não resolve, a pessoa vê uma tela de pendência em vez do estoque de todas as franquias, e a tela de Usuários destaca o cadastro incompleto.
Tela 2Enxoval por Imóvel
Rota /estoque-imoveis · somente leitura · visível apenas para Administrador
Aqui a pergunta é histórica: quanto enxoval este imóvel já recebeu, quando foi a última compra, quanto foi perdido em dano de hóspede e o que sobra hoje. Nada é editável — a tela compõe três fontes e mostra a conta.
A regra que evita contar duas vezes
Desde 13/08/2026 o app é a fonte da verdade de compra de enxoval. O Sapron virou histórico: compra registrada lá depois de 31/01/2026 não conta, aconteça o que acontecer com a origem. Do Sapron segue vivo apenas o dano de hóspede.
Na virada, as duas fontes se sobrepõem em janeiro. Quando existe pedido do app em Enviado a até 30 dias de uma compra do Sapron, as duas são o mesmo evento — e vale a do app, que é itemizada e tem valor. Pedido cancelado não suprime nada: cancelado não é compra.
Tamanho real do problema A sobreposição afeta 217 de 10.063 peças de janeiro de 2026 — 2,2%. O consolidado total é de 716.387 peças. As linhas descartadas aparecem riscadas no detalhe do imóvel, fora da soma, para quem quiser auditar.
Saldo estimado por item
compras do Sapron + pedidos enviados no app − danos com resolução “Repor item” É estimativa, não contagem física: ninguém confere peça por peça no imóvel. Serve para enxergar o imóvel que ficou descoberto.
O que a listagem oferece
- Busca por código, franquia ou cidade, com ordenação nas colunas e páginas de 100.
- Peças compradas no consolidado, com a quebra
sapron + appao lado. - Data da última compra, quantidade e valor de danos, e nº de pedidos do app.
- Exportação em CSV da tabela inteira.
- Detalhe por imóvel com o saldo item a item e a lista de danos.
De quanto em quanto tempo atualiza Os espelhos do Sapron (imóveis, franquias, proprietários e danos) são sincronizados a cada 4 horas. As compras ficam fora desse ciclo de propósito — são retrato de um histórico encerrado, recarregado só sob demanda.
ReferênciaOnde cada coisa mora
| Assunto | Onde |
|---|---|
| Regras puras do estoque (deltas, validação, status) | src/lib/estoque-utils.ts |
| Ledger, gatilho do kanban e reconciliação | src/lib/estoque-db.ts |
| Mínimo e ideal por franquia | src/lib/demanda-franquia.ts |
| Consolidação de compras e dedupe | src/lib/compras-consolidadas.ts |
| Tela de Estoque | src/components/estoque/, /api/estoque |
| Tela de Enxoval por Imóvel | src/app/(dashboard)/estoque-imoveis/ |
| Quem vê o quê | src/lib/visao-core.ts — verEstoque, verEstoquePorImovel |
| Reconciliar à mão | POST /api/estoque/reconciliar (Bearer) |