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áginasize: tamanho da páginadataHora: 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
Errorse 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 estoqueprodutosuto: Produto ao qual o estoque se refereidLocal: 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
nullse produto não tem controle
-
dtVal: Data de validade- Importante para produtos perecíveis
- Permite FEFO (First Expired, First Out)
nullse produto não tem validade
Quantidades
estoque: Quantidade física totalreservado: Quantidade comprometida em pedidosdisponivel: Quantidade efetivamente disponível para vendadisponivel = 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);