GestorEasy v3 — ERP SaaS (esqueleto inicial)

Visão Geral
- Backend em PHP 8 com arquitetura modular e multi-tenancy.
- Banco de dados MySQL com isolamento por tenant (um BD por tenant).
- Foco em qualidade de integração entre módulos e evolução incremental.

Arquitetura
- Core: roteamento simples, contexto de tenant, gerenciador de conexões e carregador de módulos.
- Multi-tenancy: resolução do `tenantId` via header `X-Tenant-Id` ou subdomínio.
- Módulos: `Clientes`, `Produtos`, `Compras`, `Vendas`, `PDV` (skeletons iniciais).

Novidades (Produtos)
- Geração de SKU: botão "Gerar SKU" ao lado do campo Código no cadastro de produto. Verificação de unicidade via endpoint `GET /api/produtos/check-codigo?codigo=<SKU>` (JWT + permissão `produtos.read`).
- Calculadora embutida: calculadora de preço integrada na aba "Preço" (modal removido), com aplicação direta dos valores ao formulário.
- Sugestões CEST: dataset extra em `public/assets/cest_extra.json` e sugestão em tempo real por prefixo de NCM (inclui 3 primeiros dígitos).
- Melhorias de UX: formatação BRL, resumo dinâmico da composição de custos e markup auto.

Primeiros Passos
1) Configuração do ambiente
   - Copie `.env.example` para `.env` e ajuste credenciais de banco.
   - Crie um banco por tenant seguindo o prefixo: `gestoreasy_tenant_{id}` (ex.: `gestoreasy_tenant_default`).
   - Defina `JWT_SECRET`, `JWT_TTL_SECONDS` e `JWT_REFRESH_TTL_SECONDS` para o fluxo de autenticação.
2) Criação de tabelas
   - Execute os scripts em `modules/<Modulo>/migrations/*.sql` dentro do banco do tenant desejado.
3) Subir servidor PHP embutido
   - `php -S localhost:8000 -t public`
   - Teste `http://localhost:8000/health`.
   - Seed de admin (apenas em `APP_ENV=local`): `POST /api/auth/seed-admin` com body `{"email":"admin@local","password":"admin123"}` e `X-Tenant-Id: default`.
    - Login: `POST /api/auth/login` com body `{"email":"admin@local","password":"admin123"}`. Use o `access_token` retornado no header `Authorization: Bearer <token>`.

Auto-migração por tenant
- O Kernel aplica migrações pendentes automaticamente no início de cada requisição usando `schema_migrations` para rastrear versões aplicadas.
- Logs de auditoria são registrados para cada migração executada.
- Falhas menores são toleradas e não impedem resposta de rotas como `/health`.
   - Teste `GET /api/clientes` com `Authorization` e `X-Tenant-Id: default`. Paginação por `page` e `per_page`, filtro por `q`.

Decisões de Projeto
- Isolamento por BD por tenant para simplificar conformidade fiscal e segurança.
- Roteador minimalista para acelerar início; poderá ser substituído por framework futuramente.
- Autenticação: middleware planejado (JWT) — placeholder no momento.

Módulos e Conformidade
- Brasileiros: campos como CPF/CNPJ, EAN, NCM, PIS, COFINS, CEST, CFOP serão suportados.
- NF-e/NFC-e/SAT/ECF: integrações planejadas com camadas dedicadas.

Autenticação
- JWT HS256 com `JWT_SECRET`, escopo por tenant.
- Rotas: `POST /api/auth/login`, `POST /api/auth/refresh`, `POST /api/auth/seed-admin` (somente local).
- RBAC básico: tabelas `roles`, `permissions`, `user_roles`, `role_permissions`. Seed cria usuário admin e vincula papel `admin`.

Certificados Fiscais (A1/A3)
- Configure `CERT_STORE_KEY` no ambiente (.env) com 32 bytes (recomendado usar 64 hex chars) para criptografia AES-GCM do repositório de certificados.
- MFA TOTP para gerenciamento: use as rotas `POST /api/auth/mfa/setup`, `POST /api/auth/mfa/enable`, `POST /api/auth/mfa/verify` e envie `x-mfa-token` nas operações de certificados.
- Endpoints principais:
  - `GET /api/fiscal/certificates` — lista metadados
  - `POST /api/fiscal/certificates/a1` — cadastra A1 `{ cnpj, environment, pfx_b64, password }`
  - `POST /api/fiscal/certificates/a3` — cadastra A3 `{ cnpj, environment, module_path, token_label, cert_label, pin? }`
  - `GET /api/fiscal/certificates/{id}` — detalhe (sem payload)
  - `DELETE /api/fiscal/certificates/{id}` — remove
  - `POST /api/fiscal/certificates/{id}/backup` — gera backup criptografado (JSON) em `CERT_BACKUP_DIR` (padrão `modules/storage/backups/certificates/<tenant>`)


Quickstart (Local)
- Criar admin padrão e permissões: `POST /api/auth/seed-admin` com body opcional `{ "email": "admin@local", "password": "admin123", "name": "Admin" }`.
- Fazer login de usuário do tenant: `POST /api/auth/login` com `{ "email": "admin@local", "password": "admin123" }` e header `x-tenant-id: default`.
- Guardar `access_token` em `localStorage` e cookie `auth_token` para navegação HTML (feito automaticamente na página `/login`).
- Ver payload do usuário autenticado: `GET /api/auth/me` com `Authorization: Bearer <token>` e `x-tenant-id: default`.

Debug e Desenvolvimento
- Endpoints de debug:
  - `GET /debug/routes` — lista todas as rotas registradas e middlewares.
  - `GET /debug/modules-status` — mostra módulos carregados e seus estados.
  - `GET /debug/modules` — lista os módulos detectados no disco.
- Requisitos de autenticação/tenant para APIs de negócio:
  - Header `x-tenant-id: <tenant>` obrigatório para escopo de banco por tenant (ex.: `default`).
  - Header `Authorization: Bearer <access_token>` obrigatório, exceto para rotas públicas (ex.: `/health`, `/login`).
  - Para HTML, o cookie `auth_token` é usado (definido no fluxo de `/login`).
- Seed local rápido (somente quando `APP_ENV=local`):
  - `POST /api/auth/seed-admin` cria usuário admin padrão no tenant atual, a role `admin` e vincula permissões base (`clientes.*`, `produtos.*`, etc.).
  - Exemplo cURL:
    ```bash
    curl -X POST http://localhost:8000/api/auth/seed-admin \
      -H "Content-Type: application/json" \
      -H "x-tenant-id: default" \
      -d '{"email":"admin@local","password":"admin123","name":"Admin"}'
    ```
- Login e uso do token:
  ```bash
  TOKEN=$(curl -s -X POST http://localhost:8000/api/auth/login \
    -H "Content-Type: application/json" \
    -H "x-tenant-id: default" \
    -d '{"email":"admin@local","password":"admin123"}' | jq -r .access_token)
  curl -H "authorization: Bearer $TOKEN" -H "x-tenant-id: default" http://localhost:8000/api/produtos
  ```
- Migrações por tenant:
  - O Kernel executa automaticamente migrações pendentes no início de cada requisição (`schema_migrations`).
  - Falhas menores (ex.: índice já existe, diferenças de dialeto) são toleradas e não interrompem o fluxo; as próximas migrações continuam a ser aplicadas.
  - Para investigar, consulte a tabela `schema_migrations` do banco do tenant (ex.: `gestoreasy_tenant_default`).
- Rodando testes locais:
  - Os testes são scripts PHP simples. Exemplos:
    - `php tests/ProductsEndpointsTest.php`
    - `php tests/AuthFlowTest.php`
  - Garanta que `.env` aponte para um MySQL acessível e que o usuário tenha permissão de criar DBs (para criação de `gestoreasy_tenant_<tenant>` quando necessário).
  - Caso obtenha `403 Forbidden`, verifique se o usuário autenticado possui as permissões via RBAC; use o seed acima para papel `admin` e permissões base.

RBAC (Gestão)
- Endpoints (requer `admin`):
  - `GET /api/auth/roles` — lista papéis
  - `GET /api/auth/permissions` — lista permissões
  - `POST /api/auth/roles` body `{"name": "sales"}` — cria papel
  - `POST /api/auth/permissions` body `{"name": "clientes.read"}` — cria permissão
  - `POST /api/auth/role-permissions` body `{"role":"admin","permission":"clientes.read"}` — vincula permissão ao papel
  - `POST /api/auth/user-roles` body `{"user_id":1,"role":"admin"}` — vincula papel ao usuário

Permissões padrão
- `clientes.read|create|update|delete`
- `produtos.read|create|update|delete`
- `compras.read|create`
- `vendas.read|create`
- `pdv.read`
- `rbac.manage` (reservada para operações administrativas)

Paginação
- Listagens retornam `data`, `page`, `per_page`, `total` e `total_pages`.

Endpoints Clientes
- `GET /api/clientes` — requer `clientes.read`; suporta `page`, `per_page`, `q`
- `GET /api/clientes/{id}` — requer `clientes.read`
   - Suporta cache de cliente no navegador via `ETag`/`If-None-Match` (retorna `304` quando não alterado)
   - Inclui cabeçalho `Last-Modified` baseado em `updated_at` (ou `created_at` quando não houver atualização)
   - Aceita `If-Modified-Since` (RFC 7232). Se não houver mudanças desde a data enviada, retorna `304`.
- `POST /api/clientes` — requer `clientes.create`; valida `CPF/CNPJ`
- `PUT /api/clientes/{id}` — requer `clientes.update`; valida `CPF/CNPJ` quando enviados
- `DELETE /api/clientes/{id}` — requer `clientes.delete`
  - `GET /api/clientes/cnpj/{cnpj}` — requer `clientes.create`; consulta dados públicos do CNPJ (BrasilAPI com fallback) para auto-preenchimento
    - Cache por tenant: tabela `cnpj_cache` com TTL configurável por `CNPJ_CACHE_TTL_SECONDS` (padrão 24h). Use `?refresh=true` para forçar atualização.
    - Proteção de refresh: mínimo configurável por `CNPJ_REFRESH_MIN_SECONDS` (padrão 60s). Resposta `429` com `retry_in_seconds` quando acionado cedo.

Endpoints Produtos
- `GET /api/produtos` — requer `produtos.read`; suporta `page`, `per_page`, `q`
- `GET /api/produtos/{id}` — requer `produtos.read`
   - Suporta cache de produto no navegador via `ETag`/`If-None-Match` (retorna `304` quando não alterado)
   - Inclui cabeçalho `Last-Modified` baseado em `updated_at` (ou `created_at` quando não houver atualização)
   - Aceita `If-Modified-Since` (RFC 7232). Se não houver mudanças desde a data enviada, retorna `304`.
- `POST /api/produtos` — requer `produtos.create`; valida `NCM`, `EAN`, `CFOP` quando enviados
- `PUT /api/produtos/{id}` — requer `produtos.update`; valida `NCM`, `EAN`, `CFOP` quando enviados
- `PATCH /api/produtos/{id}` — requer `produtos.update`; atualização parcial dos campos enviados
- `DELETE /api/produtos/{id}` — requer `produtos.delete`

Erros e respostas
- Padrão de erro: `{ "error": { "code": "codigo", "message": "mensagem" } }`
- Códigos comuns: `invalid_id`, `not_found`, `invalid_cpf`, `invalid_cnpj`, `invalid_ncm`, `invalid_ean`, `invalid_cfop`, `cnpj_service_unavailable`, `refresh_too_soon`

Convenções de API
Para detalhes completos sobre autenticação, envelopes de resposta, paginação, validações brasileiras, cache condicional e outras convenções, consulte [docs/modules.md](docs/modules.md).

Auditoria
- Logs de ações: `create`, `update`, `delete`, `read` (opcional), além de `login_success` e `login_failed`.
- Tabela: `audit_logs` com índices otimizados; `user_id` é opcional.
- Endpoints administrativos (requer `admin`):
  - `GET /api/audit/{resource}?resource_id=<id>&limit=<n>` — trilha de auditoria por recurso/ID.
  - `POST /api/audit/prune` body `{"days": 30}` — limpeza de logs antigos; retorna `{"deleted": <n>}`.
- Configuração: `AUDIT_LOG_READS=false` por padrão para evitar volume excessivo.
  - Quando `AUDIT_LOG_READS=true`, leituras de detalhe e listagens de `clientes` e `produtos` são registradas (nas listagens, `resource_id` fica `null` com metadados como `endpoint`, `page`, `per_page`, `q`).

Rate Limiting
- Middleware global com proteção reforçada para rotas sensíveis:
  - `POST /api/auth/login` — limite por IP 10 req/min e por tenant 50 req/min.
  - `POST /api/auth/refresh` — limite por IP 20 e por tenant 100.
  - `GET /api/clientes/cnpj/{cnpj}` — limite por IP 5 e por tenant 20.
- Variáveis: `RATE_LIMIT_PER_IP`, `RATE_LIMIT_PER_TENANT`, `RATE_LIMIT_WINDOW_SECONDS`.

Testes Mínimos
- Pré-requisitos: PHP 8; `.env` carregado via `bootstrap.php`. Para auditoria, o banco do tenant deve existir e ter migrações aplicadas.
- Executar:
  - `php tests/RateLimitMiddlewareTest.php` — valida bloqueio após 10 tentativas de login por IP.
- `php tests/AuditLoggerTest.php` — valida robustez do logger (não lança exceção) e retorno do prune.

Admin (Secret)
- Endpoints adicionais (requer header `x-admin-secret`):
  - `POST /api/admin/tenants/{tenant}/seed-demo` — cria dados de exemplo (clientes e produtos) se não existirem.

Metodologia
- Incremental, versão semântica, testes por módulo, documentação contínua.

Licença
- Projeto interno (não definir licença pública por enquanto).

Tema e Logo
- Paleta padrão:
  - `preto` (`#1c1c1c`), `laranja` (`#ff6a00`), `verde oliva` (`#556b2f`), `branco` (`#ffffff`).
  - Gradientes sugeridos: `--gradient-primary` (laranja → oliva), `--gradient-warm`, `--gradient-olive`.
- Arquivos:
  - CSS: `public/css/theme.css` com variáveis e utilitários.
  - Demo: `public/theme.html` para validar combinação de fundo e logo.
  - Logo: coloque `public/assets/logo-dark.png` (para fundo claro) e `public/assets/logo-light.png` (para fundo escuro/gradiente).
- Convenções:
  - Use `logo-dark` em containers `.bg-light` e `logo-light` em `.bg-dark`/`.bg-gradient`.
  - Texto: `var(--text-on-light)` em fundo claro e `var(--text-on-dark)` em fundo escuro/gradiente.