Pular para o conteúdo principal

Stock Service

Gerencia operações relacionadas ao estoque de produtos, permitindo consultar disponibilidade por empresa, local, lote e controle de validade.

Visão Geral

O StockService é responsável por:

  • Buscar informações de estoque da API remota
  • Consultar disponibilidade por empresa/filial
  • Filtrar estoque por local de armazenagem
  • Gerenciar controle de lotes e validade
  • Calcular saldo disponível (estoque - reservado)

O controle de estoque é essencial para operações de venda, permitindo verificar disponibilidade em tempo real, gerenciar múltiplos locais de armazenagem e controlar produtos com rastreabilidade (lote, série, validade).

Localização: services/stock/stock.service.ts

Endpoints Utilizados

Base URL

Todos os endpoints são relativos à base URL configurada em services/api.service.ts

Métodos

fetchRemote(params)

Busca informações de estoque remotamente com paginação e filtros.

Endpoint: GET /estoque

Parâmetros:

type FetchStockParams = {
page?: number; // Número da página (padrão: 0)
size?: number; // Quantidade de itens por página (padrão: 999)
dataHora?: string;
produtos?: string; // Filtrar por ID's de produtos
idCompany?: string; // Filtrar por ID da empresa
idLocal?: string; // Filtrar por ID do local de estoque
controle?: string; // Filtrar por código de controle (lote/série)
};

Query Params enviados:

  • page: número da página
  • size: tamanho da página
  • dataHora: timestamp (opcional)
  • produtos: ID's de produtos em formato CSV (opcional)
  • idCompany: ID da empresa (opcional)
  • idLocal: ID do local (opcional)
  • controle: código de controle (opcional)

Retorno:

Promise<Stock[]>;

Exemplo de uso:

import { stockService } from "@/services/stock/stock.service.instance";

// Buscar todo o estoque
const allStock = await stockService.fetchRemote();

// Buscar estoque de um produto específico
const productStock = await stockService.fetchRemote({
produtos: "produto-123,produto-246",
});

// Buscar estoque de uma empresa específica
const companyStock = await stockService.fetchRemote({
idCompany: "empresa-456",
});

// Buscar estoque de um local específico
const locationStock = await stockService.fetchRemote({
idLocal: "local-789",
});

// Buscar por lote/série específico
const batchStock = await stockService.fetchRemote({
controle: "LOTE-2026-001",
});

// Combinar múltiplos filtros
const specificStock = await stockService.fetchRemote({
produtos: "produto-123",
idCompany: "empresa-456",
idLocal: "local-789",
});

// Buscar com paginação
const stockPage = await stockService.fetchRemote({
page: 0,
size: 100,
});

// Buscar para sincronização
const stock = await stockService.fetchRemote({
dataHora: "2026-02-25T10:00:00Z",
});

Tratamento de erros:

  • Lança Error se o status da resposta não for 2xx
  • Mensagem: StockService.fetchRemote failed: {status}

Estruturas de Dados

Interface Stock

interface Stock {
idEmpresa: string; // ID da empresa/filial
produtosuto: string; // ID do produto
idLocal: string; // ID do local de armazenagem
controle: string | null; // Código de controle (lote/série)
dtVal: string | null; // Data de validade (ISO string)
estoque: number; // Quantidade em estoque
reservado: number; // Quantidade reservada (pedidos)
disponivel: number; // Quantidade disponível (estoque - reservado)
}

Campos Importantes

Identificação

  • idEmpresa: Identifica qual empresa/filial possui o estoque
  • produtosuto: Produto ao qual o estoque se refere
  • idLocal: Local físico de armazenagem (depósito, loja, galpão, etc)

Controle de Rastreabilidade

  • controle: Código de rastreamento

    • Lote de fabricação
    • Número de série
    • Código de lote interno
    • null se produto não tem controle
  • dtVal: Data de validade

    • Importante para produtos perecíveis
    • Permite FEFO (First Expired, First Out)
    • null se produto não tem validade

Quantidades

  • estoque: Quantidade física total
  • reservado: Quantidade comprometida em pedidos
  • disponivel: Quantidade efetivamente disponível para venda
    • disponivel = estoque - reservado

Componentes Relacionados

buildQueryParams Helper

Função utilitária que converte objeto de parâmetros em URLSearchParams.

Localização: helpers/buildQueryParams.ts

PageResponse Interface

Interface compartilhada para respostas paginadas.

Localização: services/client/client.service.ts

interface PageResponse<T> {
content: T[]; // Array de itens da página
page?: number; // Número da página atual
size?: number; // Tamanho da página
totalPages?: number; // Total de páginas
totalElements?: number; // Total de elementos
}

Fluxo de Integração

Fluxo de Consulta de Disponibilidade

1. Usuário seleciona produto para venda
2. App busca estoque do produto
3. GET /estoque?produtos=xxx&idCompany=yyy
4. API retorna registros de estoque
5. App calcula total disponível
6. Valida quantidade solicitada
7. Permite ou bloqueia venda

Fluxo de Verificação por Local

1. Vendedor está em filial específica
2. App filtra estoque da empresa atual
3. Consulta disponibilidade local
4. Exibe produtos disponíveis na filial
5. Permite transferência entre locais se necessário

Fluxo de Controle FEFO

1. Produto tem controle de validade
2. Buscar todos os lotes do produto
3. Ordenar por data de validade (mais antigo primeiro)
4. Sugerir lote mais próximo do vencimento
5. Registrar venda com controle específico

Casos de Uso

Consultar Disponibilidade de um Produto

// Verificar se produto está disponível
async function isProductAvailable(
productId: string,
companyId: string,
quantity: number,
): Promise<boolean> {
const stock = await stockService.fetchRemote({
produtos: productId,
idCompany: companyId,
});

const totalDisponivel = stock.reduce((sum, s) => sum + s.disponivel, 0);

return totalDisponivel >= quantity;
}

const available = await isProductAvailable("prod-123", "emp-456", 10);
console.log(`Produto disponível: ${available ? "Sim" : "Não"}`);

Calcular Estoque Total por Empresa

// Somar estoque de todas as filiais
async function getTotalStockByCompany() {
const stock = await stockService.fetchRemote();

const byCompany = stock.reduce(
(acc, item) => {
if (!acc[item.idEmpresa]) {
acc[item.idEmpresa] = {
estoque: 0,
reservado: 0,
disponivel: 0,
};
}

acc[item.idEmpresa].estoque += item.estoque;
acc[item.idEmpresa].reservado += item.reservado;
acc[item.idEmpresa].disponivel += item.disponivel;

return acc;
},
{} as Record<
string,
{ estoque: number; reservado: number; disponivel: number }
>,
);

return byCompany;
}

const stockByCompany = await getTotalStockByCompany();
Object.entries(stockByCompany).forEach(([companyId, totals]) => {
console.log(`Empresa ${companyId}:`);
console.log(` Estoque: ${totals.estoque}`);
console.log(` Reservado: ${totals.reservado}`);
console.log(` Disponível: ${totals.disponivel}`);
});

Testes

Localização

services/stock/stock.service.test.ts

Executar Testes

# Teste específico do serviço
npx jest services/stock/stock.service.test.ts

# Modo watch
npx jest services/stock/stock.service.test.ts --watch

# Com coverage
npx jest services/stock/stock.service.test.ts --coverage

Casos de Teste

  • fetchRemote: busca de estoque com paginação
  • fetchRemote: filtro por produto
  • fetchRemote: filtro por empresa
  • fetchRemote: filtro por local
  • fetchRemote: filtro por controle/lote
  • fetchRemote: múltiplos filtros combinados
  • ✅ Tratamento de erros para status não-2xx
  • ✅ Cálculo correto de disponível (estoque - reservado)

Mocks

Localização

__mocks__/stock.mock.ts

Dados Disponíveis

Mock com registros de estoque:

  • Diferentes empresas/filiais
  • Diferentes locais de armazenagem
  • Produtos com e sem controle de lote
  • Produtos com e sem validade
  • Diferentes quantidades (estoque, reservado, disponível)

Exemplo de uso em testes:

import { stockResponseMock } from "@/__mocks__/stock.mock";

// Mock da API
const apiMock = {
get: jest.fn(async () => ({
status: 200,
data: stockResponseMock,
})),
};

const service = new StockService({ apiWithoutAccessToken: apiMock });
const result = await service.fetchRemote();

expect(result).toEqual(stockResponseMock.content);