Skip to content

Transferência de Estoque entre Filiais

Visão Geral

O sistema de estoque usa um modelo de dois níveis:

  • InventoryItem — catálogo central de produtos (compartilhado entre filiais)
  • BranchStock — ledger por PDV/filial que registra a quantidade física e disponível de cada item em cada terminal

Uma transferência decrementa atomicamente o physicalQuantity e availableQuantity da filial de origem e incrementa a filial de destino. A operação é síncrona — não existe status intermediário: ou completa ou lança erro.

Modelo de Dados: BranchStock

Schema: backend/src/branch-stock/schemas/branch-stock.schema.ts

CampoTipoDescrição
itemObjectId → InventoryItemItem do catálogo central
pdvObjectId → PdvTerminal/filial proprietário
tenantObjectId → TenantIsolamento multi-tenant
physicalQuantitynumber (default 0)Quantidade física presente no PDV
availableQuantitynumber (default 0)Quantidade disponível para venda
minQuantitynumber?Ponto de alerta de estoque baixo para esta filial

Índices:

  • { item, pdv } — unique (uma entrada por item por PDV)
  • { pdv, tenant }

Nota: Um row BranchStock é criado na primeira vez que um item é atribuído a um PDV via PUT /branch-stock/:pdvId. Antes disso, o PDV não "conhece" o item.

Pré-requisitos para Transferência

Antes de executar uma transferência, verifique:

  1. O item deve ter um row BranchStock no PDV de origem — caso contrário, retorna 404 Not Found
  2. O PDV de destino será upsertado automaticamente (criado se ainda não existir)
  3. quantity deve ser ≥ 0.001
  4. physicalQuantity da origem deve ser ≥ quantity — caso contrário, 400 Insufficient stock in source branch
  5. Requer permissão ManageSettings (mais restrita que ManageInventory)

Fluxo da Operação de Transferência

Arquivo: backend/src/branch-stock/branch-stock.service.tstransfer()

Passos executados em sequência:

  1. Busca row de origem por { item, pdv: fromPdvId, tenant }404 se não encontrado
  2. Verifica physicalQuantity >= quantity400 se insuficiente
  3. Decrementa origem: physicalQuantity -= quantity, availableQuantity -= quantity
  4. Upsert destino: $inc { physicalQuantity: +quantity, availableQuantity: +quantity } (cria row se ausente)
  5. Emite evento WebSocket stockUpdate para a filial de origem
  6. Emite evento WebSocket stockUpdate para a filial de destino
  7. Verifica availableQuantity ≤ minQuantity em cada filial → emite lowStockAlert se necessário
  8. Retorna { from: BranchStockDocument, to: BranchStockDocument }

Aviso — Sem transação MongoDB: Os passos 3 e 4 não são executados dentro de uma transação MongoDB (deployment standalone). Se o passo 4 falhar após o 3, a origem já terá sido decrementada — o rollback deve ser feito manualmente via POST /branch-stock/:pdvId/adjust com delta positivo.

Formulário de Transferência (UI)

Arquivo: frontend-react/src/views/admin/StockTransferView.tsx

CampoTipoObrigatórioDescrição
PDV de OrigemSelect (/pdvs)SimfromPdvId
PDV de DestinoSelect (/pdvs)SimtoPdvId
ItemSelect (/inventory)SimitemId
Quantidadenumber (min 0.001)Simquantity

Validações client-side:

  • Todos os campos obrigatórios
  • fromPdvId ≠ toPdvId — erro inline se iguais
  • quantity > 0

API — Todos os Endpoints Branch-Stock

Rota base: /t/:slug/branch-stock

Guards: JwtAuthGuard, TenantGuard, PermissionsGuard

BranchScopeGuard: Usuários com role staff visualizam apenas os PDVs aos quais estão atribuídos. Usuários admin e superadmin veem todos os PDVs.

MétodoRotaPermissãoDescrição
GET/t/:slug/branch-stock?pdv=<id>ManageInventory + BranchScopeEstoque de um PDV específico
GET/t/:slug/branch-stock?item=<id>ManageInventoryItem em todos os PDVs do tenant
GET/t/:slug/branch-stockManageInventoryTodo o estoque por filial
GET/t/:slug/branch-stock/alertsManageSettingsItens abaixo de minQuantity por filial
PUT/t/:slug/branch-stock/:pdvIdManageInventory + BranchScopeUpsert — define quantidade absoluta
POST/t/:slug/branch-stock/:pdvId/adjustManageInventory + BranchScopeAjuste por delta (+/-)
POST/t/:slug/branch-stock/transferManageSettingsTransferência atômica entre filiais
DELETE/t/:slug/branch-stock/:pdvId/items/:itemIdManageInventory + BranchScopeDesvincula item do PDV

PUT /t/:slug/branch-stock/:pdvId — Upsert

Define a quantidade absoluta de um item em um PDV. Cria o row se não existir.

Body:

json
{
  "itemId": "64a1b2c3d4e5f6789",
  "physicalQuantity": 50,
  "minQuantity": 10
}

Response: BranchStock document.

POST /t/:slug/branch-stock/:pdvId/adjust — Ajuste por Delta

Incrementa ou decrementa a quantidade pelo valor de delta. Requer que o row exista.

Body:

json
{
  "itemId": "64a1b2c3d4e5f6789",
  "delta": -5
}

Errors:

  • 404 — row não encontrado para este item/PDV
  • 400 — resultado seria negativo (physicalQuantity + delta < 0)

POST /t/:slug/branch-stock/transfer — Transferência

Body:

json
{
  "fromPdvId": "64a1b2c3d4e5f6001",
  "toPdvId":   "64a1b2c3d4e5f6002",
  "itemId":    "64a1b2c3d4e5f6789",
  "quantity":  10
}

Response:

json
{
  "from": { "_id": "...", "pdv": "64a1b2c3d4e5f6001", "physicalQuantity": 40, "availableQuantity": 40 },
  "to":   { "_id": "...", "pdv": "64a1b2c3d4e5f6002", "physicalQuantity": 10, "availableQuantity": 10 }
}

Errors:

  • 404 — item não vinculado ao PDV de origem
  • 400 — quantidade insuficiente na origem

Eventos WebSocket

A transferência emite dois eventos stockUpdate (um por PDV) e, se aplicável, lowStockAlert para cada PDV afetado.

Ver detalhes completos do namespace /inventory em Módulo de Inventário — WebSocket.

Relacionados

Lançado sob a licença MIT.