WEBSOCKETfinish.order

Finalizar Pedido

Evento WebSocket para finalizar um pedido em coleta, mudando seu status de IN_PROGRESS para COMPLETED ou CANCELED. O cliente emite este evento com o ID do pedido e o novo status desejado. O servidor valida se o pedido existe e está em IN_PROGRESS, atualiza o status no banco de dados e notifica todos os clientes conectados da mesma conta via broadcast. Este evento retorna uma resposta imediata via callback (status: 'receive' ou 'refused'), permitindo que o frontend processe o resultado instantaneamente. Em caso de sucesso, o pedido é removido da lista de "Em Progresso" e adicionado à lista de "Finalizados" (se o filtro corresponder) em todos os clientes.

Access Token

Para realizar requisições GET, POST, PUT, DELETE e PATCH nos endpoints da API você precisa de uma chave de autorização. Chamamos essa chave de accessToken.

Para ter acesso ao accessToken, é necessário que o usuário master da licença efetue a liberação deste pela interface do ImageMais Clinic. O accessToken tem validade de 1 hora.

Headers

AuthorizationOBRIGATÓRIO
Tipo:string
Token de autenticação no formato Bearer {accessToken}. O token deve ser enviado durante o handshake de conexão WebSocket.

Parâmetros

orderIdOBRIGATÓRIO
Tipo:number
ID do pedido a ser finalizado

ID do pedido a ser finalizado. É um identificador numérico único que identifica um pedido específico no sistema.

Para encontrar o ID do pedido, para acessar a página de listar todos os pedidos do laboratório.

statusOBRIGATÓRIO
Tipo:string
Novo status do pedido. Valores possíveis: 'COMPLETED' ou 'CANCELED'

Fluxo passo a passo: finish.order

1

Cliente emite a requisição via WebSocket com callback

O frontend emite o evento 'finish.order' com o ID do pedido e o novo status:

// FinishOrder.tsx
async function handleFinish(id: number, status: OrdersStatusEnum) {
  socket?.emit(
    'finish.order',
    { orderId: id, status },
    (response: { status: 'receive' | 'refused'; message: string }) => {
      if (response.status === 'receive') {
        addNotification({
          title: 'Informação',
          message: `O pedido foi alterado para: ${status === OrdersStatusEnum.COMPLETED ? 'Completado' : 'Cancelado'}`,
          bg: 'primary',
          className: 'text-white',
          autohide: true,
        });
      }
      if (response.status === 'refused') {
        addNotification({
          title: 'Atenção!!!',
          message: response.message,
          bg: 'danger',
          className: 'text-white',
          autohide: true,
        });
      }
    }
  );
}
Quem emite: frontend (FinishOrder.tsx)
Quando: ao clicar no botão 'Completado' ou 'Cancelado'
Payload: { orderId: number, status: 'COMPLETED' | 'CANCELED' }
Callback: processa resposta imediata (status: 'receive' ou 'refused')
Ação em sucesso: exibe notificação informando mudança de status
Ação em erro: exibe notificação de erro ao usuário
2

Servidor valida existência e status do pedido

No backend (NestJS + Gateway), o servidor valida se o pedido existe e está em IN_PROGRESS:

@SubscribeMessage('finish.order')
async handleOrderCompleted(
  @ConnectedSocket() client: Socket,
  @MessageBody() data: { orderId: number; status: string },
) {
  try {
    const payload = client.data.payload as PayloadTokenDto;
    
    // Busca o pedido
    const exists = await this.ordersService.findById(
      data.orderId,
      payload,
    );
    
    // Valida se o pedido existe e está em IN_PROGRESS
    if (
      !exists ||
      exists.status.name !== OrdersStatusEnum.IN_PROGRESS
    ) {
      return {
        status: 'refused',
        message: 'O pedido não foi encontrado ou não existe',
        timestamp: new Date().toISOString(),
      };
    }
    // ... continua no próximo passo
  } catch (e) {
    // ... tratamento de erro
  }
}
Quem recebe: servidor (Gateway WS)
Validação 1: verifica se o pedido existe
Validação 2: verifica se o pedido está em IN_PROGRESS (só pode finalizar pedidos em coleta)
Se validação falhar: retorna erro imediato { status: 'refused', message: '...' }
3

Servidor valida se o status existe no banco

O servidor busca o status no banco de dados para validar se é válido:

// Continuação do handleOrderCompleted
// Busca o status no banco
const status = await this.prisma.ordersStatus.findFirst({
  where: {
    name: data.status, // 'COMPLETED' ou 'CANCELED'
  },
});

// Valida se o status existe
if (!status) {
  return {
    status: 'refused',
    message: 'O status não foi encontrado ou não existe',
    timestamp: new Date().toISOString(),
  };
}
// ... continua no próximo passo
Validação: verifica se o status (COMPLETED ou CANCELED) existe no banco
Se status inválido: retorna erro imediato { status: 'refused', message: '...' }
4

Servidor emite evento interno e notifica clientes

O servidor emite evento interno para processar a atualização e notifica os clientes:

// Continuação do handleOrderCompleted
// Emite evento interno para processar a atualização
this.eventEmitter.emitAsync(
  'orders.finish',
  new OrderFinishEvent(
    exists.id,
    status.name as OrdersStatusEnum.COMPLETED | OrdersStatusEnum.CANCELED,
    payload,
  ),
);

// Remove da lista de progresso (notifica todos os clientes)
this.handleRemoveProgress(client, exists);

// Notifica via completed (ainda com status antigo, será atualizado pelo listener)
this.notifyOnEvent('completed', exists, payload);

// Retorna sucesso
return {
  status: 'receive',
  timestamp: new Date().toISOString(),
};
Evento interno: emitAsync('orders.finish') para processar atualização no banco
Remove progresso: notifica todos os clientes via 'remove.progress'
Notifica completed: notifica todos os clientes via 'completed' (status será atualizado depois)
Resposta: retorna { status: 'receive' } via callback
5

Listener atualiza pedido no banco de dados

O listener escuta o evento interno e atualiza o status do pedido no banco:

// finish.listener.ts
@OnEvent('orders.finish')
async handleOrderFinish(orderFinishEvent: OrderFinishEvent) {
  try {
    // Busca o status no banco
    const status = await this.prisma.ordersStatus.findUnique({
      where: {
        name: orderFinishEvent.status, // COMPLETED ou CANCELED
      },
    });
    
    // Atualiza o pedido no banco (muda de IN_PROGRESS para COMPLETED ou CANCELED)
    await this.ordersService.finish(
      { id: orderFinishEvent.orderId, statusId: status.id },
      orderFinishEvent.payload,
    );
    
    // Emite evento adicional para notificar com pedido atualizado
    this.eventEmitter.emitAsync(
      'send.orders',
      new SendOrderEvent(
        'completed',
        orderFinishEvent.orderId,
        orderFinishEvent.payload,
      ),
    );
  } catch (e) {
    this.logger.debug(e);
  }
}
O que faz: atualiza o status do pedido no banco de dados
Mudança de status: de IN_PROGRESS para COMPLETED ou CANCELED
Evento adicional: emite 'send.orders' para notificar com pedido atualizado
6

Listener notifica com pedido atualizado

O listener escuta o evento 'send.orders' e notifica todos os clientes com o pedido atualizado:

// orders.listener.ts
@OnEvent('send.orders')
async handleOnOrdersByCollect(sendEvent: SendOrderEvent) {
  // Busca o pedido atualizado (com novo status)
  const order = await this.ordersService.findById(
    sendEvent.order,
    sendEvent.payload,
  );
  
  // Notifica todos os clientes com o pedido atualizado
  await this.gateway.notifyOnEvent(
    sendEvent.event, // 'completed'
    order,
    sendEvent.payload,
  );
}
O que faz: busca pedido atualizado e notifica todos os clientes
Garantia: todos recebem o pedido com status atualizado (COMPLETED ou CANCELED)
Evento: 'completed' com dados completos do pedido atualizado
7

Clientes removem pedido da lista 'Em Progresso'

Todos os clientes conectados da mesma conta recebem o evento 'remove.progress' e removem o pedido:

// ListInProgress.tsx
socket.on('remove.progress', (remove: Order) => {
  setOrders((prev) => 
    prev.filter((order) => order.id !== remove.id)
  );
});
Quem recebe: todos os clientes conectados da mesma conta
O que faz: remove pedido da lista 'Coletando'
Sincronização: todos os clientes veem a remoção em tempo real
8

Clientes adicionam pedido à lista 'Finalizados'

Todos os clientes conectados da mesma conta recebem o evento 'completed' e adicionam o pedido (se o filtro corresponder):

// Side.tsx
const handleCompleted = (newOrder: Order) => {
  // Filtra por status antes de adicionar
  if (newOrder.status.name === searchParams.status) {
    setOrders((prev) =>
      // Previne duplicatas
      prev.some((o) => o.id === newOrder.id)
        ? prev
        // Adiciona no início da lista (pedidos mais recentes primeiro)
        : [newOrder, ...prev]
    );
  }
};

socket.on('completed', handleCompleted);
Quem recebe: todos os clientes conectados da mesma conta
O que faz: adiciona pedido à lista 'Finalizados' (se filtro corresponder)
Filtro: só adiciona se newOrder.status.name === searchParams.status
Prevenção de duplicatas: verifica se o pedido já existe antes de adicionar
Sincronização: todos os clientes veem a atualização em tempo real

Request URL

ws://api-dev.imagemais.com
{
  "orderId": 123,
  "status": "COMPLETED"
  ...
{
  "orderId": 123,
  "status": "COMPLETED"
}

Respostas

{
  "status": "receive",
  "timestamp": "2025-01-23T10:30:00.000Z"
  ...
{
  "status": "receive",
  "timestamp": "2025-01-23T10:30:00.000Z"
}
{
  "status": "refused",
  "message": "O pedido não foi encontrado ou não existe",
  ...
{
  "status": "refused",
  "message": "O pedido não foi encontrado ou não existe",
  "timestamp": "2025-01-23T10:30:00.000Z"
}