# Fase 2 — a oitava fatia: quem atende

Escolhida em 08/09/2026. A agenda pede `colaborador` desde a segunda fatia e o
PDV pede `colaborador` desde a primeira — e **nada listava os colaboradores**.
Uma tela nova conseguiria marcar horário para um id que ela não tem como
descobrir.

---

## Só leitura, e a lista do que ficou de fora é o recorte

`UsuariosController` tem **vinte ações**. Duas entram:

| endpoint | verbo | origem |
|---|---|---|
| `/adm/api/usuarios` | `GET` | `indexAction` |
| `/adm/api/usuarios/:id` | `GET` | `editarAction`, leitura |

**Fica de fora tudo que escreve**, e cada grupo por um motivo próprio:

| o quê | por quê |
|---|---|
| senha (`editarSenha`, `updateMeusDadosSenha`) | credencial; a regra de troca e de recuperação é assunto de autenticação |
| permissões (`permissoes*`, 4 ações) | é a tela que decide quem pode o quê — escrever isso por API sem decidir o formato seria dar poder por engano |
| foto (`editarFoto`, `upload`, `definirImagem`) | é upload de arquivo, e a Fase 1.5 tem uma decisão pendente sobre S3 |
| comissão (`comissao`, `comissaoExtraSalvar`) | entra no cálculo financeiro caracterizado; mexer nela é mexer no dinheiro |
| horário de agenda (`editarAgenda`) | são 28 colunas de abertura/fechamento/almoço por dia da semana — tela própria |
| **`autenticarComo`** | personificação. Ver abaixo |
| criar e alterar usuário | dependem de login e e-mail únicos, validados por duas consultas no controller |

O que a agenda e o PDV precisam **hoje** é escolher quem atende. Isso é leitura,
e duas leituras resolvem sem esconder nenhuma decisão.

---

## O escopo não é só a unidade, e a regra mora no repositório

```php
if (!$request['administrador']) {
    $dql .= " AND uni.id = :unidade AND gru.administrador = :administrador";
}
```

Quem **não** é administrador vê só a própria unidade **e só quem não é
administrador**. Não é filtro de tela: é a regra, e ela vive na consulta.

A fatia passa `administrador` do mesmo jeito que a tela passa — **da sessão,
nunca do pedido**. Aceitá-lo por query string seria deixar qualquer um escolher
o próprio escopo.

**Há uma assimetria, e ela é do ERP:** `editarAction` guarda só
`unidade == sessão || administrador`, sem o "e não é administrador" da listagem.
Então um gerente não vê o administrador na lista, mas o alcança pelo id se ele
for da mesma unidade. A API repete isso — mudar aqui faria a API e a tela
discordarem sobre a mesma pessoa.

---

## O que o recurso não devolve, e por que essa lista importa mais

A entidade tem **78 getters**. Seis devolvem coisa que não pode atravessar uma
API:

| getter | o que é |
|---|---|
| `getSenha()` | o bcrypt |
| `getSalt()` | **credencial**: é a senha, decifrável (D78), e o login pelo cookie (`entrarCookies`) a passa ao autenticador. O `UsuarioService::autenticarComo()` que esta linha citava não tinha chamador e saiu em 03/10/2026 |
| `getTokenWeb()` | o token de sessão |
| `getTokenTrocarSenha()` | o token de recuperação |
| `getOnesignalId()` | identificador de push |
| `getCpf()`, `getCnpj()`, `getInss()` | documento fiscal — LGPD |

O `salt` merece o destaque: **ele é senha com outro nome.** Quem o tiver entra
como aquela pessoa.

`UsuarioApiTest::testNenhumaCredencialAtravessaORecurso` serializa o recurso e
procura cada um desses nomes, mais o prefixo `$2y$` do bcrypt. Um campo novo
acrescentado por descuido é pego ali, e não em produção.

---

## O grupo vai com os quatro sinalizadores

`administrador`, `gerente`, `caixa` e `colaborador` são as colunas que **decidem
o que a pessoa pode fazer** — e o **D124** mostrou 56 lugares no ERP comparando o
*id* do grupo com um número em vez de ler estas.

O recurso entrega os quatro. Uma tela nova que os receba não precisa repetir
aquele erro, e o front da Fase 3 não nasce com `grupo == 3 || grupo == 4`
espalhado por 56 lugares.

---

## `autenticarComo`, e por que ela não entra (D127)

```php
if ($this->usuarioAdmin || !empty($this->sessao->getUsuario('usuarioSessao'))) {
```

A personificação é para administradores — são **10 pessoas ativas**, do grupo
"Gerente geral". Mas a segunda metade da condição abre um caminho que o desenho
não previu: **enquanto se está personificando**, `usuarioSessao` está
preenchido, e a condição passa para **qualquer id**. O ramo de "voltar" chama
`setAcl(null, null)` sobre o usuário da rota, o que **limpa a marca de
personificação**.

Ou seja: um administrador personifica X, e a partir dali pode "voltar" como Y —
ficando Y, sem rastro de que houve personificação. Não é escalada de privilégio
(a porta só abre para quem já é administrador), mas **é perda de rastro**, e o
rastro é a única coisa que distingue personificação legítima de acesso indevido.

Não foi consertado aqui: mexer no fluxo de sessão exige decidir o que a marca
deve significar, e isso é decisão de quem opera. Está no **D127**.

---

## A tela nova, e o que a leitura ganhou — 29/09/2026

**A unidade da lista é escolha do administrador** (D292). A API mandava sempre a da
sessão ao repositório, e o administrador não via outra franquia. Agora
`?unidade=` aceita o id de uma unidade ou `todas` — só do administrador; sem ele, a
da sessão; para os outros, outra unidade ou `todas` é 422, com a regra dos cupons.

**Duas listas para os filtros:** `GET /adm/api/usuarios/unidades` (todas as ativas ao
administrador, a própria aos outros) e `GET /adm/api/usuarios/grupos` (sem o grupo de
administrador para quem não é) — as consultas das listas suspensas do ERP.

**E toda escrita de `/adm/api` passou por uma barreira de origem** (D290): o pedido de
escrita que vem de outra origem é 403 antes da ação. A regra está em
`ISBase\Util\OrigemDoPedido`.

A tela nova lê a lista, os dados de cada usuário e os privilégios, altera os
privilégios, e ativa e desativa. O cadastro, a senha, a foto, a agenda e a comissão
continuam na tela antiga, por enquanto.

## Ativar e desativar — a fatia 2a, 29/09/2026

**Dois endereços, e não um interruptor:** `POST /adm/api/usuarios/:id/ativacao` e
`/desativacao`. O que já está no estado pedido é 409 (`motivo: estado`); ninguém ativa
nem desativa a si mesmo, nem o administrador (403, `motivo: proprio`); quem não é
administrador não alcança outra unidade nem um administrador (404, o teto do D204). A
resposta é o usuário inteiro, como o `GET`, com `proprio`.

**A tela antiga passou a POST com o estado pedido** (D294): o link que alternava virou
um formulário na janela de confirmação. E o campo "Status" da tela de editar passa pela
mesma porta do botão.

**O login:** desativar acrescenta `_old` -- ou `_old2`, se o `_old` já for de outra
pessoa --, e reativar tira o sufixo inteiro quando o login original está livre
(`RegraDoUsuario`). O front reconhece o sufixo pelo mesmo padrão
(`SUFIXO_DO_LOGIN_INATIVO`), conferido pelo `RegraDoUsuarioTest`.

**A sessão de quem é desativado cai no pedido seguinte:** a guarda de login do ERP
confere o `status` da identidade, que é relida do banco a cada pedido. A rota do ERP
cai no "entrar", e a API nova responde 401. O login pela senha e pelo cookie recusam o
desativado. Isso vale para a desativação; o privilégio tirado de quem segue ativo
valia só no próximo login até 17/10/2026 -- desde a resposta 102 (b) do dono, quem
tem o acesso mudado também sai no pedido seguinte, pelo `token_web` (D291).

**Na tela:** desativar pergunta no próprio botão, dizendo o alcance; ativar não
pergunta. O 409 diz que mudou por fora e relê o usuário; voltar relê a lista quando o
estado mudou. O botão não aparece para a própria pessoa nem para quem só lê.
