# CLAUDE.md — API SCOA

Sistema de Coordenação de Operações de Embarque da Allink. API REST em Laravel 12 para gestão de logística marítima (FCL), pré-bookings, cotações, pickup, acompanhamento de embarques e integrações com Teams, GCS e sistemas legados.

**Homologação:** `dev.allinkscoa.com.br`

---

## ⚠️ BANCO DE DADOS — REGRAS CRÍTICAS

> O ambiente "local" conecta a um **MariaDB REMOTO** (`DB_HOST` no `.env`, base `GERAIS`). **Não é um banco descartável** — qualquer comando atinge dados reais de homologação/produção.

**É ESTRITAMENTE PROIBIDO** (a não ser que o usuário peça explicitamente):

- Rodar `php artisan migrate` ou qualquer variante (`--force`, `--seed`, `--fresh`, `--step`)
- Rodar `php artisan migrate:rollback`, `migrate:reset`, `migrate:refresh`, `migrate:fresh`
- Rodar `php artisan db:wipe`
- Executar qualquer comando SQL de `DROP`, `TRUNCATE`, `DELETE` sem `WHERE`, `ALTER TABLE` destrutivo
- Criar, modificar ou deletar tabelas diretamente no banco
- Rodar `php artisan tinker` com comandos que escrevam no banco
- Qualquer ação que possa modificar ou apagar dados do banco de homologação

---

## Stack & Tecnologias

- **Framework:** Laravel 12 (PHP 8.2 — imagem `php:8.2-fpm`)
- **Autenticação:** JWT via `tymon/jwt-auth` (TTL de 1 ano)
- **Banco:** MariaDB remoto (multi-schema — ver seção de banco). `DB_CONNECTION=mariadb`
- **Queue:** driver `database`
- **Cache:** driver `file` (`CACHE_STORE=file`)
- **Storage:** Google Cloud Storage
- **PDF:** dompdf
- **Boleto:** openboleto/openboleto

---

## Docker (ambiente local)

```bash
docker compose up -d --build       # subir (build do Dockerfile + nginx)
docker compose exec app php artisan migrate
docker compose exec app php artisan <comando>
docker compose logs -f app         # logs PHP-FPM
```

O `docker-compose.yml` define **apenas dois serviços**: `app` (PHP-FPM, build do `Dockerfile`) e `nginx`. **Não há serviço de banco no compose** — a API se conecta a um **MariaDB remoto** definido no `.env` (`DB_HOST`, ver seção de banco). O código é montado por volume (`.:/var/www/html`); o serviço `app` roda `composer install` + ajusta permissões de `storage`/`bootstrap/cache` e sobe `php-fpm`.

| Serviço | Imagem | Porta | Observações |
|---------|--------|-------|-------------|
| `nginx` (`api-scoa-nginx`) | `nginx:alpine` | `9091:80` | Config em `docker/nginx/app.conf`, faz `fastcgi_pass app:9000` |
| `app` (`api-scoa-app`) | build local (`php:8.2-fpm`) | `9000` (interna) | PHP-FPM; extensões: `pdo_mysql mbstring exif pcntl bcmath gd zip`; upload/post 50M |

**App URL local:** `http://localhost:9091` (`APP_URL`)

> **Containers de outro stack (legado):** ao rodar `docker ps` você também verá `mysql-scoa` (`mariadb:10.1`, porta 3307), `phpmyadmin-scoa` (porta 8081) e `scoa` (`allink/scoa`, portas 9090/9443). Esses **não pertencem a este `docker-compose.yml`** — são o sistema SCOA legado/banco compartilhado e sobem separadamente.

**Dockerfile (build de produção):** instala deps de sistema, extensões PHP, Composer, copia o código, roda `composer install --no-dev --optimize-autoloader` e expõe `9000`. Em dev o `command` do compose sobrescreve isso reinstalando deps com dev e corrigindo permissões.

**Schemas usados (no MariaDB remoto):** `api_scoa`, `GERAIS`, `CLIENTES`, `USUARIOS`, `FINANCEIRO`

---

## Estrutura de Pastas

```
app/
  Http/
    Controllers/     # 49 controllers (um por domínio)
    Middleware/      # LogApiActivity, CaptureOldDataForUpdates
  Models/            # 64 models Eloquent
  Services/          # 60+ classes de negócio (lógica aqui, não nos controllers)
    Integrations/
      Teams/         # TeamsService, PowerAutomateClient, CardBuilder
  Providers/
routes/
  api.php            # todas as rotas da API (~150 rotas)
  web.php            # formulário de pre-booking e health check
database/
  migrations/        # 9 migrations locais
docker/
  nginx/app.conf     # configuração do nginx
```

---

## Banco de Dados (Multi-Schema)

O projeto usa múltiplos schemas MariaDB. Cada model declara o schema via `protected $table`.

| Schema     | Uso principal |
|------------|---------------|
| `api_scoa` | Cache, jobs, activity logs, templates de email, pickup, pre-booking |
| `GERAIS`   | Users (`users_api_scoa`), acompanhamento de embarque, posição de navios, FCL propostas |
| `CLIENTES` | Clientes, agentes, armadores (tabelas legadas, acesso read-mostly) |
| `USUARIOS` | Usuários do sistema legado |
| `FINANCEIRO` | Dados financeiros |

> Quando criar um novo model que usa schema diferente de `api_scoa`, sempre declarar `protected $connection` e o schema no `$table`.

---

## Autenticação

- Guard: `api` (JWT)
- Model: `App\Models\User` — tabela `GERAIS.users_api_scoa`
- Claims customizados: `user_id`, `email`, `name`, `permissoes` (array de departamentos)
- Permissões carregadas de `App\Models\ScoaPermissoesAcesso`

Rotas protegidas usam middleware `auth:api`. Rotas públicas não têm middleware de auth.

---

## Controllers principais

| Controller | Responsabilidade |
|------------|-----------------|
| `AuthController` | Login, register, logout, refresh, me |
| `PreBookingController` | CRUD de pré-bookings e documentos |
| `AcompanhamentoEmbarqueController` | Tracking de embarques, timeline, finalizações, anexos |
| `BookingController` | Consulta de bookings e tarifas |
| `CotacaoController` | Cotações de importação |
| `CotacaoPublicaController` | Cotações sem autenticação |
| `CotacaoSubsidiadaController` | Cotações subsidiadas (CRUD gerencial) |
| `PropostaController` | Propostas FCL |
| `PickupController` | Preços e regras de pickup/frete terrestre |
| `PosicaoNaviosImpController` | Posição de navios importação, templates e tags |
| `EmailTemplateController` | CRUD de templates de e-mail |
| `EnviarEmailController` | Disparo de e-mails (pré-booking, template, genérico) |
| `ActivityLogController` | Logs de auditoria da API |
| `GoogleCloudStorageController` | Upload/download/delete no GCS |
| `TeamsController` | Notificações no Microsoft Teams |
| `BoletoController` | Geração de boletos bancários |
| `AgendamentoAcompanhamentoController` | Agendamentos de follow-up de embarques |
| `ProgressoAcompanhamentoController` | Progresso de embarques |
| `AtividadeCoordenacaoEmbarqueController` | Atividades de coordenação |
| `EtapaCoordenacaoEmbarqueController` | Etapas de coordenação |
| `ScoaPermissoesAcessoController` | Importação e consulta de permissões |
| `AuthController (Legacy)` | Compatibilidade com sistema legado |

---

## Models principais

| Model | Tabela | Observações |
|-------|--------|-------------|
| `User` | `GERAIS.users_api_scoa` | JWT subject, carrega permissões |
| `AcompanhamentoEmbarque` | `GERAIS.acompanhamento_embarque` | SoftDeletes |
| `FclProposta` | `GERAIS.fcl_propostas` | hasMany containers, mercadorias, taxas, contatos |
| `PreBookingFilledByAgent` | `pre_booking_filled_by_agent` | hasMany AnexoCoordenacaoExp |
| `ApiActivityLog` | `api_activity_logs` | Auditoria, scopes por usuário/endpoint/status |
| `EmailTemplate` | `email_templates` | SoftDeletes, belongsToMany AtividadeCoordenacaoEmbarque |
| `CotacaoSubsidiada` | `cotacao_subsidiada` | SoftDeletes |
| `PickupPrice` | — | belongsTo Fornecedor, hasMany PickupTableValue |
| `PosicaoNaviosImp` | `GERAIS.posicao_navios_imp` | hasMany Tags e XTags |
| `ScoaPermissoesAcesso` | — | user_ids (string CSV) → permissao_departamento |

---

## Services (lógica de negócio)

A arquitetura é service-oriented: controllers delegam toda lógica para services.

| Service | Responsabilidade |
|---------|-----------------|
| `AcompanhamentoEmbarqueService` | Tracking completo (~31KB, mais complexo) |
| `AgendamentoAcompanhamentoService` | Agendamentos e histórico (~31KB) |
| `CotacaoService` | Cálculo de cotações, chamadas à API de tarifas (~15KB) |
| `PropostaService` | Propostas FCL (~19KB) |
| `EmailTemplateService` | Templates e variáveis (~14KB) |
| `ActivityLogService` | Registro de logs de API (~13KB) |
| `BuscarFretesService` | Busca de fretes terrestres (~11KB) |
| `GoogleCloudStorageService` | Operações no GCS (~10KB) |
| `CepService` | CEP via BrasilAPI + distâncias via IBGE (~10KB) |
| `SerproMonitoringService` | Detecção de anomalias SERPRO (~13KB) |
| `CotacaoSubsidiadaService` | Cotações subsidiadas (~8KB) |
| `AtribuicaoCoordenacaoExpService` | Atribuições de coordenação exportação (~8KB) |
| `CoordenadorIndicadoresService` | Indicadores de carga de coordenadores (~9KB) |
| `Integrations\Teams\TeamsService` | Notificações Teams via Power Automate |
| `LegacyAuthService` | Compatibilidade auth legado |

---

## Integrações Externas

| Integração | Env Vars | Uso |
|-----------|----------|-----|
| Microsoft Teams | `TEAMS_INTEGRATION_ENABLED`, Power Automate webhook | Notificações de eventos |
| Google Cloud Storage | `GOOGLE_APPLICATION_CREDENTIALS` | Documentos de embarque |
| BrasilAPI | nenhuma | Lookup de CEP |
| IBGE API | nenhuma | Cidades e distâncias |
| API Tarifas interna | `PROPOSTA_API_URL`, `PROPOSTA_API_TOKEN`, `PROPOSTA_COOKIE` | Cotações e tarifas |
| SMTP Allink | `MAIL_*` | Envio de e-mails |
| Gemini AI | `GEMINI_API_KEY`, `GEMINI_MODEL` | Em desenvolvimento |
| SERPRO | nenhuma | Monitoramento de anomalias aduaneiras |

---

## Middleware

| Middleware | Função |
|-----------|--------|
| `LogApiActivity` | Registra toda requisição/resposta em `api_activity_logs` (global) |
| `CaptureOldDataForUpdates` | Captura estado anterior a updates para auditoria de mudanças |

---

## Rotas — Grupos principais (`routes/api.php`)

| Prefixo | Auth | Descrição |
|---------|------|-----------|
| `/api/auth` | misto | Login, register, logout, refresh, me |
| `/api/prebooking` | sim | CRUD pré-bookings + tracking de embarques |
| `/api/posicao-navios-imp` | sim | Posição de navios, templates, tags |
| `/api/comercial/importacao` | misto | Cotações, subsidiadas, tarifário, portos |
| `/api/pickup` | misto | Preços e fretes de pickup terrestre |
| `/api/embarques` | sim | Progresso e agendamentos de embarques |
| `/api/booking` | sim | Consulta de booking e tarifas |
| `/api/email-templates` | sim | CRUD templates de e-mail |
| `/api/enviar/email` | não | Disparo de e-mails |
| `/api/bucket` | misto | Upload/download GCS |
| `/api/teams` | misto | Notificações Teams |
| `/api/activity-logs` | sim | Auditoria da API |
| `/api/proposta/fcl` | misto | Propostas FCL |
| `/api/boleto` | misto | Geração de boleto |
| `/api/serpro` | não | Monitoramento SERPRO |
| `/api/public/info` | não | Health check |
| Lookups de referência | misto | Clientes, agentes, portos, armadores, incoterms, países, NCM |

---

## Campo novo na coordenação de embarque — checklist

Ao adicionar um campo em `GERAIS.acompanhamento_embarque`, ele **não** fica disponível
sozinho nas telas de configuração. Sem os passos abaixo o campo existe, salva e some — quem
monta regras e templates nunca o enxerga.

1. **Banco** — `ALTER TABLE` (aplicar **em produção**: dev é sobrescrito toda madrugada por
   uma cópia de produção, então DDL só em dev se perde).
2. **Model** `AcompanhamentoEmbarque` — `$fillable` e `$casts`.
3. **Validação** — `AcompanhamentoEmbarqueRequest`.
4. **Tela** — aba correspondente em `scoa-frontend/src/components/coordenacao-embarque-exp/
   components/tabs/` + o tipo em `src/types/coordenacao-embarque-exp/embarque.ts`.
5. **Variável de e-mail** (todo campo) — `emailTemplateVariables.ts` (a lista da tela) **e**
   `app/Mail/EmailTemplateMail.php` (o mapa de substituição). Listar sem mapear faz a
   variável aparecer para o usuário e sair vazia no e-mail enviado.
6. **Condição de tarefa** (só campos Sim/Não) — `AtividadeCondicao::CAMPOS_CONDICIONAIS` **e**
   o espelho `CAMPOS_CONDICIONAIS` em `gestao/services/activitiesApi.ts`, para o campo virar
   opção em Gestão > Tarefas > "Condições para criação automática".

7. **Coleção em tabela própria** (containers, IMOs...) — além dos passos acima, entrar em
   `RELACOES_POR_MODEL` no middleware `CaptureOldDataForUpdates`. Sem isso o estado anterior
   não carrega a relação, a comparação de alterações a descarta como "campo que não é do
   recurso", e mexer nela **não aparece na timeline**. Já aconteceu duas vezes: com `imos`
   e depois com `containers`.

Campos Sim/Não devem sair por extenso ("Sim"/"Não") no mapa do `EmailTemplateMail` — `1`/`0`
não diz nada a quem lê o e-mail. Referência: `is_dta`, que percorreu os seis passos.

---

## Convenções do projeto

- **Controllers** são finos: recebem request, delegam ao service, retornam JSON.
- **Services** contêm toda lógica de negócio — é onde o trabalho real acontece.
- **Models** com SoftDeletes usam `deleted_at`.
- **Locale:** `pt_BR`, timezone `America/Sao_Paulo`.
- **Responses JSON** seguem o padrão `{ data: ..., message: ... }` ou similar por controller.
- **Queue** usa driver `database`; **cache** usa driver `file` (sem Redis em dev).
- **Logs** ficam em `storage/logs/` com canal `stack` → `single`.
