# Rota `lancamentos` — menu da tela de opções de lançamento por disciplina

Implementa o pedido: "ao clicar na disciplina, abrir a página com as opções de lançamentos".
O front busca, para a disciplina (regência) clicada na tela inicial, a relação de menus
(nome, link, ícone) configurados para ela, via `GET /api/v1/lancamentos`.

## 1. Endpoint

| Método | Rota | Middleware | Descrição |
|---|---|---|---|
| GET | `/api/v1/lancamentos` | `identify.tenant`, `ensure.session.tenant`, `auth:sanctum` | Retorna os menus (nome, link, icone) da disciplina/ano informados |

Parâmetros via query string, validados por `LancamentoMenuRequest`:

| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| `ano` | integer | sim | Ano letivo (chave `ano` do retorno de `GET /api/v1/main`) |
| `regencia` | integer | sim | `regencia_id` da disciplina clicada (dentro de `disciplinas` no retorno de `GET /api/v1/main`) |

## 2. Fluxo de dados

```
GET /api/v1/lancamentos?ano=&regencia=
  → identify.tenant / ensure.session.tenant / auth:sanctum
    → LancamentoMenuRequest        (valida ano/regencia)
      → LancamentoMenuDTO::fromRequest
        → LancamentoController::index
          → LancamentoMenuServiceContract::getMenus(dto)
            → LancamentoMenuRepositoryInterface::findByAnoRegencia(ano, regencia)
              → DB::connection('tenant')->select(...) na consulta fornecida
                (join e2ti.portal_professor_config_links / e2ti.portal_professor_links
                 com escola.regencia / escola.turma / escola.calendario)
          ← Collection de {nome, link, icone}
        ← LancamentoMenuResource::collection(...)
```

Diferente da rota `/main`, a consulta desta rota não é feita através de um Model Eloquent
(a combinação `ano` + `regencia` é dinâmica por requisição, então não faz sentido modelar
como uma view fixa): o Repository usa `DB::connection('tenant')->select()` com bindings
nomeados (`:ano`, `:regencia`). Como essa consulta não passa pelo ciclo de vida de um
Model Eloquent, o listener global de conversão UTF-8
(`App\Listeners\ConvertEloquentAttributesToUtf8`, que só escuta `eloquent.retrieved`) não
se aplica — por isso o Repository chama `App\Facades\Utils::convertToUtf8()` (ver
`docs/00007 - Facade Utils (convertToUtf8).md`) em cada linha antes de devolver, convertendo
cada string de Latin-1 para UTF-8 do mesmo jeito que o listener faz para os Models.

## 3. Arquivos criados

| Arquivo | Camada | O que faz |
|---|---|---|
| `app/Repositories/Contracts/LancamentoMenuRepositoryInterface.php` | Repository (contrato) | Define `findByAnoRegencia(ano, regencia)` |
| `app/Repositories/EloquentLancamentoMenuRepository.php` | Repository | Executa a consulta fornecida via `DB::connection('tenant')->select()`, convertendo cada string para UTF-8 |
| `app/DTOs/LancamentoMenuDTO.php` | DTO | Empacota `ano`/`regencia` já validados e convertidos para inteiro |
| `app/Http/Requests/LancamentoMenuRequest.php` | FormRequest | Valida `ano`/`regencia` (obrigatórios, inteiros) |
| `app/Services/Contracts/LancamentoMenuServiceContract.php` | Service (contrato) | Define `getMenus(LancamentoMenuDTO $dto)` |
| `app/Services/LancamentoMenuService.php` | Service | Delega ao Repository (sem regra de negócio adicional por enquanto) |
| `app/Http/Resources/LancamentoMenuResource.php` | Resource | Formata cada item como `{ nome, link, icone }` |
| `app/Http/Controllers/Api/V1/LancamentoController.php` | Controller | `index()`: valida via FormRequest, monta o DTO, chama o Service, devolve `LancamentoMenuResource::collection(...)` |
| `docs/00005 - Rota Lancamentos (Menu por Disciplina).md` | Documentação | Este arquivo |
| `tests/Feature/Api/V1/LancamentoControllerTest.php` | Teste (Feature) | `GET /lancamentos`: tenant ausente, autenticação ausente, validação de `ano`/`regencia`, resposta de sucesso (Service mockado) |
| `tests/Unit/Services/LancamentoMenuServiceTest.php` | Teste (Unit) | Confirma que o Service delega ao Repository com `ano`/`regencia` do DTO |

## 4. Arquivos modificados

| Arquivo | Mudança | Motivo |
|---|---|---|
| `routes/api.php` | Novo `GET /lancamentos` no grupo autenticado (`ensure.session.tenant` + `auth:sanctum`) | Expõe o endpoint |
| `app/Providers/AppServiceProvider.php` | Novos bindings (`LancamentoMenuRepositoryInterface` → `EloquentLancamentoMenuRepository`, `LancamentoMenuServiceContract` → `LancamentoMenuService`) | Injeção de dependência das novas camadas |
| `docs/00000 - Rotas.md` | Documenta o novo endpoint (seção 4 e resumo da seção 5) | Manter a lista de rotas atualizada |

## 5. Front-end

O front (`professor-front`) chama esta rota ao clicar em uma disciplina na tela inicial
(`Portal`/`LotacaoAccordion`), navegando para uma página que lista os menus recebidos como
cartões clicáveis (ícone + nome), usando o `link` de cada item. Os arquivos de ícone
referenciados pelo campo `icone` (ex.: `conteudo-programatico.svg`) devem ser adicionados em
`public/lancamentos-icons/` no front — ver `README.md` dentro dessa pasta.

## 6. Testes

```
composer test
# ou
php artisan test tests/Feature/Api/V1/LancamentoControllerTest.php tests/Unit/Services/LancamentoMenuServiceTest.php
```
