Construa um app de inventário com ferramentas de IA que nunca fica negativo
Construa um app de inventário com ferramentas de IA e evite estoque negativo usando regras simples de receber, vender e ajustar que você pode testar antes de colocar em produção.

O que estamos construindo e por que o estoque dá errado
Quando as pessoas dizem que um app de inventário está 'errado', normalmente querem dizer uma coisa: o estoque fica negativo. Depois disso, qualquer relatório vira suspeito. Você não sabe se está sem estoque, se houve oversell, ou se o número é só resultado de uma atualização ruim.
O objetivo não é um ERP completo. É um app pequeno e simples que você pode rodar para uma loja ou um catálogo reduzido em que a contagem permaneça confiável. Mesmo usando ferramentas de IA para gerar telas e código, as regras vêm primeiro.
Mantenha o sistema com três ações:
- Receive: entrada de estoque (entrega, devolução, estoque inicial).
- Sell: saída de estoque (venda, envio, ou qualquer uso de saída).
- Adjust: alteração porque a realidade e o sistema discordam (danos, perda, recontagem).
'Correto' é fácil de definir e testar:
- O estoque nunca fica abaixo de zero para nenhum item.
- Toda mudança tem uma razão e um timestamp.
- Totais são reproduzíveis: reproduza a história e você obtém o mesmo número.
- Qualquer número pode ser explicado apontando as transações que o criaram.
O estoque normalmente dá errado por duas razões:
-
O app edita um campo de 'estoque atual' diretamente em vez de registrar uma transação.
-
Ele registra transações, mas esquece de checar o que está disponível no momento da venda.
Um exemplo rápido: você tem 5 canecas. Duas pessoas compram 4 canecas cada, com minutos de diferença. Se seu app não bloquear o passo de checar e decrementar, ambas as vendas podem passar na verificação de 'em estoque' e você termina com -3.
O menor modelo de dados que ainda funciona
Comece com um modelo de dados difícil de bagunçar. O objetivo principal é tornar impossível 'editar o número do estoque' diretamente. O estoque disponível deve ser um total calculado a partir de um histórico de mudanças.
Para a maioria dos apps pequenos de inventário, o conjunto mínimo é:
- Product: o que você rastreia (nome, SKU, flag de ativo).
- Transaction: toda mudança de estoque (receive, sell, adjust), com quantidade e timestamp.
- Location (opcional): onde o estoque fica (armazém, loja, van). Adicione só se precisar agora.
- User (opcional): quem criou a transação.
Uma regra chave: não armazene 'stock on hand' como um campo livre que possa ser editado. Compute-o como a soma de todas as quantidades das transações para aquele produto (e localização, se usar locais). Isso mantém o número explicável porque cada unidade tem uma razão.
Local único agora, múltiplos depois
Se você não tem certeza sobre múltiplas localizações, escolha um caminho seguro:
- Single location v1: sem tabela Location, totais por produto.
- Future-ready v1: inclua Location e exija
location_idem toda transação, mas comece com uma localização padrão.
Evite 'local opcional' nas transações. Valores nulos em location quase sempre criam totais confusos depois.
IDs e timestamps em que você vai confiar
Decida isso desde o início:
- Use IDs únicos para Product e Transaction.
- Armazene
created_atem toda transação. - Mantenha um
effective_atseparado só se precisar de correções retroativas. - Não edite quantidades in-loco. Adicione uma nova transação corretiva.
Exemplo: se você contou 10 unidades ontem mas digitou 8, não sobrescreva 8 para 10. Adicione um ajuste de +2 com uma nota.
Regras que impedem o estoque de ficar negativo
As regras importam mais que a UI. A maioria dos bugs de estoque negativo acontece porque o app 'edita o número' em vez de registrar o que aconteceu.
Pense no inventário como uma conta bancária: você só muda através de transações registradas, e nunca permite que o saldo fique abaixo de zero.
Aqui estão as regras que carregam a maior parte do peso:
- O estoque nunca deve ficar abaixo de zero após qualquer transação.
- Toda mudança cria um registro de transação (sem edições silenciosas).
- Quantidades são números positivos; o tipo de transação decide soma ou subtração.
- Qualquer ajuste precisa de uma razão e de quem o fez.
- Proteja contra duas ações acontecendo ao mesmo tempo.
O número 'on hand' não é a fonte de verdade. O histórico de transações é.
O que 'sem estoque negativo' significa na prática
Antes de salvar uma transação que remove itens, o app checa a quantidade atual disponível para esse item. Se a remoção tornaria o total negativo, rejeite-a (ou marque como pendente, se você suportar esse fluxo intencionalmente).
Concorrência é a armadilha. Duas pessoas vendem a última unidade ao mesmo tempo. Ambas as telas mostram '1 disponível', ambas clicam em Salvar, e você termina com -1 a menos que o banco de dados imponha a checagem. O padrão seguro é ler o estoque atual e gravar a transação em uma única operação de banco de dados, assim só uma consegue ter sucesso.
Recebendo estoque: o fluxo mais simples e seguro
O recebimento é onde seus números começam. Se você registrar uma entrega errado, toda venda depois parecerá errada também.
Mantenha o formulário de recebimento pequeno para que as pessoas realmente usem. Um mínimo sólido é produto, quantidade e timestamp, mais uma nota opcional. Custo e fornecedor podem esperar, a menos que você saiba que precisa deles.
A validação deve ser rígida e entediante: quantidade maior que 0, o produto deve existir e o timestamp deve ser registrado. Se qualquer checagem falhar, não atualize nada.
Ao salvar, crie uma transação RECEIVE. Se você também mantiver um valor em cache de on-hand por performance, atualize-o na mesma transação de banco de dados para não acabar com números desencontrados.
Caso de borda: recebimento duplicado
Duplicatas acontecem quando alguém clica duas vezes em Salvar ou re-digita um recebimento já registrado. Não delete a linha antiga. Corrija com histórico.
Exemplo: você quis receber 10 unidades, mas registraram duas vezes. Adicione um ajuste reversor (ou uma ação de anulação, se suportar) que subtraia 10 e referencie o recebimento errado na nota.
Vendendo estoque: checagens que previnem overselling
Vender é onde apps de inventário normalmente quebram.
Para cada linha de venda, capture produto (ou SKU), quantidade e timestamp. Preço e cliente são opcionais, mas uma nota curta ajuda quando alguém perguntar 'Por que o estoque caiu?'.
Duas checagens importam:
- Quantidade maior que 0.
- Quantidade menor ou igual ao estoque disponível no momento da venda.
Se alguém tenta vender 7 quando só há 5 disponíveis, bloqueie. Mostre uma mensagem que ajude a agir, tipo: 'Apenas 5 em estoque. Reduza a quantidade ou receba estoque primeiro.' Evite erros vagos.
Atualizando estoque sem perder histórico
Não sobrescreva um número de 'stock on hand' diretamente. Registre uma linha de transação para a venda (um movimento subtrativo) vinculada ao produto e ao registro da venda. O on-hand continua sendo a soma de todos os movimentos.
Backorders podem esperar na v1. A escolha mais segura no início é não permiti-los. Se for necessário suportá-los, mantenha-os como um status separado que não altera o estoque até que os itens realmente sejam enviados.
Ajustes: correções sem quebrar seu histórico
Ajustes são para a vida real: mercadoria danificada, roubo, recontagem ou limpeza de dados após uma importação. O objetivo é consertar o número on-hand de hoje sem fingir que o passado não existiu.
A maioria dos apps pequenos só precisa de dois estilos de ajuste:
- Set-to: 'Definir on-hand igual a X' (melhor após uma contagem física).
- Delta: 'Adicionar ou remover N' (melhor para um evento conhecido como '-2 danificados').
Valide ajustes como qualquer outro movimento de estoque:
- Valor set-to deve ser 0 ou maior.
- Delta pode ser positivo ou negativo, mas não pode empurrar o on-hand para abaixo de 0.
- Item e localização (se você rastrear locais) devem ser selecionados.
- O horário do ajuste deve ser registrado.
Torne os ajustes responsáveis. Exija uma razão e quem fez a mudança. Mantenha razões curtas e consistentes (recontagem, dano, roubo, limpeza de dados), com uma nota opcional.
Passo a passo: como pedir a uma ferramenta de IA para construir com segurança
Se você usa ferramentas de IA para construir o app, seja explícito sobre o que a ferramenta deve produzir: um esquema de BD, lógica no servidor e alguns testes básicos. Se você pedir só telas, muitas vezes acaba com regras aplicadas apenas na UI, que é exatamente como o estoque negativo entra.
Descreva o modelo de dados em inglês simples. Nomeie os tipos de transação (receive, sell, adjust) e o que cada um deve armazenar: item, quantidade, timestamp, nota. Peça por uma tabela de transações imutável e um jeito claro de calcular o estoque atual a partir do ledger.
As regras devem viver no servidor
Peça validação no servidor e deixe claro que a UI não é confiável.
Um conjunto útil de prompts (ajuste ao seu stack):
- 'Generate DB tables for items and inventory_transactions. Transactions are append-only.'
- 'Create one server function applyTransaction(type, itemId, qty, note) that validates and writes in a DB transaction.'
- 'Rules: receive adds stock, sell subtracts stock but must not allow stock < 0, adjust can add or subtract but also must not allow stock < 0.'
- 'Add API endpoints that call applyTransaction. UI is just a client and cannot bypass rules.'
- 'Generate 3 seed items and 8 sample transactions that demonstrate a blocked oversell.'
Peça também uma visão de auditoria: uma página que mostra a lista de transações (mais novas primeiro) e o estoque atual ao lado de cada item. Essa tela é onde você detecta estranhezas rapidamente.
Checagens rápidas antes de confiar nos números
Antes de usuários reais usarem, prove que as regras funcionam com alguns testes simples. Bugs de inventário escondem-se até a sequência exata acontecer.
Escreva de 5 a 10 casos de teste em linguagem simples. Por exemplo:
- Receive 10, sell 3, confirmar on-hand 7.
- Tentar vender 8 quando só 7 estão disponíveis, confirmar que é bloqueado com mensagem clara.
- Receive 1, sell 1, depois vendem 1 novamente, confirmar que a segunda venda é bloqueada.
- Fazer uma recontagem (set-to), confirmar que a matemática e o histórico continuam fazendo sentido.
Depois teste 'duas vendas ao mesmo tempo' para o mesmo item. Abra o item em duas abas (ou dois dispositivos) e tente vender a última unidade quase ao mesmo tempo. Você deve ver uma venda bem-sucedida e a outra falhando.
Mensagens claras importam. 'Não é possível vender: apenas 2 disponíveis' é útil. 'Erro 500' não é.
Finalmente, decida sua política de desfazer. Escolha uma e mantenha:
- Soft delete (ocultar uma transação, manter para auditoria), ou
- Transações de reversão (adicionar uma entrada oposta para desfazer), que costuma ser mais seguro.
Erros comuns que ainda causam estoque negativo
A maioria dos problemas não é erro de matemática. Vem de atalhos que parecem ok numa demo e quebram quando duas pessoas usam o app.
A maior armadilha é tratar 'stock on hand' como um único número editável. Parece simples, mas tira a única coisa que você precisa depois: uma história clara de como o número mudou.
Os reincidentes:
- Manter estoque como campo editável sem log de transações.
- Validar apenas no frontend.
- Ignorar concorrência.
- Permitir ajustes temporariamente negativos.
- Não ter trilha de auditoria quando alguém pergunta por que um item mostra -3.
Um pequeno cenário que mostra a falha: você tem 5 unidades. Dois funcionários vendem 3 cada ao mesmo tempo. Se seu app checa 'estoque >= 3' no navegador, ambas as telas veem 5 e passam. Cada uma grava sua atualização e você acaba com -1. Conserte impondo a regra no servidor e fazendo a checagem e a gravação de forma atômica.
Outro problema fácil de perder é misturar edição de produto (nome, SKU, preço) com edição de estoque no mesmo formulário. Isso convida alterações acidentais de estoque e totais difíceis de explicar.
Exemplo: um item desde a entrega até a venda e recontagem
Imagine uma pequena loja com uma localização e três itens: Coffee Beans (1 lb bag), Paper Filters e Oat Milk. Siga só um item, Coffee Beans.
Início do dia: on hand = 0.
Um app de inventário seguro armazena uma lista de transações e calcula o número atual a partir desse histórico.
Aqui está o dia com saldo corrido:
- Receive +10: on hand vai de 0 para 10.
- Sell -3: on hand vai de 10 para 7.
- Adjust -2 (dano): on hand vai de 7 para 5.
- Tentativa de vender -6: o app bloqueia porque 5 - 6 seria -1.
Quando o caixa tenta essa última venda, o app deve impedir antes de salvar qualquer coisa. Mantenha a mensagem simples: 'Estoque insuficiente. Você tem 5, tentando vender 6.' O importante é que a checagem aconteça no momento da transação.
Agora a recontagem: alguém conta 5 sacos, que bate com o saldo. Nada precisa ser 'consertado' porque o ledger já explica o número: um recebimento, uma venda, um ajuste por dano.
Próximos passos: lançar a v1 e depois endurecer para uso real
Assim que a v1 puder receber, vender e ajustar sem deixar o estoque negativo, lance para um grupo pequeno e use com pedidos reais. Você aprende mais numa semana de uso real do que em outra rodada de telas geradas.
Adicione só o que os usuários realmente pedirem. Passos comuns seguintes são múltiplas localizações, papéis simples para que nem todo mundo possa ajustar estoque, importação CSV para inventário inicial, alertas de baixo estoque e alguns relatórios básicos.
Depois faça uma passada rápida de endurecimento. É aí que protótipos costumam falhar: autenticação, segredos expostos e validação inconsistente.
Uma lista de verificação simples:
- Autenticação que realmente bloqueia acesso (não apenas botões escondidos).
- Segredos fora do browser e fora do repositório.
- Validação de entrada para quantidades, SKUs e IDs.
- Mensagens de erro claras e logs de auditoria para cada movimento de estoque.
- Checagens básicas de segurança contra injeção comum e falhas de permissão.
Se você já tem um protótipo gerado por IA que está se comportando de forma estranha (estoque negativo, validações só na UI, ou lógica de negócio espalhada), FixMyMess (fixmymess.ai) pode ajudar auditando o código e movendo as regras para um fluxo transacional único e aplicável. Um ponto de partida prático é a auditoria de código gratuita deles, que mapeia os problemas antes de você decidir o que consertar ou reconstruir.
Perguntas Frequentes
Por que o estoque fica negativo mesmo quando a matemática parece correta?
O estoque negativo geralmente acontece quando se atualiza um único campo de 'estoque atual' diretamente, ou quando se registram transações mas não se aplica a checagem de 'disponível agora' no momento da venda. A solução mais segura é tratar o inventário como um livro-razão: toda mudança é uma transação, e qualquer transação que faça o estoque ficar abaixo de zero é rejeitada no servidor.
Qual é o esquema de banco de dados mínimo que funciona para inventário?
Comece com uma tabela products e uma tabela append-only inventory_transactions. Cada transação deve incluir o ID do produto, tipo (receive, sell, adjust), quantidade, timestamp e um campo de nota/razão para que toda mudança possa ser explicada depois.
Devo armazenar um campo 'stock_on_hand' no produto?
Não como sua fonte de verdade. Calcule 'on hand' a partir do histórico de transações para que você possa reproduzir o livro-razão e cada unidade seja rastreável a uma razão.
Como evito que duas pessoas vendam a última unidade ao mesmo tempo?
Faça a checagem e a escrita em uma única transação de banco de dados para que duas vendas não possam passar simultaneamente na checagem de 'em estoque'. Se seu app checa no navegador e só escreve depois, duas pessoas podem vender a última unidade mesmo que ambas as telas estivessem corretas.
Quais validações devo aplicar ao receber estoque?
Mantenha o recebimento chato e rigoroso: quantidade maior que zero, o produto deve existir e o timestamp deve ser registrado. Ao salvar, escreva uma transação RECEIVE e evite editar linhas antigas para manter o histórico limpo.
O que fazer se alguém recebeu a mesma remessa duas vezes por engano?
Não apague a entrada original nem a edite silenciosamente. Adicione uma transação corretiva que reverta o erro e referência o recebimento equivocado na nota, para que no futuro fique claro o que aconteceu.
Devo permitir backorders ou vender em negativo na v1?
Por padrão, bloqueie. Quando alguém tenta vender mais do que o disponível, rejeite a transação e mostre uma mensagem clara como 'Apenas 5 disponíveis' para que o usuário reduza a quantidade ou receba estoque primeiro.
Quando usar um ajuste em vez de editar ou deletar uma transação?
Use ajustes para a vida real: danos, perdas, contagens ou limpeza de dados importados. Prefira um ajuste 'set-to' após uma contagem física e um ajuste 'delta' para eventos conhecidos. Exija sempre uma razão e quem fez a alteração.
Como instruir uma ferramenta de IA para que ela não construa um app de inventário quebrado?
Peça ao AI regras no servidor e uma tabela append-only, não apenas telas. Peça para gerar uma função central que valide e grave os movimentos de inventário, e adicione alguns testes que provem que um oversell é bloqueado e que o livro-razão pode ser reproduzido.
Como sei que meu protótipo gerado por IA é inseguro e o que posso fazer?
Procure regras de negócio aplicadas apenas na UI, lógica espalhada por várias páginas, campos de estoque editáveis, falta de timestamps e ausência de uma visão clara de auditoria. Se você herdou um protótipo gerado por IA com esses problemas, FixMyMess pode executar uma auditoria gratuita de código e consolidar as regras em um fluxo transacional único e aplicável para que os números se sustentem em produção.