Skip to content

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ísticaDetalhe
Arquivo principalbackend/src/inventory/prediction.service.ts
Controllerbackend/src/inventory/prediction.controller.ts
Rota/t/:slug/inventory/predictions/*
PermissãoinventoryPrediction (plan feature gate)
RoleAdmin, Superadmin
UIAba "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

CampoTipoDescrição
tenantObjectIdIsolamento multi-tenant
dateDateData do dia (meia-noite UTC)
menuItemObjectId → MenuItemItem vendido
quantitySoldnumber (min 0)Quantidade total vendida no dia
revenuenumber (min 0)Receita total no dia
dayOfWeeknumber (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

CampoTipoDescrição
tenantObjectIdIsolamento multi-tenant
horizonDaysnumber (min 1)Horizonte de previsão em dias
supplierObjectId? → SupplierFornecedor sugerido (opcional)
itemsSuggestedPOItem[]Itens sugeridos
statusenumpending | approved | dismissed | converted
convertedPurchaseOrderIdObjectId?PO real criado após conversão

Sub-documento SuggestedPOItem:

CampoTipoDescrição
inventoryItemObjectId → InventoryItemIngrediente
itemNamestringNome (denormalizado)
predictedDemandnumberConsumo previsto no horizonte
currentStocknumberEstoque atual no momento da geração
suggestedQuantitynumberQuantidade a pedir (com margem de segurança)
estimatedCostnumberCusto 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:

  1. Para cada tenant ativo, agrega os pedidos de ontem (não cancelados) agrupados por menuItem
  2. Calcula quantitySold (soma de quantidades) e revenue (soma de totais)
  3. Faz upsert do DailySalesSnapshot com { 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íodoPeso
Últimos 7 dias
8–14 dias atrás
15–42 dias atrás

Fórmula da média ponderada:

avgDaily = Σ(quantitySold × peso) / Σ(peso × diasNoPeríodo)
predicted = avgDaily × horizonDays

Cá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çãoTendência
Última > Anterior × 1.1up
Última < Anterior × 0.9down
Demais casosstable

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

  1. Para cada DemandForecast com predicted > 0:
    • Busca todos os ProductIngredient onde menuItem = menuItemId
    • Para cada ingrediente: predictedConsumption = predicted × quantityPerUnit
  2. Agrega consumo por inventoryItem (um mesmo ingrediente pode ser usado em múltiplos pratos)
  3. Adiciona margem de segurança de 10%
  4. Calcula daysUntilStockout:
    dailyConsumptionRate = totalPredictedConsumption / horizonDays
    daysUntilStockout = currentStock / dailyConsumptionRate
  5. Determina suggestedOrder:
    • Se minQuantity definido: suggestedOrder = max(0, predictedConsumption - (currentStock - minQuantity))
    • Sem minQuantity: suggestedOrder = max(0, predictedConsumption - currentStock)

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çãoDetalhes
Histórico mínimo7 dias para resultados úteis; 21 dias para confiança máxima
Itens sem BOMNão aparecem em sugestões de ingredientes
Base de dadosApenas pedidos não cancelados são incluídos nos snapshots
SazonalidadeO WMA não modela sazonalidade — pesos são apenas por recência
Snapshot único por diaReconstruir 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-abaDadosDescrição
Previsão de DemandaDemandForecast[]Previsão por item do cardápio com tendência e confiança
IngredientesIngredientDemand[]Demanda de ingredientes com daysUntilStockout
Pedidos SugeridosSuggestedPurchaseOrder[]Lista de sugestões com ações aprovar/dispensar

Interface TypeScript: DemandForecast

ts
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

ts
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étodoEndpointDescrição
GET/demand?horizon=7Previsão de demanda por item do cardápio
GET/suggestions?horizon=7Demanda de ingredientes com sugestão de compra
GET/suggested-ordersLista de SuggestedPurchaseOrder (máx 20, desc)
POST/generate?horizon=7Gera e persiste novo SuggestedPurchaseOrder
PUT/suggested-orders/:id/:actionaction: approve ou dismiss
POST/snapshots/buildTrigger 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.

json
[
  {
    "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).

json
[
  {
    "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.

json
{
  "_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

ArquivoDescrição
backend/src/inventory/prediction.service.tsServiço principal — WMA, snapshots, sugestões
backend/src/inventory/prediction.controller.tsController REST
backend/src/inventory/schemas/daily-sales-snapshot.schema.tsSchema DailySalesSnapshot
backend/src/inventory/schemas/suggested-purchase-order.schema.tsSchema SuggestedPurchaseOrder

Relacionados

Lançado sob a licença MIT.