# Documentação técnica — Tenants (multi-inquilino)

Este documento descreve a entidade **`tenant`**, o vínculo com **usuário**, **empresa** e **plano**, o **`TenantScope`** (isolamento de dados), a sessão **`selectedTenant`**, o cadastro administrativo e o **provisionamento** ao criar um tenant (observer, seeders). Documentos relacionados: **`Doc_Modulo_Pessoa_Usuario.md`** (usuário `idtenant`), **`Doc_Modulo_Status_Fluxo_Cores.md`** (fluxo/status por tenant), **`Doc_Modulo_Produto_Servico.md`** (Grupo Conta por `idtenant`).

---

## 1. Model `Tenant` (`tenant`)

| Aspecto | Detalhe |
|---------|---------|
| PK | `idtenant` |
| Timestamps | `criadoem`, `alteradoem` |
| **Escopo global** | `TenantScope` registrado no model — ver § 3 (comportamento especial para a própria tabela `tenant`). |
| **Fillable** | `nome`, `status`, **`is_admin_tenant`**, `idpessoa`, **`idplano`**, **`auth_padrao`**, **`sso_provedores`**, **`permitir_senha_local`** (coluna legada `tipo` VENDAS/RECEITAS existe no banco mas **não é usada** pelo ERP) |

**Relações:** `empresas()`, `pessoa()` (`idpessoa`), `plano()`.

**Flags:**

- **`is_admin_tenant`:** quando verdadeiro, o middleware **`admin.tenant`** libera rotas reservadas (ex.: CRUD de tenants no admin).
**Status:** valores usados nas telas admin: `ATIVO`, `INATIVO`.

---

## 2. Usuário e empresa

- **`users.idtenant`** — cada usuário pertence a **um** tenant. O **`TenantScope`** na tabela `users` permite ver **o próprio usuário** **ou** usuários cujo `idtenant` coincide com o tenant “ativo” (ver § 3.1).
- **`empresa.idtenant`** — empresas são filhas do tenant. Modelos que têm **`idempresa`** são filtrados indiretamente: existe **`whereExists`** contra **`empresa`** com `empresa.idtenant = selectedTenantId` (ou `user.idtenant`).

Cadastro de usuário no admin (**`UserController::store`**) define `idtenant` = **`auth()->user()->idtenant`** (novo usuário no mesmo tenant do operador).

---

## 3. `TenantScope` (`App\Scopes\TenantScope`)

Aplica-se a **dezenas** de models que chamam `addGlobalScope(new TenantScope)` no `booted`.

### 3.1 Ramo **`users`**

Se o model é **`users`** e há usuário autenticado:

```text
WHERE (id = usuário_logado) OR (users.idtenant = selectedTenantId)
```

`selectedTenantId` = `session('selectedTenant')->idtenant` se existir, senão **`user->idtenant`**.

### 3.2 Models com coluna **`idempresa`** (exceto lista de exceções)

Inclui, por exemplo, `nf`, `pessoa`, `produtoservico`, `contrato`, etc. **Não** inclui `upload`, `empresa`, `tenant`, `users`, `funcionalidades`, `planos`, `plano_funcionalidade`, `timetracking`.

Filtro:

```sql
EXISTS (SELECT 1 FROM empresa WHERE empresa.idempresa = <tabela>.idempresa AND empresa.idtenant = <selectedTenantId>)
```

### 3.3 Demais models com **`idtenant`** na tabela

Se a tabela **não** está na lista de exclusão (`tenant`, `funcionalidades`, `planos`, `plano_funcionalidade`, `timetracking`):

```text
<tabela>.idtenant = selectedTenantId
```

### 3.4 Tabela **`tenant`**

A própria tabela **`tenant`** está na **lista de exclusão**: o escopo **não** adiciona `WHERE idtenant = …` ao consultar **`Tenant`**. Na prática, **`Tenant::query()`** pode retornar **todos** os tenants do banco, salvo filtros explícitos no controller — o que condiz com a tela de **lista global** de instâncias para quem tem acesso ao módulo.

### 3.5 `withoutGlobalScope(TenantScope::class)`

Usado pontualmente (seeders, observers, helpers) quando é necessário ignorar o isolamento.

---

## 4. Sessão `selectedTenant`

Vários pontos leem **`session('selectedTenant')`** como objeto (esperado: model **`Tenant`** ou equivalente com `idtenant`):

- `TenantScope`, `FluxoService::getIdTenant`, sidebar (`$tenant = session('selectedTenant') ?? auth()->user()->tenant`).
- Controllers de **fluxo**, **status-fluxo**, **paleta de cores**, **finalidade fiscal**, **grupo conta**, etc.: `session('selectedTenant')->idtenant ?? auth()->user()->idtenant`.

**Observação:** no código pesquisado **não** foi encontrado `session()->put('selectedTenant', …)`. Se a chave nunca for gravada, o comportamento efetivo é sempre **`auth()->user()->idtenant`** (e `user->tenant` na sidebar). Se no futuro existir **troca de tenant** na UI, deve-se garantir persistência consistente dessa sessão e revisar segurança (apenas usuários `is_admin_tenant` ou lista permitida).

---

## 5. Middleware `admin.tenant` (`AdminTenantMiddleware`)

- Permite a requisição se **`$user->tenant->is_admin_tenant == 1`**.
- Caso contrário, redireciona ao **`dashboard`** com “Acesso negado.”

**Rotas:** em `web.php`, o grupo de **`tenants`** (procurar, fetch, resource) está dentro de `middleware(['admin.tenant'])` — só **tenant administrador da plataforma** gerencia instâncias.

---

## 6. `TenantController` (admin)

- **Middleware:** `admin.tenant` no construtor.
- **Dashboard de assinaturas:** `GET /admin/tenants/dashboard` — métricas apenas de contratos com **`modalidade = RECORRENCIA`**. Menu **Painéis → Assinaturas**. Ver **`Doc_Modulo_Assinatura_Contrato.md`**.
- **Contrato de assinatura:** `tenant.idcontrato_assinatura` → registro em `contrato` com **`modalidade = RECORRENCIA`** (`AssinaturaContratoService`). Contratos comerciais (`CONTRATO`) são outro fluxo: **`Doc_Modulo_Contrato.md`**.
- **CRUD:** `index`, `create`, `store`, `edit`, `update`, `destroy`, `search`, `fetch`.
- **Validação (`store` / `update` com redirect):** `nome`, `idpessoa` opcional, `status` ATIVO/INATIVO, `idplano` obrigatório, `is_admin_tenant` via checkbox `on` → 1; na **edição**, também **`auth_padrao`**, **`sso_provedores[]`**, **`permitir_senha_local`** (ver § 11).
- **Representantes no form:** lista **`Pessoa::funcionarioOuCliente()`** (não só representantes) para vínculo `idpessoa`.
- **`fetch`:** possível inconsistência no partial de status — usa **`idformapagamento`** em vez de `idtenant` (mesmo padrão de bug visto em outros `fetch`).

---

## 7. Observer `TenantObserver` (criação)

Registrado em **`AppServiceProvider`**. Ao **`created(Tenant)`:**

1. Insere linhas em **`paleta_cor`** com cores de **`config('paleta_cores.padrao')`** (grid de hex por tenant).
2. Executa **`FluxoStatusSeeder::runForTenant($idtenant)`** — status e fluxos padrão.
3. Executa **`FinalidadeFiscalSeeder::runForTenant($idtenant)`**.
4. Executa **`CatalogoPadraoSistemaService::provisionarTenant($idtenant)`** — réplicas dos cadastros já marcados como padrão em **`tabela_padrao`** (ver **`Doc_Modulo_Catalogo_Padrao.md`**).

Ou seja, **cada novo tenant** nasce com paleta, fluxos, finalidades fiscais base e, em seguida, o que estiver ativo no catálogo padrão do sistema.

---

## 8. Cadastro público / checkout (`CadastroController`)

Fluxo de assinatura/cadastro cria **pessoa**, **`Tenant`** (com `idplano`, vínculo à pessoa), **usuário** ADMIN com `idtenant`, **empresa** com o mesmo `idtenant`, NF de plano, etc. Se o lead se cadastra via **Google/Microsoft**, o tenant recebe **`auth_padrao = sso`** e o usuário **`auth_tipo = sso`** (sem senha local). Ver § 11 e **`Doc_Modulo_Pessoa_Usuario.md`** § 6.

Detalhamento de **planos**, **funcionalidades**, **Pagar.me**, webhook e tela **Pagamentos**: **`Doc_Modulo_Planos_Assinatura_Pagamento.md`**.

**Seeders** executados na criação de tenant (e demais): **`Doc_Seeders_e_Dados_Iniciais.md`**.

Rota pública de status: **`cadastro.tenant.status`** — `GET cadastro/{tenant}/status` retorna JSON do `tenant.status` (middleware `guest`).

**Webhook** (`WebhookController`): lê `idtenant` de metadata do gateway e localiza `Tenant` para ações em lote (ex.: usuários do tenant).

---

## 9. Outras interações

- **`VendaController`:** transferência entre empresas exige que origem e destino tenham o **mesmo** `idtenant` (bloqueio cross-tenant).
- **`GrupoContaController` / `FinalidadeFiscalController`:** atribuem `idtenant` a partir de `session('selectedTenant')` ou `user->tenant`.
- **Sidebar:** menu **Tecnologia** inclui **`tenants`** para operadores com perfil adequado; condicionais usam **`tenant->is_admin_tenant`** em alguns links (ex.: representantes).

---

## 11. Autenticação SSO (instância)

Configuração na **edição do tenant** (`admin/tenants/{id}/edit` → bloco **Autenticação da instância**). Somente operadores com **`admin.tenant`**.

| Campo | Valores | Efeito |
|-------|---------|--------|
| **`auth_padrao`** | `senha`, `sso`, `hibrido` | Padrão ao **criar novos usuários** no admin (`UserController::store`). **Não altera** usuários já existentes. |
| **`sso_provedores`** | JSON: `google`, `azure` (Microsoft) | Restringe provedores permitidos no login. **Vazio/null** = todos os provedores configurados no `.env`. |
| **`permitir_senha_local`** | boolean | Se `false`, usuários **`hibrido`** não podem logar por senha (respeitado em `SsoAuthService::podeLogarComSenha`). |

**Importante:** alterar o tenant **não migra** usuários antigos. Cada **`users.auth_tipo`** deve ser ajustado individualmente (admin de usuários) ou via suporte/script. Usuário com `auth_tipo = senha` recebe *"Esta conta não está habilitada para login com Google"* ao tentar SSO.

**Cadastro público:** lead com senha → tenant `auth_padrao = senha`; lead com OAuth → tenant `auth_padrao = sso` e `sso_provedores = [provedor usado]`.

**Variáveis de ambiente** (plataforma): `SSO_HABILITADO`, `GOOGLE_*`, `MICROSOFT_*` / `azure` em `config/services.php`. Ver `config/nolapis_auth.php`.

**Arquivos:** `app/Services/SsoAuthService.php`, `app/Http/Controllers/SocialAuthController.php`, rotas `auth.social.redirect` / `auth.social.callback`, view `admin/tenants/_auth-form.blade.php`.

---

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

| Arquivo | Função |
|---------|--------|
| `app/Models/Tenant.php` | Entidade tenant. |
| `app/Scopes/TenantScope.php` | Filtro global por tenant / empresa. |
| `app/Http/Middleware/AdminTenantMiddleware.php` | Protege gestão de tenants. |
| `app/Http/Controllers/Admin/TenantController.php` | CRUD admin de instâncias. |
| `app/Observers/TenantObserver.php` | Paleta + seeders pós-criação. |
| `config/paleta_cores.php` | Cores iniciais por tenant. |
| `app/Http/Controllers/CadastroController.php` | Onboarding cliente + tenant + empresa + user (+ SSO no cadastro). |
| `app/Services/SsoAuthService.php` | Regras de login senha/SSO por usuário e tenant. |
| `app/Http/Controllers/SocialAuthController.php` | OAuth Google / Microsoft (login e cadastro). |
| `app/Http/Middleware/SingleSession.php` | Uma sessão ativa por usuário (`idsession`); independente de tenant, mas afeta login. |

---

*Documento baseado na leitura do código. Confirme nomes de colunas (`is_admin_tenant` vs `is_tenant_admin`) e política de `selectedTenant` no ambiente em produção.*
