Previsão de Demanda (IA)
Visão Geral
O módulo de Previsão de Demanda analisa o histórico de vendas e calcula a demanda esperada de itens do cardápio e ingredientes de estoque para um horizonte de dias configurável. O resultado permite gerar Pedidos de Compra Sugeridos automaticamente.
| Característica | Detalhe |
|---|---|
| Arquivo principal | backend/src/inventory/prediction.service.ts |
| Controller | backend/src/inventory/prediction.controller.ts |
| Rota | /t/:slug/inventory/predictions/* |
| Permissão | inventoryPrediction (plan feature gate) |
| Role | Admin, Superadmin |
| UI | Aba "Prediction IA" em InventoryView.tsx — componente PredictionTab.tsx |
Schemas de Dados
DailySalesSnapshot
Registro diário de vendas por item do cardápio. Fonte de dados para o algoritmo de previsão.
Arquivo: backend/src/inventory/schemas/daily-sales-snapshot.schema.ts
| Campo | Tipo | Descrição |
|---|---|---|
tenant | ObjectId | Isolamento multi-tenant |
date | Date | Data do dia (meia-noite UTC) |
menuItem | ObjectId → MenuItem | Item vendido |
quantitySold | number (min 0) | Quantidade total vendida no dia |
revenue | number (min 0) | Receita total no dia |
dayOfWeek | number (0–6) | Dia da semana — 0 = Domingo |
Índices:
{ tenant, date, menuItem }— unique (um snapshot por item por dia){ tenant, menuItem, date: -1 }
SuggestedPurchaseOrder
Pedido de compra sugerido gerado pelo algoritmo de previsão. Não é um PurchaseOrder real — precisa ser convertido manualmente.
Arquivo: backend/src/inventory/schemas/suggested-purchase-order.schema.ts
| Campo | Tipo | Descrição |
|---|---|---|
tenant | ObjectId | Isolamento multi-tenant |
horizonDays | number (min 1) | Horizonte de previsão em dias |
supplier | ObjectId? → Supplier | Fornecedor sugerido (opcional) |
items | SuggestedPOItem[] | Itens sugeridos |
status | enum | pending | approved | dismissed | converted |
convertedPurchaseOrderId | ObjectId? | PO real criado após conversão |
Sub-documento SuggestedPOItem:
| Campo | Tipo | Descrição |
|---|---|---|
inventoryItem | ObjectId → InventoryItem | Ingrediente |
itemName | string | Nome (denormalizado) |
predictedDemand | number | Consumo previsto no horizonte |
currentStock | number | Estoque atual no momento da geração |
suggestedQuantity | number | Quantidade a pedir (com margem de segurança) |
estimatedCost | number | Custo estimado (suggestedQuantity × costPrice) |
Índice: { tenant, status, createdAt: -1 }
Coleta de Dados: Snapshots Diários
Cron noturno (02:00)
PredictionService.buildDailySnapshots() executa todos os dias às 02:00 via @Cron(EVERY_DAY_AT_2AM).
Processo:
- Para cada tenant ativo, agrega os pedidos de ontem (não cancelados) agrupados por
menuItem - Calcula
quantitySold(soma de quantidades) erevenue(soma de totais) - Faz upsert do
DailySalesSnapshotcom{ tenant, date, menuItem }como chave
Trigger manual via API
POST /t/:slug/inventory/predictions/snapshots/build
Útil para seed inicial ou reconstrução após migração. Processa a data de ontem por padrão.
Algoritmo: Weighted Moving Average (WMA)
O algoritmo usa médias móveis ponderadas sobre um janela de até 42 dias de snapshots históricos.
Pesos por período
| Período | Peso |
|---|---|
| Últimos 7 dias | 3× |
| 8–14 dias atrás | 2× |
| 15–42 dias atrás | 1× |
Fórmula da média ponderada:
avgDaily = Σ(quantitySold × peso) / Σ(peso × diasNoPeríodo)
predicted = avgDaily × horizonDaysCálculo de confiança
confidence = min(1.0, dataPoints / 21)dataPoints= número de snapshots disponíveis (dias com venda registrada)- Confiança máxima (1.0) é atingida com 21+ dias de histórico
- Com menos de 7 dias, a previsão tem baixa confiança (
< 0.33)
Cálculo de tendência
Compara a média dos últimos 7 dias com a média dos 7 dias anteriores:
| Condição | Tendência |
|---|---|
| Última > Anterior × 1.1 | up |
| Última < Anterior × 0.9 | down |
| Demais casos | stable |
De Previsão de Cardápio → Demanda de Ingredientes
O algoritmo não para na previsão de itens do cardápio — ele resolve os ingredientes necessários via ProductIngredient (BOM).
Processo
- Para cada
DemandForecastcompredicted > 0:- Busca todos os
ProductIngredientondemenuItem = menuItemId - Para cada ingrediente:
predictedConsumption = predicted × quantityPerUnit
- Busca todos os
- Agrega consumo por
inventoryItem(um mesmo ingrediente pode ser usado em múltiplos pratos) - Adiciona margem de segurança de 10%
- Calcula
daysUntilStockout:dailyConsumptionRate = totalPredictedConsumption / horizonDays daysUntilStockout = currentStock / dailyConsumptionRate - Determina
suggestedOrder:- Se
minQuantitydefinido:suggestedOrder = max(0, predictedConsumption - (currentStock - minQuantity)) - Sem
minQuantity:suggestedOrder = max(0, predictedConsumption - currentStock)
- Se
Itens sem BOM
Ingredientes que não estão vinculados a nenhum item do cardápio via ProductIngredient não aparecem nas sugestões de ingredientes. Configure os vínculos em Inventário → Vínculo com Cardápio.
SuggestedPurchaseOrder: Ciclo de Vida
pending → approved
→ dismissed
→ converted (via conversão manual para PurchaseOrder)Aprovação não converte automaticamente
Aprovar um SuggestedPurchaseOrder muda apenas o status para approved. Para criar um PurchaseOrder real, o administrador deve usar a funcionalidade de conversão na UI ou criar manualmente.
Limitações
| Limitação | Detalhes |
|---|---|
| Histórico mínimo | 7 dias para resultados úteis; 21 dias para confiança máxima |
| Itens sem BOM | Não aparecem em sugestões de ingredientes |
| Base de dados | Apenas pedidos não cancelados são incluídos nos snapshots |
| Sazonalidade | O WMA não modela sazonalidade — pesos são apenas por recência |
| Snapshot único por dia | Reconstruir snapshots de dias já processados faz upsert (sobrescreve) |
Interface — Aba "Prediction IA"
Componente: frontend-react/src/components/inventory/PredictionTab.tsx
A aba "Prediction IA" em InventoryView.tsx possui 3 sub-abas:
| Sub-aba | Dados | Descrição |
|---|---|---|
| Previsão de Demanda | DemandForecast[] | Previsão por item do cardápio com tendência e confiança |
| Ingredientes | IngredientDemand[] | Demanda de ingredientes com daysUntilStockout |
| Pedidos Sugeridos | SuggestedPurchaseOrder[] | Lista de sugestões com ações aprovar/dispensar |
Interface TypeScript: DemandForecast
interface DemandForecast {
menuItemId: string;
menuItemName: string;
avgDaily: number; // média diária calculada
predicted: number; // previsão total para o horizonte
confidence: number; // 0.0 – 1.0
trend: 'up' | 'down' | 'stable';
}Interface TypeScript: IngredientDemand
interface IngredientDemand {
inventoryItemId: string;
itemName: string;
unit: string;
currentStock: number;
predictedDemand: number; // consumo previsto (com margem de 10%)
daysUntilStockout: number; // Infinity se currentStock suficiente
suggestedOrder: number; // quantidade a pedir
estimatedCost: number; // suggestedOrder × costPrice
}API
Rota base: /t/:slug/inventory/predictions
Guards: JwtAuthGuard, TenantGuard, RolesGuard
Roles: Admin, Superadmin
Plan feature: inventoryPrediction
| Método | Endpoint | Descrição |
|---|---|---|
GET | /demand?horizon=7 | Previsão de demanda por item do cardápio |
GET | /suggestions?horizon=7 | Demanda de ingredientes com sugestão de compra |
GET | /suggested-orders | Lista de SuggestedPurchaseOrder (máx 20, desc) |
POST | /generate?horizon=7 | Gera e persiste novo SuggestedPurchaseOrder |
PUT | /suggested-orders/:id/:action | action: approve ou dismiss |
POST | /snapshots/build | Trigger manual do cron de snapshots (data de ontem) |
GET /demand?horizon=7
Retorna DemandForecast[] com previsão para cada item do cardápio com histórico.
[
{
"menuItemId": "64a1b2c3d4e5f6001",
"menuItemName": "X-Burguer",
"avgDaily": 12.5,
"predicted": 87.5,
"confidence": 0.95,
"trend": "up"
}
]GET /suggestions?horizon=7
Retorna IngredientDemand[] ordenado por daysUntilStockout crescente (itens mais críticos primeiro).
[
{
"inventoryItemId": "64a1b2c3d4e5f6002",
"itemName": "Pão de hambúrguer",
"unit": "un",
"currentStock": 50,
"predictedDemand": 96.25,
"daysUntilStockout": 3.6,
"suggestedOrder": 46.25,
"estimatedCost": 92.50
}
]POST /generate?horizon=7
Executa generateSuggestions() e persiste novo SuggestedPurchaseOrder. Retorna null se nenhum ingrediente precisar de reposição.
{
"_id": "64a1b2c3d4e5f6003",
"horizonDays": 7,
"status": "pending",
"items": [...],
"createdAt": "2026-04-10T12:00:00.000Z"
}PUT /suggested-orders/:id/:action
action aceita approve ou dismiss. Atualiza o campo status.
Backend
| Arquivo | Descrição |
|---|---|
backend/src/inventory/prediction.service.ts | Serviço principal — WMA, snapshots, sugestões |
backend/src/inventory/prediction.controller.ts | Controller REST |
backend/src/inventory/schemas/daily-sales-snapshot.schema.ts | Schema DailySalesSnapshot |
backend/src/inventory/schemas/suggested-purchase-order.schema.ts | Schema SuggestedPurchaseOrder |