Pular para o conteúdo principal

Colaboradores, Cargos & Endereços

Estrutura organizacional de uma organização: os colaboradores (vendedores, gestores…) que os pedidos referenciam, seus cargos e os endereços compartilhados. Decisão de arquitetura registrada na ADR-018 (.claude/docs/decisions.md).

Modelo​

  • Position (cargo) — rótulo por organização (Vendedor, Supervisor, Coordenador, Gerente…), extensível. Desacoplado do RBAC: cargo é representativo, não concede permissão — o controle de acesso continua em Roles/Permissions. Semeado com os quatro cargos padrão ao criar a organização; o admin adiciona outros (Comprador, Executante…).
  • Collaborator — cadastro comercial, não é conta de login. Identificado por externalCode (único por organização), que casa com sellerExternalCode/managerExternalCode do pedido. Tem cargo (positionId + snapshot positionExternalCode), gestor direto (auto-referência, formando a hierarquia), dados de contato/documento e um attributes (JSONB) extensível. O campo userId liga a um login quando existir (fase E-3.1).
  • Address — tabela compartilhada e polimórfica: um endereço pertence a um dono via ownerType (CLIENT / POINT_OF_SALE / COLLABORATOR) + ownerId, sem chave estrangeira. Hoje é gerenciada embutida no colaborador; o módulo Comercial (Clientes/PDV) reusa a mesma tabela.
Organization ──< Position ──< Collaborator >── Collaborator (manager, auto-ref)
│
└── Address (ownerType=COLLABORATOR, ownerId)

Cargo ≠ RBAC​

Um ponto deliberado: cargo não é papel de acesso. "Gerente" é um rótulo de estrutura comercial; quem pode ver/editar o quê continua definido pelas permissões (collaborator:read, position:write, …) atribuídas via Roles. Isso evita acoplar a hierarquia de vendas ao controle de segurança — os dois evoluem separados.

Endereços polimórficos​

Address não tem FK para o dono: o vínculo é (orgId, ownerType, ownerId). Ao editar um colaborador, o conjunto de endereços é sincronizado por substituição (apaga e recria) dentro da mesma transação — simples e previsível. No máximo um endereço fica marcado como isMain. Por não haver cascade, a remoção do dono limpa seus endereços explicitamente.

Endpoints (API NestJS)​

Todas as rotas exigem organização ativa (ActiveOrgGuard) e a permissão indicada. O master de plataforma faz bypass do RBAC ao entrar numa organização.

MétodoRotaPermissãoDescrição
GET/api/positionsposition:readLista cargos (com contagem de colaboradores).
POST/api/positionsposition:writeCria cargo.
PATCH/api/positions/:idposition:writeAtualiza cargo.
DELETE/api/positions/:idposition:writeRemove cargo (bloqueado se estiver em uso).
GET/api/collaboratorscollaborator:readLista colaboradores (cargo/gestor resolvidos).
GET/api/collaborators/:idcollaborator:readDetalhe (com cargo, gestor e endereços).
POST/api/collaboratorscollaborator:writeCria colaborador (endereços embutidos).
PATCH/api/collaborators/:idcollaborator:writeAtualiza (endereços sincronizados se enviados).
DELETE/api/collaborators/:idcollaborator:writeRemove colaborador e seus endereços.

Schemas completos na Referência da API (Scalar) — tags Estrutura · Cargos e Estrutura · Colaboradores.

Regras de validação​

  • Collaborator.externalCode é único por organização (409 em conflito).
  • positionId/managerId informados devem existir na mesma organização (400 caso contrário).
  • Um colaborador não pode ser o próprio gestor.
  • Sem ciclos na hierarquia: o gestor escolhido não pode ter o próprio colaborador na sua cadeia de gestores (ex.: A gestor de B e B gestor de A). A API sobe a cadeia a partir do gestor proposto e rejeita com 400 se reencontrar o colaborador — impede relacionamento cruzado direto (A↔B) ou indireto (A→B→C→A).
  • Ao remover um cargo em uso, a API bloqueia (reatribua os colaboradores antes).
  • FKs positionId e managerId são ON DELETE SET NULL: remover um cargo ou um gestor não apaga os colaboradores — apenas zera o vínculo.

Frontend (Next.js)​

Dois itens no menu da organização: Colaboradores e Cargos (visíveis conforme as permissões collaborator:read / position:read). As telas trazem CRUD em tabela + modal; o formulário de colaborador inclui um editor de endereços embutido e seletores de cargo e gestor. As chamadas passam por route handlers autenticados (proxy com o cookie httpOnly), como no restante do app.

Seed de cargos​

Ao criar uma organização, os cargos padrão são semeados na mesma transação:

CargoexternalCoderank
GerenteGERENTE1
CoordenadorCOORDENADOR2
SupervisorSUPERVISOR3
VendedorVENDEDOR4

rank menor = mais alto na hierarquia. Os externalCode acompanham o enum hierarchy do ERP de origem, facilitando o casamento na sincronização futura.