Manual Técnico — Módulo Workflow

Documento de referência técnica exclusivo do módulo Workflow (Kanban/Gestão de Tarefas).


Sumário

  1. Visão Geral
  2. Modelo de Dados
  3. Regras de Negócio
  4. Requisitos Funcionais
  5. Arquitetura e Estrutura de Código
  6. Mapa de APIs
  7. Schemas de Validação (Zod)
  8. Notificações
  9. Registro de Tempo (Timesheet)
  10. Alçadas de Aprovação
  11. Isolamento por Empresa
  12. Testes

1. Visão Geral

O módulo Workflow é um sistema de gestão de tarefas estilo Kanban integrado à plataforma NotaNacional. Permite criar quadros (workspaces) com etapas configuráveis, cards (tarefas) com campos financeiros, prioridades, anexos, comentários, registro de tempo e alçadas de aprovação.

Funcionalidades Principais

Funcionalidade Descrição
Workspaces Quadros Kanban com nome, cor e descrição
Membros Gestão de membros por workspace (papéis ADMIN/MEMBRO)
Etapas Colunas configuráveis com posição, cor, flags inicial/final
Prioridades Níveis customizáveis com cor e ícone
Cards Tarefas com campos financeiros, responsável, prazo, prioridade
Drag-and-Drop Movimentação entre etapas via @dnd-kit
Anexos Upload de arquivos (PDF, imagens, DOC, XLS) com controle de volumetria
Comentários Comentários com notificação seletiva de membros
Encaminhamento Copiar cards entre workspaces mantendo rastreabilidade
Registro de Tempo Lançamento manual ou via cronômetro (1 por usuário)
Notificações In-app com preferências por tipo de evento e canal
Aprovação Alçadas por valor que bloqueiam movimentação de cards
Isolamento Filtro por empresa dentro do tenant
PDF Exportação de card individual para PDF
Histórico Rastreamento de mudanças de responsável

2. Modelo de Dados

Diagrama ER (Mermaid)

erDiagram Workspace ||--o{ WorkspaceMembro : "membros" Workspace ||--o{ WorkspaceEtapa : "etapas" Workspace ||--o{ WorkspacePrioridade : "prioridades" Workspace ||--o{ WorkspaceCard : "cards" WorkspaceEtapa ||--o{ WorkspaceCard : "cards na etapa" WorkspacePrioridade ||--o{ WorkspaceCard : "cards com prioridade" WorkspaceCard ||--o{ WorkspaceCardAnexo : "anexos" WorkspaceCard ||--o{ WorkspaceCardComentario : "comentários" WorkspaceCard ||--o{ WorkspaceCardHistorico : "histórico" WorkspaceCard ||--o{ WorkspaceRegistroTempo : "registros tempo" WorkspaceCard ||--o| WorkspaceCard : "encaminhamento (cardOrigemId)" WorkspaceCard }o--o| Tomador : "clienteId" WorkspaceCard }o--o| Tomador : "fornecedorId" WorkspaceCard }o--o| CentroCusto : "centroCustoId" WorkspaceCard }o--o| Empresa : "empresaId" Workspace ||--o{ RegraAprovacao : "regra (tipo WORKSPACE)" RegraAprovacao ||--o{ Aprovador : "aprovadores" RegraAprovacao ||--o{ SolicitacaoAprovacao : "solicitações" WorkspaceNotificacao }o--|| WorkspaceCard : "cardId" WorkspaceNotificacaoPreferencia }o--|| UsuarioAcesso : "usuarioId"

Entidades

Workspace

Campo Tipo Restrições Descrição
id CHAR(36) PK, UUID Identificador
prestadorId CHAR(36) NOT NULL, INDEX Multi-tenancy
nome VARCHAR(100) NOT NULL Nome do quadro
descricao VARCHAR(500) NULLABLE Descrição
cor VARCHAR(7) NOT NULL Cor hex (#RRGGBB)
ativo BOOLEAN DEFAULT true Soft delete
createdAt DATETIME DEFAULT now() Criação
updatedAt DATETIME @updatedAt Atualização

Constraints: @@unique([prestadorId, nome])

WorkspaceMembro

Campo Tipo Restrições Descrição
id CHAR(36) PK, UUID Identificador
workspaceId CHAR(36) FK Workspace Workspace
prestadorId CHAR(36) NOT NULL Multi-tenancy
usuarioAcessoId CHAR(36) NOT NULL Ref UsuarioAcesso
papel ENUM ADMIN, MEMBRO Papel no workspace
ativo BOOLEAN DEFAULT true Soft delete

Constraints: @@unique([workspaceId, usuarioAcessoId])

WorkspaceEtapa

Campo Tipo Restrições Descrição
id CHAR(36) PK, UUID Identificador
workspaceId CHAR(36) FK Workspace Workspace
prestadorId CHAR(36) NOT NULL Multi-tenancy
nome VARCHAR(50) NOT NULL Nome da coluna
cor VARCHAR(7) NOT NULL Cor hex
posicao INT NOT NULL Ordem de exibição
isInitial BOOLEAN DEFAULT false Etapa de entrada
isFinal BOOLEAN DEFAULT false Etapa de conclusão
ativo BOOLEAN DEFAULT true Soft delete

Constraints: @@unique([workspaceId, nome])

WorkspacePrioridade

Campo Tipo Restrições Descrição
id CHAR(36) PK, UUID Identificador
workspaceId CHAR(36) FK Workspace Workspace
prestadorId CHAR(36) NOT NULL Multi-tenancy
nome VARCHAR(50) NOT NULL Nome (ex: Alta, Média)
cor VARCHAR(7) NOT NULL Cor hex
icone VARCHAR(50) NOT NULL Ícone Lucide
posicao INT NOT NULL Ordem
ativo BOOLEAN DEFAULT true Soft delete

Constraints: @@unique([workspaceId, nome])

WorkspaceCard

Campo Tipo Restrições Descrição
id CHAR(36) PK, UUID Identificador
workspaceId CHAR(36) FK Workspace Workspace
prestadorId CHAR(36) NOT NULL Multi-tenancy
etapaId CHAR(36) FK WorkspaceEtapa Etapa atual
prioridadeId CHAR(36) FK Prioridade, NULLABLE Prioridade
responsavelId CHAR(36) NULLABLE Membro responsável
titulo VARCHAR(200) NOT NULL Título do card
descricao TEXT NULLABLE Descrição
dataEntrega DATE NULLABLE Prazo
posicao INT NOT NULL Ordem dentro da etapa
ativo BOOLEAN DEFAULT true Soft delete
status ENUM ABERTO, EM_ANDAMENTO, FINALIZADO, CANCELADO Estado do ciclo
Campos Financeiros
valor DECIMAL(12,2) NULLABLE Valor monetário
centroCusto VARCHAR(100) NULLABLE Centro de custo (texto)
centroCustoId CHAR(36) FK CentroCusto, NULLABLE Ref Centro Custo
entidadeCategoria VARCHAR(100) NULLABLE Categoria
vencimento DATE NULLABLE Vencimento financeiro
fornecedor VARCHAR(200) NULLABLE Fornecedor (texto)
fornecedorId CHAR(36) FK Tomador, NULLABLE Ref Fornecedor
numeroDocumento VARCHAR(50) NULLABLE Nº documento/NF
dataEmissaoNf DATE NULLABLE Data emissão NF
Referências
clienteId CHAR(36) FK Tomador, NULLABLE Ref Cliente
severidadeId VARCHAR(20) NULLABLE BAIXA/MEDIA/ALTA/CRITICA
Encaminhamento
cardOrigemId CHAR(36) FK self, NULLABLE Card que originou
workspaceOrigemId CHAR(36) NULLABLE Workspace de origem
Isolamento
empresaId CHAR(36) FK Empresa, NULLABLE Empresa vinculada

WorkspaceCardAnexo

Campo Tipo Restrições Descrição
id CHAR(36) PK, UUID Identificador
cardId CHAR(36) FK WorkspaceCard Card
prestadorId CHAR(36) NOT NULL Multi-tenancy
url VARCHAR(500) NOT NULL URL do arquivo
nomeArquivo VARCHAR(255) NOT NULL Nome do arquivo
mimeType VARCHAR(100) NOT NULL Tipo MIME
tamanhoBytes INT NOT NULL Tamanho em bytes
autorId CHAR(36) NULLABLE Quem fez upload
ativo BOOLEAN DEFAULT true Soft delete

WorkspaceCardComentario

Campo Tipo Restrições Descrição
id CHAR(36) PK, UUID Identificador
cardId CHAR(36) FK WorkspaceCard Card
prestadorId CHAR(36) NOT NULL Multi-tenancy
autorId CHAR(36) NOT NULL Autor
conteudo TEXT NOT NULL Texto do comentário
ativo BOOLEAN DEFAULT true Soft delete

WorkspaceCardHistorico

Campo Tipo Descrição
id CHAR(36) Identificador
cardId CHAR(36) Card
prestadorId CHAR(36) Multi-tenancy
responsavelAnteriorId CHAR(36) Responsável anterior
responsavelNovoId CHAR(36) Novo responsável
autorId CHAR(36) Quem mudou
createdAt DATETIME Quando ocorreu

WorkspaceRegistroTempo

Campo Tipo Descrição
id CHAR(36) Identificador
workspaceId CHAR(36) Workspace
cardId CHAR(36) Card vinculado
prestadorId CHAR(36) Multi-tenancy
usuarioId CHAR(36) Quem registrou
descricao VARCHAR(500) O que foi feito
dataRegistro DATE Dia do registro
horaInicio DATETIME Início
horaFim DATETIME Fim
duracaoMin INT Duração em minutos
origem ENUM MANUAL ou CRONOMETRO
ativo BOOLEAN Soft delete

WorkspaceCronometro

Campo Tipo Descrição
id CHAR(36) Identificador
workspaceId CHAR(36) Workspace
cardId CHAR(36) Card em foco
prestadorId CHAR(36) Multi-tenancy
usuarioId CHAR(36) Usuário (UNIQUE — 1 por user)
status ENUM EM_ANDAMENTO ou PAUSADO
iniciadoEm DATETIME Quando iniciou
pausadoEm DATETIME Quando pausou (nullable)
acumuladoMs BIGINT Tempo acumulado em ms
descricao VARCHAR(500) Descrição (opcional)

WorkspaceNotificacao

Campo Tipo Descrição
id CHAR(36) Identificador
prestadorId CHAR(36) Multi-tenancy
destinatarioId CHAR(36) Para quem
tipo VARCHAR(30) Tipo do evento
cardId CHAR(36) Card relacionado
workspaceId CHAR(36) Workspace
autorId CHAR(36) Quem disparou (nullable)
titulo VARCHAR(300) Texto da notificação
lida BOOLEAN Se foi lida
ativo BOOLEAN Soft delete

WorkspaceNotificacaoPreferencia

Campo Tipo Descrição
id CHAR(36) Identificador
prestadorId CHAR(36) Multi-tenancy
usuarioId CHAR(36) Usuário
tipoEvento VARCHAR(30) Tipo de evento
canalInApp BOOLEAN Receber in-app
canalEmail BOOLEAN Receber por email

Constraints: @@unique([prestadorId, usuarioId, tipoEvento])

PrestadorVolumetria (Compartilhado)

Campo Tipo Descrição
prestadorId CHAR(36) PK, UNIQUE
bytesUtilizados BIGINT DEFAULT 0
bytesLimite BIGINT DEFAULT 1GB

3. Regras de Negócio

3.1 Multi-tenancy

  • Toda query filtra por prestadorId.
  • Toda mutação valida que o recurso pertence ao prestadorId do usuário.
  • Nome de workspace é único por prestador.

3.2 Ciclo de Vida do Card

stateDiagram-v2 [*] --> ABERTO : Criação ABERTO --> EM_ANDAMENTO : Mover para etapa não-inicial EM_ANDAMENTO --> FINALIZADO : Mover para etapa final ou ação explícita ABERTO --> CANCELADO : Cancelar EM_ANDAMENTO --> CANCELADO : Cancelar FINALIZADO --> [*] CANCELADO --> [*]
  • Status FINALIZADO e CANCELADO são terminais (não podem ser revertidos).
  • Status muda automaticamente ao mover card para etapa isFinal = true.

3.3 Etapas

  • Cada workspace deve ter pelo menos 1 etapa com isInitial = true (obrigatório para encaminhamento).
  • Podem existir etapas com isFinal = true (marcam cards como FINALIZADO ao entrar).
  • Posição determina a ordem visual das colunas (pode ser reordenada via drag-and-drop).
  • Nome de etapa é único dentro do workspace.
  • Inativação de etapa: somente se não houver cards ativos nela (a validar no backend).

3.4 Membros

  • Papéis: ADMIN (gerencia workspace, membros, etapas, prioridades) e MEMBRO (opera cards).
  • Membro é vinculado via usuarioAcessoId (tabela UsuarioAcesso).
  • Unique constraint: um usuário não pode ser adicionado duas vezes ao mesmo workspace.

3.5 Cards — Campos Financeiros

  • Cards possuem campos financeiros opcionais: valor, centroCusto, fornecedor, vencimento, etc.
  • Esses campos permitem usar o workflow como fluxo de contas a pagar/aprovação financeira.
  • Valor usa Decimal(12,2) — máscara BRL R$ X.XXX,XX.
  • Se valor > alçada, dispara solicitação de aprovação (ver seção 10).

3.6 Encaminhamento entre Workspaces

  • Um card pode ser copiado para outro workspace via /cards/:id/encaminhar.
  • O novo card é criado na etapa inicial (isInitial = true) do workspace destino.
  • O card original permanece inalterado no workspace de origem.
  • Campos copiados: titulo, descricao, valor, centroCusto, fornecedor, clienteId, etc.
  • Anexos ativos são duplicados (copiados, não referenciados).
  • Rastreabilidade: cardOrigemId e workspaceOrigemId no card novo.
  • Pré-requisito: o usuário deve ser membro ativo do workspace destino.
  • Pré-requisito: o workspace destino deve ter etapa com isInitial = true.

3.7 Posicionamento e Reordenação

  • Cards dentro de uma etapa têm campo posicao (1-based).
  • Mover um card entre etapas: altera etapaId + recalcula posição na etapa destino.
  • Reordenar dentro da mesma etapa: PATCH em batch com novo array de posições.
  • Etapas e prioridades também possuem posicao reordenável.

3.8 Soft Delete

  • Todas as entidades usam ativo = true/false.
  • DELETE na API marca ativo = false (nunca remove fisicamente).
  • Listagens padrão filtram ativo = true.

3.9 Volumetria de Storage

  • Uploads de anexos contam contra PrestadorVolumetria.bytesUtilizados.
  • Limite padrão: 1 GB (bytesLimite = 1073741824).
  • Se ultrapassar, uploads são recusados.
  • Ao deletar anexo (soft delete), bytes são devolvidos ao saldo.

3.10 Comentários com Notificação Seletiva

  • Ao criar comentário, pode-se enviar notificarIds: string[] com IDs de membros.
  • O sistema cria uma WorkspaceNotificacao do tipo NOVO_COMENTARIO para cada membro listado.
  • Respeita as preferências de canal do destinatário (in-app e/ou email).

3.11 Severidade

  • Cards podem ter campo severidadeId com valores: BAIXA, MEDIA, ALTA, CRITICA.
  • É independente de prioridade (prioridade é customizável, severidade é fixa).

4. Requisitos Funcionais

4.1 Workspaces

ID Requisito
WF-01 CRUD de workspaces (nome, cor, descrição)
WF-02 Listagem paginada com filtros (busca, ativo/inativo, ordenação)
WF-03 Nome de workspace único por prestador
WF-04 Inativação/reativação via soft delete
WF-05 Exibir contagem de membros e etapas na listagem

4.2 Membros

ID Requisito
WF-06 Adicionar membros ao workspace (selecionar de UsuarioAcesso)
WF-07 Definir papel: ADMIN ou MEMBRO
WF-08 Alterar papel de membro existente
WF-09 Remover membro (soft delete)
WF-10 Unique: um usuário não pode estar 2x no mesmo workspace

4.3 Etapas (Colunas Kanban)

ID Requisito
WF-11 CRUD de etapas (nome, cor)
WF-12 Flags: isInitial (entrada), isFinal (conclusão)
WF-13 Reordenação de etapas via drag-and-drop
WF-14 Inativação de etapa
WF-15 Nome de etapa único dentro do workspace

4.4 Prioridades

ID Requisito
WF-16 CRUD de prioridades (nome, cor, ícone)
WF-17 Reordenação via drag-and-drop
WF-18 Inativação de prioridade
WF-19 Nome de prioridade único dentro do workspace

4.5 Cards (Tarefas)

ID Requisito
WF-20 Criar card com: título (obrigatório), descrição, responsável, prazo, prioridade, etapa, empresa
WF-21 Editar card com campos básicos + campos financeiros estendidos
WF-22 Campos financeiros: valor, centroCusto, centroCustoId, fornecedor, fornecedorId, vencimento, numeroDocumento, dataEmissaoNf, clienteId
WF-23 Severidade: BAIXA, MEDIA, ALTA, CRITICA
WF-24 Inativar/reativar card (soft delete)
WF-25 Mover card entre etapas (altera etapaId + posicao)
WF-26 Reordenar cards dentro da mesma etapa (batch)
WF-27 Visualização modo Kanban (agrupado por etapa)
WF-28 Visualização modo Tabela (listagem paginada com filtros)
WF-29 Filtros: busca, responsável, prioridade, etapa, empresa, ativo
WF-30 Exportar card para PDF
WF-31 Alterar status manualmente: FINALIZADO ou CANCELADO
WF-32 Número sequencial do card por workspace (card_number)

4.6 Encaminhamento

ID Requisito
WF-33 Encaminhar card para outro workspace
WF-34 Card novo vai para etapa inicial do workspace destino
WF-35 Copiar anexos ativos do card original
WF-36 Registrar rastreabilidade (cardOrigemId, workspaceOrigemId)
WF-37 Validar que usuário é membro ativo do workspace destino

4.7 Anexos

ID Requisito
WF-38 Upload de arquivos (PDF, JPEG, PNG, DOC, DOCX, XLS, XLSX)
WF-39 Tamanho máximo por arquivo: 10 MB
WF-40 Controle de volumetria por prestador (limite 1 GB padrão)
WF-41 Listar anexos ativos de um card
WF-42 Deletar anexo (soft delete, devolve bytes à volumetria)

4.8 Comentários

ID Requisito
WF-43 Criar comentário (1 a 5000 caracteres)
WF-44 Notificação seletiva: enviar notificarIds para notificar membros específicos
WF-45 Listar comentários ativos de um card
WF-46 Deletar comentário (soft delete)
WF-47 Exibir autor (nome + avatar) no comentário

4.9 Histórico

ID Requisito
WF-48 Registrar mudança de responsável (anterior → novo, quem mudou, quando)
WF-49 Listar histórico de atribuições de um card

5. Arquitetura e Estrutura de Código

Páginas (Frontend)

src/app/(app)/workflow/
├── page.tsx                           # Lista de workspaces
└── [workspaceId]/
    ├── page.tsx                       # Kanban/tabela do workspace
    └── registro-tempo/
        └── page.tsx                   # Timesheet do workspace

API Routes (Backend)

src/app/api/workflow/
├── workspaces/
│   ├── route.ts                       # GET (list) + POST (create)
│   └── [id]/
│       ├── route.ts                   # GET (detail) + PATCH + DELETE
│       ├── membros/
│       │   ├── route.ts               # GET (list) + POST (add)
│       │   └── [membroId]/route.ts    # PATCH + DELETE
│       ├── etapas/
│       │   ├── route.ts               # GET + POST
│       │   ├── [etapaId]/route.ts     # PATCH + DELETE
│       │   └── reorder/route.ts       # PATCH (reordenar)
│       ├── prioridades/
│       │   ├── route.ts               # GET + POST
│       │   ├── [prioridadeId]/route.ts # PATCH + DELETE
│       │   └── reorder/route.ts       # PATCH (reordenar)
│       ├── cards/
│       │   ├── route.ts               # GET (kanban/tabela) + POST
│       │   ├── reorder/route.ts       # PATCH (reordenar)
│       │   └── [cardId]/
│       │       ├── route.ts           # GET + PATCH + DELETE
│       │       ├── mover/route.ts     # PATCH (mover entre etapas)
│       │       ├── status/route.ts    # PATCH (alterar status)
│       │       ├── encaminhar/route.ts # POST (encaminhar)
│       │       ├── pdf/route.ts       # GET (gerar PDF)
│       │       ├── anexos/route.ts    # GET + POST + DELETE
│       │       ├── comentarios/route.ts # GET + POST + DELETE
│       │       └── historico/route.ts # GET
│       ├── registro-tempo/
│       │   ├── route.ts               # GET + POST
│       │   ├── [registroId]/route.ts  # PATCH + DELETE
│       │   ├── relatorio/route.ts     # GET (relatório agrupado)
│       │   └── cronometro/
│       │       ├── iniciar/route.ts   # POST
│       │       ├── pausar/route.ts    # POST
│       │       ├── retomar/route.ts   # POST
│       │       ├── parar/route.ts     # POST
│       │       └── status/route.ts    # GET
│       ├── aprovacao/route.ts         # GET + POST (regra + decisão)
│       └── aprovadores/               # GET + POST + PATCH
└── notificacoes/
    ├── route.ts                       # GET (listar)
    ├── contagem/route.ts              # GET (count não-lidas)
    ├── marcar-todas-lidas/route.ts    # PATCH
    ├── preferencias/route.ts          # GET + PUT
    ├── [id]/route.ts                  # PATCH (marcar lida)
    └── cron/verificar-prazos/         # Cron: verificar prazos vencidos

Services (Client-side)

src/services/
├── workflow.ts                  # Workspaces, membros, etapas, prioridades
├── workflow-cards.ts            # Cards, anexos, comentários, encaminhamento, histórico
├── workflow-registro-tempo.ts   # Registros de tempo + cronômetro
├── workflow-notificacoes.ts     # Notificações + preferências
└── alcadas-aprovacao.ts         # Regras, aprovadores, solicitações

Validators (Zod)

src/lib/validators/
├── workflow.ts                  # Workspace, membros, etapas, prioridades
├── workflow-cards.ts            # Cards, status, encaminhamento, anexos, comentários, reorder
├── workflow-registro-tempo.ts   # Registros, cronômetro, relatório
├── workflow-notificacoes.ts     # Preferências, listagem
└── alcadas-aprovacao.ts         # Regras, aprovadores, decisão

6. Mapa de APIs

6.1 Workspaces

Método Rota Descrição
GET /api/workflow/workspaces Listar workspaces (page, limit, search, ativo, orderBy, orderDir)
POST /api/workflow/workspaces Criar workspace (nome, cor, descricao)
GET /api/workflow/workspaces/:id Detalhe do workspace
PATCH /api/workflow/workspaces/:id Editar workspace
DELETE /api/workflow/workspaces/:id Inativar workspace

6.2 Membros

Método Rota Descrição
GET /api/workflow/workspaces/:id/membros Listar membros
POST /api/workflow/workspaces/:id/membros Adicionar membro (usuarioAcessoId, papel)
PATCH /api/workflow/workspaces/:id/membros/:membroId Editar papel
DELETE /api/workflow/workspaces/:id/membros/:membroId Remover membro

6.3 Etapas

Método Rota Descrição
GET /api/workflow/workspaces/:id/etapas Listar etapas ordenadas por posição
POST /api/workflow/workspaces/:id/etapas Criar etapa (nome, cor, isInitial, isFinal)
PATCH /api/workflow/workspaces/:id/etapas/:etapaId Editar etapa
DELETE /api/workflow/workspaces/:id/etapas/:etapaId Inativar etapa
PATCH /api/workflow/workspaces/:id/etapas/reorder Reordenar (array de {id, posicao})

6.4 Prioridades

Método Rota Descrição
GET /api/workflow/workspaces/:id/prioridades Listar prioridades
POST /api/workflow/workspaces/:id/prioridades Criar prioridade (nome, cor, icone)
PATCH /api/workflow/workspaces/:id/prioridades/:prioridadeId Editar prioridade
DELETE /api/workflow/workspaces/:id/prioridades/:prioridadeId Inativar prioridade
PATCH /api/workflow/workspaces/:id/prioridades/reorder Reordenar

6.5 Cards

Método Rota Descrição
GET /api/workflow/workspaces/:id/cards Listar (modo=kanban/tabela, filtros diversos)
POST /api/workflow/workspaces/:id/cards Criar card
GET /api/workflow/workspaces/:id/cards/:cardId Detalhe do card (inclui campos financeiros)
PATCH /api/workflow/workspaces/:id/cards/:cardId Editar card (schema estendido)
DELETE /api/workflow/workspaces/:id/cards/:cardId Inativar card
PATCH /api/workflow/workspaces/:id/cards/:cardId/mover Mover entre etapas (etapaDestinoId, posicao)
PATCH /api/workflow/workspaces/:id/cards/:cardId/status Alterar status (FINALIZADO/CANCELADO)
POST /api/workflow/workspaces/:id/cards/:cardId/encaminhar Encaminhar para outro workspace
GET /api/workflow/workspaces/:id/cards/:cardId/pdf Gerar PDF do card
PATCH /api/workflow/workspaces/:id/cards/reorder Reordenar cards na etapa (array)

6.6 Anexos

Método Rota Descrição
GET /api/workflow/workspaces/:id/cards/:cardId/anexos Listar anexos ativos
POST /api/workflow/workspaces/:id/cards/:cardId/anexos Criar anexo (metadata) ou upload (FormData)
DELETE /api/workflow/workspaces/:id/cards/:cardId/anexos/:anexoId Deletar anexo

6.7 Comentários

Método Rota Descrição
GET /api/workflow/workspaces/:id/cards/:cardId/comentarios Listar comentários
POST /api/workflow/workspaces/:id/cards/:cardId/comentarios Criar comentário (conteudo, notificarIds)
DELETE /api/workflow/workspaces/:id/cards/:cardId/comentarios/:id Deletar comentário

6.8 Histórico

Método Rota Descrição
GET /api/workflow/workspaces/:id/cards/:cardId/historico Listar mudanças de responsável

6.9 Registro de Tempo

Método Rota Descrição
GET /api/workflow/workspaces/:id/registro-tempo Listar registros (filtros: card, usuario, data, paginação)
POST /api/workflow/workspaces/:id/registro-tempo Criar registro manual
PATCH /api/workflow/workspaces/:id/registro-tempo/:registroId Editar registro
DELETE /api/workflow/workspaces/:id/registro-tempo/:registroId Inativar registro
GET /api/workflow/workspaces/:id/registro-tempo/relatorio Relatório agrupado (diário/semanal/mensal)
POST .../registro-tempo/cronometro/iniciar Iniciar cronômetro (cardId, descricao?)
POST .../registro-tempo/cronometro/pausar Pausar cronômetro
POST .../registro-tempo/cronometro/retomar Retomar cronômetro
POST .../registro-tempo/cronometro/parar Parar cronômetro (gera registro)
GET .../registro-tempo/cronometro/status Status do cronômetro ativo

6.10 Notificações

Método Rota Descrição
GET /api/workflow/notificacoes Listar notificações (page, limit, tipo, lida)
GET /api/workflow/notificacoes/contagem Count de não-lidas
PATCH /api/workflow/notificacoes/marcar-todas-lidas Marcar todas como lidas
PATCH /api/workflow/notificacoes/:id Marcar individual como lida
GET /api/workflow/notificacoes/preferencias Buscar preferências
PUT /api/workflow/notificacoes/preferencias Atualizar preferências

6.11 Aprovação

Método Rota Descrição
GET /api/workflow/workspaces/:id/aprovacao Buscar regra e solicitações
POST /api/workflow/workspaces/:id/aprovacao Criar/atualizar regra
GET /api/workflow/workspaces/:id/aprovadores Listar aprovadores
POST /api/workflow/workspaces/:id/aprovadores Criar aprovador
PATCH /api/workflow/workspaces/:id/aprovadores/:id Editar aprovador

7. Schemas de Validação (Zod)

7.1 Workspace

createWorkspaceSchema: { nome: string(2-100), descricao?: string(max 500), cor: #RRGGBB }
updateWorkspaceSchema: { nome?, descricao?, cor?, ativo? }

7.2 Membros

addMembroSchema: { usuarioAcessoId: UUID, papel: "ADMIN" | "MEMBRO" }
updateMembroSchema: { papel: "ADMIN" | "MEMBRO" }

7.3 Etapas

createEtapaSchema: { nome: string(2-50), cor: #RRGGBB, isInitial?: boolean, isFinal?: boolean }
updateEtapaSchema: { nome?, cor?, isInitial?, isFinal?, ativo? }
reorderEtapasSchema: { etapas: [{id: UUID, posicao: int ≥ 1}] }

7.4 Prioridades

createPrioridadeSchema: { nome: string(2-50), cor: #RRGGBB, icone: string(1-50) }
updatePrioridadeSchema: { nome?, cor?, icone?, ativo? }
reorderPrioridadesSchema: { prioridades: [{id: UUID, posicao: int ≥ 1}] }

7.5 Cards

createCardSchema: {
  titulo: string(2-200) REQUIRED,
  descricao?: string | null,
  responsavelId?: UUID | null,
  dataEntrega?: datetime (ISO offset) | null,
  prioridadeId?: UUID | null,
  etapaId?: UUID | null,
  empresaId?: UUID | null
}

updateCardSchemaExtended: {
  ...updateCardSchema,                    // titulo?, descricao?, responsavelId?, dataEntrega?, prioridadeId?, etapaId?, ativo?
  empresaId?: UUID | null,
  valor?: number(0 - 999999999999.99) | null,
  centroCusto?: string(max 100) | null,
  centroCustoId?: UUID | null,
  entidadeCategoria?: string(max 100) | null,
  vencimento?: datetime | null,
  fornecedor?: string(max 200) | null,
  fornecedorId?: UUID | null,
  numeroDocumento?: string(max 50) | null,
  dataEmissaoNf?: datetime | null,
  clienteId?: UUID | null,
  severidadeId?: "BAIXA" | "MEDIA" | "ALTA" | "CRITICA" | null,
  cardOrigemId?: UUID | null
}

updateStatusSchema: { status: "FINALIZADO" | "CANCELADO" }
moverCardSchema: { etapaDestinoId: UUID, posicao: int ≥ 1 }
reorderCardsSchema: { etapaId: UUID, cards: [{id: UUID, posicao: int ≥ 1}] }
encaminharCardSchema: { workspaceDestinoId: UUID }

7.6 Anexos

createAnexoSchema: { url: URL(max 500), nomeArquivo: string(1-255), mimeType: string(1-100), tamanhoBytes: int > 0 }
uploadArquivoSchema: { mimeType: PDF|JPEG|PNG|DOC|DOCX|XLS|XLSX, tamanhoBytes: int(max 10MB) }
// Constantes: MAX_FILE_SIZE_BYTES = 10485760 (10MB), MAX_LOGO_SIZE_BYTES = 2097152 (2MB)

7.7 Comentários

createComentarioSchema: { conteudo: string(1-5000) }
createComentarioComNotificacaoSchema: { conteudo: string(1-5000), notificarIds: UUID[] (default []) }

7.8 Registro de Tempo

createRegistroTempoSchema: {
  cardId: UUID,
  descricao: string(3-500),
  dataRegistro: "YYYY-MM-DD",
  horaInicio: "HH:MM",
  horaFim: "HH:MM"
}
updateRegistroTempoSchema: { descricao?, dataRegistro?, horaInicio?, horaFim?, ativo? }
iniciarCronometroSchema: { cardId: UUID, descricao?: string(max 500) }
pararCronometroSchema: { descricao?: string(3-500) }
relatorioQuerySchema: { dataInicio: "YYYY-MM-DD", dataFim: "YYYY-MM-DD", usuarioId?, cardId?, agrupamento: "diario"|"semanal"|"mensal" }

7.9 Notificações

// Tipos de evento: ATRIBUICAO, PRAZO_VENCIMENTO, NOVO_COMENTARIO, MUDANCA_ETAPA, EDICAO_CARD, CRIACAO_CARD
updatePreferenciasSchema: { preferencias: [{ tipoEvento, canalInApp: bool, canalEmail: bool }] (1-6 items) }
listNotificacoesQuerySchema: { page: int ≥ 1, limit: int(1-50), tipo?: TipoEvento, lida: "true"|"false"|"all" }

7.10 Aprovação

createRegraAprovacaoSchema: { valorMinimo: number(0.01 - 9999999999.99) }
createAprovadorSchema: { membroId: UUID, valorAlcada: number(0.01 - 999999999.99) }
decisaoSchema: { decisao: "APROVADO" | "REJEITADO", observacao?: string(max 500) }
listPendentesSchema: { page, perPage, workspaceId?, status, de?, ate?, orderBy, orderDir }

8. Notificações

Tipos de Evento

Tipo Quando dispara
ATRIBUICAO Card é atribuído a um responsável
PRAZO_VENCIMENTO Data de entrega do card está vencendo/venceu (via cron)
NOVO_COMENTARIO Comentário criado com notificarIds preenchido
MUDANCA_ETAPA Card movido entre etapas
EDICAO_CARD Card editado
CRIACAO_CARD Card criado no workspace

Canais

Canal Descrição
IN_APP Notificação visível no badge do sino (header)
EMAIL Envio de e-mail via nodemailer

Preferências

  • Cada usuário pode configurar por tipo de evento quais canais deseja receber.
  • Padrão: ambos ativos (canalInApp: true, canalEmail: true).
  • Unique: [prestadorId, usuarioId, tipoEvento].

Contagem

  • Endpoint /api/workflow/notificacoes/contagem retorna total de não-lidas.
  • Usado para exibir badge numérico no header.

Cron — Verificar Prazos

  • Rota /api/workflow/notificacoes/cron/verificar-prazos verifica cards com dataEntrega próxima/vencida.
  • Dispara notificação PRAZO_VENCIMENTO para o responsável.

9. Registro de Tempo (Timesheet)

Lançamento Manual

  • Campos: cardId, descrição, dataRegistro, horaInício (HH:MM), horaFim (HH:MM).
  • Duração calculada automaticamente em minutos (duracaoMin).
  • Origem marcada como MANUAL.
  • Um registro por vez — sem overlap checking.

Cronômetro

  • 1 cronômetro ativo por usuário (usuarioId é UNIQUE em WorkspaceCronometro).
  • Estados: EM_ANDAMENTOPAUSADOEM_ANDAMENTO (loop) → PARADO (gera registro).
stateDiagram-v2 [*] --> EM_ANDAMENTO : Iniciar EM_ANDAMENTO --> PAUSADO : Pausar PAUSADO --> EM_ANDAMENTO : Retomar EM_ANDAMENTO --> [*] : Parar (gera registro) PAUSADO --> [*] : Parar (gera registro)
  • Ao pausar: salva pausadoEm e soma tempo ao acumuladoMs.
  • Ao retomar: limpa pausadoEm e recomeça contagem.
  • Ao parar: calcula tempo total, cria WorkspaceRegistroTempo com origem CRONOMETRO, deleta o cronômetro.

Relatório

  • Endpoint: GET .../registro-tempo/relatorio?dataInicio&dataFim&agrupamento
  • Retorna:
    • totalMinutos e totalFormatado (ex: "12h 30min")
    • mediaDiariaMin e mediaDiariaFormatada
    • porPeriodo[] — agrupado por dia/semana/mês
    • porResponsavel[] — agrupado por usuário
    • porCard[] — agrupado por card

10. Alçadas de Aprovação

Conceito

Quando um card com campo valor excede o valor mínimo configurado na regra de aprovação do workspace, o sistema bloqueia a movimentação até que um aprovador com alçada suficiente aprove.

Entidades

Entidade Descrição
RegraAprovacao Regra por workspace com valorMinimo (única por tipo+prestador ativo)
Aprovador Membro com valorAlcada — pode aprovar cards até esse valor
SolicitacaoAprovacao Pedido de aprovação com status PENDENTE/APROVADO/REJEITADO

Fluxo

sequenceDiagram participant U as Usuário participant S as Sistema participant A as Aprovador U->>S: Move card com valor > valorMinimo S->>S: Cria SolicitacaoAprovacao (PENDENTE) S->>A: Notifica aprovador(es) com alçada suficiente A->>S: Decide (APROVADO + observação) S->>S: Atualiza status solicitação S->>U: Card liberado para movimentação

Regras

  • Uma regra ativa por tipo (WORKSPACE/FINANCEIRO) por prestador.
  • Aprovador deve ser membro do workspace.
  • valorAlcada define até quanto o aprovador pode aprovar.
  • Se valor do card > alçada do aprovador, outro aprovador com alçada maior deve decidir.
  • Decisão: APROVADO ou REJEITADO com observação opcional (max 500 chars).
  • Listagem de pendentes com filtros: status, workspace, período, ordenação.

11. Isolamento por Empresa

Conceito

Dentro de um mesmo tenant (prestadorId), cards podem ser isolados por empresa. Isso permite que prestadores com múltiplas empresas/CNPJs gerenciem workflows separados.

Implementação

  • WorkspaceCard.empresaId: FK para Empresa.
  • Filtro no GET cards: ?empresaId=UUID retorna apenas cards daquela empresa.
  • Modelo Empresa: { id, prestadorId, nome, cnpj } com unique [prestadorId, cnpj].
  • Modelo EmpresaUsuario: vincula usuários a empresas (ou acessoGlobal: true).
  • Cards sem empresaId são visíveis a todos.

12. Testes

Cobertura Existente

src/app/api/workflow/workspaces/__tests__/
├── route.test.ts                                    # CRUD workspaces
├── workspace-access-control.exploration.test.ts     # Access control exploratório
└── workspace-access-control.preservation.test.ts    # Access control preservação

src/app/api/workflow/workspaces/[id]/cards/__tests__/
└── route.test.ts                                    # CRUD cards

src/app/api/workflow/workspaces/[id]/etapas/__tests__/
└── route.test.ts                                    # CRUD etapas

src/app/api/workflow/workspaces/[id]/etapas/reorder/__tests__/
└── route.test.ts                                    # Reordenação

src/app/api/workflow/workspaces/[id]/prioridades/__tests__/
└── route.test.ts                                    # CRUD prioridades

src/app/api/workflow/workspaces/[id]/prioridades/reorder/__tests__/
└── route.test.ts                                    # Reordenação

src/app/api/workflow/workspaces/[id]/registro-tempo/__tests__/
└── route.test.ts                                    # Registro de tempo

src/app/api/workflow/workspaces/[id]/registro-tempo/cronometro/__tests__/
└── ...                                              # Cronômetro

src/app/api/workflow/workspaces/[id]/aprovacao/__tests__/
└── route.test.ts                                    # Alçadas

src/app/api/workflow/workspaces/[id]/membros/[membroId]/__tests__/
└── ...                                              # Membros

src/app/api/workflow/workspaces/[id]/prioridades/[prioridadeId]/__tests__/
└── ...                                              # Prioridade individual

src/app/api/workflow/workspaces/[id]/etapas/[etapaId]/__tests__/
└── ...                                              # Etapa individual

src/app/api/workflow/workspaces/[id]/__tests__/
└── ...                                              # Workspace individual

Executar Testes do Módulo

# Todos os testes do módulo workflow
npx vitest run --testPathPattern=workflow

# Testes específicos de cards
npx vitest run --testPathPattern=workflow.*cards

# Testes de aprovação
npx vitest run --testPathPattern=workflow.*aprovacao

Documento atualizado em Junho/2026. Manter sincronizado com evolução do módulo.