# Documentação técnica — Cadastro de pessoas (cliente, fornecedor, funcionário, representante) e usuários

**Última atualização:** 2026-06-08 — SSO (Google/Microsoft), forgot password (Brevo), campos `auth_*` em usuário.

Este documento descreve a tabela **`pessoa`**, os **controllers** que segmentam o cadastro por tipo, integrações (API CNPJ, modal Livewire, pessoa padrão por empresa) e o cadastro de **`users`** com **Spatie Permission** e **tenant**. A relação de `pessoa` com NF, contratos e agenda está resumida em **`Doc_Modulo_Financeiro.md`** e **`Doc_Modulo_Evento.md`**. Isolamento multi-inquilino (`idtenant`, `TenantScope`, tenant “admin”): **`Doc_Modulo_Tenant.md`**. Cadastro público e planos: **`Doc_Modulo_Planos_Assinatura_Pagamento.md`**.

---

## 1. Model `Pessoa` (`pessoa`)

### 1.1 Escopo e chave

- **`TenantScope`** em todas as consultas Eloquent.
- PK: **`idpessoa`**. Timestamps: **`criadoem`**, **`alteradoem`**.
- Trait **`HandlesFileUploads`** (uploads quando aplicável nas telas).

### 1.2 Campo `tipo` (discriminador)

O mesmo registro físico serve a vários perfis. Os **scopes** aceitam **várias grafias** históricas:

| Perfil | Valores reconhecidos nos scopes | Uso típico no formulário |
|--------|----------------------------------|---------------------------|
| **Fornecedor** | `fornecedor` | Hidden `tipo = fornecedor` (`admin.pessoas._form` via FornecedorController). |
| **Cliente** | `cliente`, `CLIENTE` | Hidden `tipo = cliente`. |
| **Funcionário** | `F`, `FUNCIONARIO` | Hidden `tipo = F` (`admin.funcionarios._form`). |
| **Representante** | `R`, `representante`, `REPRESENTANTE` | Hidden `tipo = representante` (create) ou rótulo `Representante` no edit. |

### 1.3 Outros campos relevantes (`$fillable`)

- **Empresa e vínculos:** `idempresa`, **`idrepresentante`** (cliente vinculado a um representante comercial).
- **Identificação:** `nome`, `razaosocial`, `cpfcnpj`, `inscestadual`, `rg`.
- **Endereço e contato:** `cep`, `endereco`, `numero`, `complemento`, `bairro`, `cidade`, `estado`, `telefone`, `celular`, `celular2`, `email`, `website`.
- **Pessoa física / RH:** `datanascimento`, `nomepai`, `nomemae`, `estadocivil`, `cargo`, `esocial`, `admissao`, `admissaomotivo`, `demissao`, `demissaomotivo`.
- **Operação:** `responsavel`, `descricao`, `observacao`, **`temagenda`** (funcionário com agenda), **`status`**, **`cor`** (UI).
- **Flags:** **`pessoapadrao`** — quando `true`, é o registro espelhado da **empresa** para um dado `tipo` (ver § 5).

### 1.4 Relações

- **`empresa()`** — `hasOne` por `idempresa`.
- **`representante()`** — `hasOne` em `Pessoa` via **`idrepresentante`** (o “pai” comercial é outra `pessoa`).

### 1.5 Scopes úteis

- **`active()`** — `status = ATIVO`.
- **`fornecedor()`, `cliente()`, `funcionario()`, `representante()`, `funcionarioOuCliente()`** — conforme § 1.2.

---

## 2. Telas administrativas por tipo

Há **dois padrões**:

1. **Módulos dedicados** (menu principal): **`FornecedorController`**, **`ClienteController`**, **`FuncionarioController`**, **`RepresentanteController`** — cada um com resource, `fetch` JSON e, na maioria, `search`.
2. **Grupo legado `PessoaController`** em `admin/pessoas/{tipo}` — lista apenas **`fornecedor`** vs **`cliente`** conforme parâmetro; `store`/`update` genéricos; **`destroy` vazio**; **`TipoPessoa` injetado no `create` mas não usado** no trecho analisado.

### 2.1 Rotas (prefixo `admin.`, middleware `auth` + `single.session`)

| Área | Rotas relevantes |
|------|-------------------|
| **Pessoas genérico** | `admin.pessoas.index/create/search/edit` com `{tipo}`, `admin.pessoas.store`, `admin.pessoas.update`, `admin.pessoas.check-cpfcnpj`, `admin.pessoas.get` |
| **Fornecedores** | `admin.fornecedores.*`, `fornecedores.search`, `fornecedores.fetch` |
| **Clientes** | `admin.clientes.*`, `clientes.search`, `clientes.fetch` |
| **Funcionários** | `admin.funcionarios.*`, `funcionarios.search` (URL `funcionario/procurar`), `funcionarios.fetch` |
| **Representantes** | `admin.representantes.*`, `representantes.fetch`; **`representantes.search` está declarada em `web.php` porém não há método `search` no `RepresentanteController`** (risco de erro ao acessar a rota). |

Várias ações `search` usam middleware **`role:ADMIN`**.

### 2.2 `FornecedorController`

- **Index:** `pessoapadrao = false`, `status != INATIVO`, scope **`fornecedor()`**.
- **Store/update:** `only(...)` com dados cadastrais + `tipo`; formulário compartilhado **`admin.pessoas._form`** com `tipo = fornecedor`.
- **Fetch:** colunas para grid; partial de status usa **`idtagtipo`** como id (cópia de outro módulo — possível inconsistência com `idpessoa`).

### 2.3 `ClienteController`

- **Index / fetch:** se **`auth()->user()->representante->tipo == 'R'`**, filtra clientes com **`idrepresentante = user.representante.idpessoa`**; caso contrário lista todos os clientes (com `pessoapadrao = false`, etc.). Ou seja, **usuário ligado a pessoa representante** vê apenas **sua** carteira.
- **Store/update:** lista fechada de campos (`only`); **`idrepresentante` não** entra no `only` — vínculo representante→cliente, se usado, depende de outro fluxo ou campo no form não incluído nesse `only`.
- **Search:** filtra `nome` e `status` **sem** reforçar scope **`cliente()``** — pode retornar outros tipos se existirem com mesmo nome.

### 2.4 `FuncionarioController`

- **Index / fetch / search:** scope **`funcionario()`**; search opcional por intervalo de **`admissao`** (`date_range` d/m/y).
- **Store:** `only(...)` inclui **`tipo`** (o form envia **`F`** via hidden).
- Listas fixas **`admissaomotivo`** e **`demissaomotivo`** (arrays no controller) para UI.
- **Fetch:** partial de status usa **`idformapagamento`** como id — provável **erro de cópia** (deveria ser `idpessoa` para atualizar a pessoa correta).
- Comentário **`@TODO: Gerar usuario com permissões padroes`** no `store` (não implementado).

### 2.5 `RepresentanteController`

- **Index / fetch:** scope **`representante()`**.
- **Store/update:** `create`/`update` com **`$request->all()`** (sem `only`) — maior superfície de atributos que nos outros controllers.
- **Destroy:** com transação.

### 2.6 `PessoaController` (rotas `pessoas/{tipo}`)

- **`checkCpfCnpj`:** POST JSON; chama **`BrasilApiService::cnpj`** (consulta externa).
- **`store`:** `Model::create($request->all())` — sem validação Laravel explícita no controller.
- **`get($id)`:** JSON de pessoas da empresa **`idempresa = $id`** com scope **`funcionarioOuCliente()`** (funcionários + clientes para combos).

---

## 3. Formulário compartilhado `admin.pessoas._form`

- Campo hidden **`tipo`** vindo da variável Blade **`$tipo`**.
- Condições de exibição para **representante** (`R` / `Representante`) e blocos específicos quando **`$tipo` não é** fornecedor/cliente/representante (ex.: campos adicionais de funcionário em fluxos mistos).
- **Cor** obrigatória no markup (`required` no input color).
- **Consulta CNPJ:** `fetch` para **`admin.pessoas.check-cpfcnpj`** preenche dados quando aplicável.

---

## 4. `PessoaModal` (Livewire)

- Arquivo: **`App\Livewire\PessoaModal`**, view **`livewire.pessoa-modal`**.
- Cria **`Pessoa`** com **`tipo = fornecedor`** fixo, **`status = ATIVO`**, validação reduzida (`idempresa`, `nome`, etc.).
- Dispara eventos **`entidadeAtualizada`** / **`fechar-modal`** para integração com outros componentes (ex.: cadastro rápido de fornecedor).

---

## 5. `PessoaPadraoService`

- Ao criar/manter empresa, pode existir uma **pessoa padrão** por tipo: `create($empresa, $tipo)` copia dados da empresa (nome fantasia, razão social, CNPJ, IE, endereço), **`pessoapadrao = true`**, **`status = ATIVO`**.
- **`existe($empresa, $tipo)`** evita duplicidade.
- Esses registros costumam ser **excluídos das listagens** operacionais (`pessoapadrao = false`).

---

## 6. Usuários (`users`)

### 6.1 Model `User`

- **`HasRoles`** (Spatie Permission), **`HasApiTokens`** (Sanctum), **`TenantScope`**.
- **`$fillable`:** `name`, `email`, `password`, `username`, `idtenant`, `idsession`, `status`, **`auth_tipo`**, **`auth_provedor`**, **`sso_obrigatorio`**, **`provider_id`**, **`provider_email`**.
- **`password`:** mutator **`bcrypt`** ao atribuir; **`null`** permitido para usuários só SSO.
- **`auth_tipo`:** `senha` | `sso` | `hibrido` — define o que aparece no login (`SsoAuthService`).
- **`auth_provedor`:** `google` | `azure` | `null`. **`null` (= Automático)** permite Google **e** Microsoft quando `auth_tipo` é `sso` ou `hibrido`. Valor fixo restringe a um provedor.
- **`sso_obrigatorio`:** se true, bloqueia login por senha mesmo com hash definido.
- **`provider_id` / `provider_email`:** preenchidos no primeiro login OAuth bem-sucedido.
- **`tenant()`** — `belongsTo` **`Tenant`**.
- **`representante()`** — `belongsTo` **`Pessoa`** em **`idrepresentante`**, com **`withDefault()`** (acesso `->representante` sem erro quando nulo — usado no `ClienteController` e na sidebar).

**Observação:** colunas **`idrepresentante`** e **`api_token`** existem nas migrations / uso no `UserController`, mas **não** estão em `$fillable`. O **`create($data)`** do Eloquent **ignora** chaves fora de `fillable` — vale **confirmar no banco** se os valores são persistidos (pode exigir ajuste no model ou `forceFill`).

### 6.2 `UserController`

| Método | Comportamento |
|--------|----------------|
| **`index`** | Paginação via **`User::getAll('paginate', 100)`** (respeita escopo do model). |
| **`fetch`** | JSON para grid (nome, username, email, primeiro papel, switch de status). |
| **`create` / `edit`** | Papéis Spatie (`Role`), empresas, status ATIVO/INATIVO, lista **`Pessoa::representante()`** para vínculo opcional. |
| **`store`** | Validação inclui **`auth_tipo`**, **`auth_provedor`**, **`sso_obrigatorio`**. **`auth_tipo`** padrão = **`tenant.auth_padrao`**. Senha obrigatória exceto quando **`auth_tipo = sso`**. Define **`api_token`**, **`idtenant`**, **`assignRole`**. |
| **`update`** | Atualiza campos de autenticação; se **`auth_tipo = senha`**, limpa **`provider_id`** / **`provider_email`**. Transação; troca de papel Spatie quando aplicável. |
| **`changePassword`** | PUT **`user.change.password`**: atribui nova senha (passa pelo mutator bcrypt). |
| **`destroy`** | Remove usuário. |
| **`search`** | Filtro por **`name`** com **`User::filter`**. |

### 6.5 Login SSO (Google / Microsoft)

- **Rotas:** `GET auth/{provedor}/redirect` (`auth.social.redirect`), `GET auth/{provedor}/callback` (`auth.social.callback`). Provedores: **`google`**, **`azure`** (Microsoft).
- **Telas:** botões em `sessions/create` e `pages/cadastro` (passo 2) via partial **`partials/sso-auth-buttons`**.
- **Pré-cadastro obrigatório:** OAuth só autentica e-mail já existente em **`users`** com **`status = ATIVO`**. Não há auto-registro pelo IdP.
- **Regras (`SsoAuthService`):**
  - Login senha: `auth_tipo ∈ {senha, hibrido}` e tenant **`permitir_senha_local`** (se híbrido).
  - Login SSO: `auth_tipo ∈ {sso, hibrido}`; e-mail do IdP = **`users.email`**; provedor compatível com **`auth_provedor`** e **`tenant.sso_provedores`**.
- **Formulário admin:** `admin/partials/auth-campos-usuario.blade.php` (método, provedor, SSO obrigatório).
- **Suporte NoLapis:** editar tenant (`auth_padrao`, provedores) **e/ou** usuário individual — ver **`Doc_Modulo_Tenant.md`** § 11.

**Configuração:** `.env` — `SSO_HABILITADO`, `GOOGLE_CLIENT_ID/SECRET`, `MICROSOFT_CLIENT_ID/SECRET`, `MICROSOFT_TENANT_ID=common`, URIs de callback `{APP_URL}/auth/google/callback` e `{APP_URL}/auth/azure/callback`.

### 6.6 Recuperação de senha (Forgot Password)

Fluxo **independente** do SSO. Contas **`sso_obrigatorio`** devem usar o IdP; o **`ForgotPasswordController` ainda não bloqueia** esse envio — apenas o login por senha é bloqueado em **`SsoAuthService`**.

| Rota | Controller | Função |
|------|------------|--------|
| `GET password/reset` | `ForgotPasswordController@showLinkRequestForm` | `password.request` — formulário “Esqueci minha senha” |
| `POST password/email` | `ForgotPasswordController@sendResetLinkEmail` | Envia link via **Brevo** (`BREVO_API_KEY`) |
| `GET /reset-password/{token}` | view | Formulário nova senha (query `email`) |
| `POST password/reset` | `ForgotPasswordController@reset` | `password.update` — grava senha (mín. 8 caracteres) |

**Pré-requisitos:** `config('services.brevo.api_key')` e `mail.from.address` configurados. Link no e-mail: `{APP_URL}/reset-password/{token}?email=...`.

**Telas:** `sessions/password/verify.blade.php`, `sessions/password/reset.blade.php`. Link na login: `route('password.request')`.

**Observação:** após reset bem-sucedido, usuário pode permanecer `auth_tipo = senha` ou `hibrido`; não altera vínculo SSO (`provider_id`).

### 6.7 Rotas relacionadas (login)

- Resource **`admin.users`** (sem `show`).
- **`admin.users.search`**, **`admin.users.fetch`**.
- Fora do grupo admin com prefixo diferente: **`user.reset.password`**, **`user.change.password`**.

### 6.8 Sidebar / representante

- Link **representantes** condicionado a **`tenant->is_admin_tenant`** e expressão **`!Auth()->user()->representante->tipo == 'R'`** (precedência de PHP: comparação antes do `!` — **lógica provavelmente não é a intencionada**; revisar se o menu não aparece quando deveria).

---

## 7. `TipoPessoa` (`tipopessoa`)

Model com **`TenantScope`**, campos `idempresa`, `tipopessoa`, `status`, scope **`active()`**. Pouco ou nada acoplado ao fluxo principal dos controllers de cadastro analisados (além da injeção não usada no `PessoaController::create`).

---

## 8. Referência rápida de arquivos

| Arquivo | Função |
|---------|--------|
| `app/Models/Pessoa.php` | Scopes, fillable, empresa/representante. |
| `app/Http/Controllers/Admin/PessoaController.php` | Rotas `pessoas/{tipo}`, CNPJ, `get` JSON. |
| `app/Http/Controllers/Admin/FornecedorController.php` | CRUD fornecedor. |
| `app/Http/Controllers/Admin/ClienteController.php` | CRUD cliente, filtro por representante logado. |
| `app/Http/Controllers/Admin/FuncionarioController.php` | CRUD funcionário, motivos admissão/demissão. |
| `app/Http/Controllers/Admin/RepresentanteController.php` | CRUD representante. |
| `app/Services/PessoaPadraoService.php` | Pessoa espelho da empresa. |
| `app/Services/PessoaService.php` | Variante de listagem JSON por empresa (sem scope funcionário/cliente). |
| `app/Livewire/PessoaModal.php` | Modal rápido (fornecedor). |
| `app/Models/User.php` | Auth, roles, tenant, representante. |
| `app/Http/Controllers/Admin/UserController.php` | CRUD usuário, senha e autenticação SSO. |
| `app/Services/SsoAuthService.php` | Regras login senha/SSO. |
| `app/Http/Controllers/SocialAuthController.php` | OAuth redirect/callback. |
| `app/Http/Controllers/SessionsController.php` | Login por senha (+ bloqueio SSO-only). |
| `app/Http/Controllers/ForgotPasswordController.php` | Recuperação de senha (Brevo). |
| `resources/views/admin/partials/auth-campos-usuario.blade.php` | Campos auth no form de usuário. |
| `resources/views/admin/pessoas/_form.blade.php` | Form compartilhado. |

---

*Documento baseado na leitura do código. Corrija rotas quebradas e inconsistências de id em partials conforme prioridade do produto.*
