# Requerimento de Diárias — Gaps, Bugs e Dívidas

> Achados verificados por leitura do código, `diff` entre `v5/` e `v6/` e
> inspeção do schema PostgreSQL (`prefeituras_v5`).

---

## 1. Bugs e erros conhecidos

### B1 — Injeção de SQL nos filtros do Portal (**crítico**)

**Arquivo:** `v6/app/Http/Controllers/Portal/BuscaDiariasController.php:33-44`

```php
$ids = Servidor::whereRaw("translate(lower(name), '...') ilike '%" . $request->req_name . "%'")->pluck('id');
$ids = Servidor::whereRaw("translate(lower(cpf), '...') ilike '%" . $request->cpf . "%'")->pluck('id');
$diarias = $diarias->whereRaw("CAST(codigo_diarias AS TEXT) ilike '%" . $request->num_diaria . "%'");
```

Os três valores vêm do request HTTP e são concatenados em SQL **sem binding**.
`DiariasRequest` só valida `cpf` com `nullable|min:14` — `min` não impede
aspas. Um termo com `'` fecha a string e permite injeção.

**Impacto:** leitura arbitrária do banco via rota pública.
**Correção:** `whereRaw('... ilike ?', ['%'.$termo.'%'])`.

### B2 — `padrao_arquivo_config` aponta para uma coluna inexistente

**Arquivos:** `v6/app/Http/Controllers/Prefecture/RequerimentoDiariasController.php:283`
e `v6/resources/views/prefecture/contents/requerimento-diarias/form.blade.php`

A coluna não existe em `requerimento_diarias` (confirmado com `\d`) nem está
no `$fillable` do model. O `update()` é ignorado silenciosamente.

**Impacto:** o checkbox "Usar Arquivo de Lei Padrão" não persiste estado; o
botão "Excluir Arquivo" não desmarca nada; o formulário sempre exibe o
checkbox sem marcação.

### B3 — Edição sem novo upload falha

**Arquivo:** `v6/app/Http/Controllers/Prefecture/RequerimentoDiariasController.php:153-154`

```php
$data['title'] = $request->archive->getClientOriginalName();
$archive = $request->file('archive')->storeAs('leis_de_requerimentos', $data['title']);
```

Com `$request->archive === null` (edição sem reenvio), ocorre
`Error: Call to a member function getClientOriginalName() on null`. O
`try/catch` do controller transforma isso em "Ocorreu um erro ao tentar
cadastrar o registro." — a edição é perdida sem mensagem explicativa.

Agravante: o formulário só mostra o `<input type="file">` quando **não** há
arquivo anterior; logo, um requerimento **com** arquivo não pode ser
reeditado.

### B4 — `destroyAll` sempre reporta sucesso

**Arquivo:** `v6/app/Repositories/Repository.php:99-112` (usado por
`v6/app/Http/Controllers/Prefecture/RequerimentoDiariasController.php:210`)

`Repository@destroyAll` retorna `response()->json(true|false)` — objetos
`JsonResponse`, sempre truthy. O `if` do controller nunca cai no ramo de
erro.

**Impacto:** exclusão em lote sem ids (ou com ids inexistentes) é reportada
como sucesso.

### B5 — Filtro "Data retorno" compara contra a data de saída

**Arquivo:** `v6/app/Http/Controllers/Portal/BuscaDiariasController.php:45`

```php
if (isset($request->data_retorno)) $diarias->where("data_saida", "<=", $request->data_retorno);
```

O campo `data_entrada` existe na tabela e é o "retorno". O filtro usa
`data_saida`, o que altera o conjunto retornado.

### B6 — Conjunto de caracteres divergente entre os filtros de nome e CPF

**Arquivo:** `v6/app/Http/Controllers/Portal/BuscaDiariasController.php:34,38`

O filtro por nome usa `'àáâãäéèëêíìïîóòõöôúùüûçÇ'` (26 caracteres) e o de
CPF usa `' àáâãäéèëêíìïîóòõöôúùüûçÇ'` (**27**, com espaço inicial),
enquanto o destino `'aaaaaeeeeiiiiooooouuuucc'` tem 26. No PostgreSQL,
`translate()` remove os caracteres de `from` sem correspondente em `to` — o
espaço inicial faz o mapeamento das letras acentuadas ficar **deslocado** no
filtro de CPF.

**Impacto:** CPF com dígito verificador acentuado não é normalizado
corretamente.

### B7 — `Repository@upInsert` faz `dd($th)`

**Arquivo:** `v6/app/Repositories/Repository.php:66-68`

No `catch (\Throwable)`, o método chama `dd($th)` — despeja a exceção na
tela e aborta. Em produção, stack trace vaza ao operador e o tratamento de
erro do controller (`try/catch` com flash) nunca é alcançado.

### B8 — Campo `archive` no `$fillable` sem coluna correspondente

**Arquivo:** `v6/app/Http/Model/Prefecture/RequerimentoDiarias.php`

`'archive'` está no `$fillable`, mas `requerimento_diarias` não tem essa
coluna. Se o valor chegasse ao `updateOrCreate`, o PostgreSQL acusaria coluna
inexistente.

### B9 — `destroy_archive` limpa todas as configurações

**Arquivo:** `v6/app/Http/Controllers/Prefecture/RequerimentoDiariasController.php:269`

`RequerimentoDiariasConfig::query()->update(['archive' => null, 'path' => null])`
— sem `where('id', 1)`. Com o singleton na linha 1 o efeito prático é o
mesmo, mas a intenção (limpar só a config ativa) não está expressa.

### B10 — `path` gravado aponta para um diretório inexistente

**Arquivo:** `v6/app/Http/Controllers/Prefecture/RequerimentoDiariasController.php:95,157,240`

O código grava `storage/app/public/leis_de_requerimento/` (singular), mas o
arquivo é salvo em `leis_de_requerimentos` (plural). O campo `path` é
inutilizável; o link do Portal usa o plural e funciona.

### B11 — Import inválido no model de configuração

**Arquivo:** `v6/app/Http/Model/Prefecture/RequerimentoDiariasConfig.php:7`

`use Illuminate\Database\Eloquent\Model\RequerimentoDiarias;` — classe
inexistente (o model real é `App\Http\Model\Prefecture\RequerimentoDiarias`).
Não quebra em runtime porque o import não é usado.

### B12 — Chaveamento Blade malformado no Portal

**Arquivo:** `v6/resources/views/portal/layouts/busca-diarias/show.blade.php:119-131`

O `@else` está **dentro** do `@elseif`, e o `@endif` fecha apenas o `@if`.
O resultado é que o "sem arquivo" nunca produz saída — o que funciona, mas
deixa a lógica de apresentação ambígua e frágil.

### B13 — Import de Summernote sem efeito

**Arquivo:** `v6/resources/views/prefecture/contents/requerimento-diarias/form.blade.php` (seção `other-scripts`)

Summernote e o `summernote.js` são carregados, mas nenhum campo do formulário
usa editor WYSIWYG (`dados_objetivo` é `textarea` simples). Sonho de carga
desnecessário.

### B14 — `RequerimentosConfigsRequest` nunca aplicado

**Arquivo:** `v6/app/Http/Requests/RequerimentosConfigsRequest.php`

`update_configuration()` recebe `Illuminate\Http\Request` cru — as regras
(`lei_padrao` required, `archive` required/mimes) nunca são executadas. É
possível gravar configuração sem lei padrão e sem arquivo.

### B15 — Permissões de `store`/`destroy` Abertas no middleware

**Arquivo:** `v6/app/Http/Middleware/CheckRoutePermission.php:44-48, 75-79`

A lista `resourceRoutes` (`edit => update`, `create => store`, `delete => destroy`)
é verificada por `in_array($value, $resourceRoutes)` — que compara contra os
**valores** (`update`, `store`, `destroy`). Como `accessArr` contém o segmento
da rota (`store`, `destroy`), essas rotas são marcadas como "abertas" e
**qualquer usuário logado** acessa `POST /cms/requerimento-diarias` e
`DELETE /cms/requerimento-diarias/{id}`, independentemente das permissões
atribuídas ao papel.

> `[NÃO VERIFICADO]` — não foi possível confirmar em execução qual papel
> consegue criar/excluir; a leitura do código indica o comportamento acima.

### B16 — Rotas destrutivas em GET e fora do prefixo `/cms`

**Arquivo:** `v6/routes/basepro.php:529-530`

`/deletar-arquivo-padrao` e `/deletar-arquivo/{id}` usam GET para apagar
arquivos e ficam fora do grupo `/cms` (prefixo da aplicação). Além do risco
de prefetch/cache, o `check.route.permission` deriva a permissão do nome da
rota — slugs inexistentes em `resources` —, na prática exigindo
`super-admin`.

### B17 — `IRequerimentoRepository` sem nenhum método

**Arquivo:** `v6/app/Repositories/Contracts/IRequerimentoRepository.php`

Interface vazia; o binding em
`v6/app/Providers/RepositoryServiceProvider.php:92` não agrega verificação de
contrato.

### B18 — Duas instâncias do repositório no mesmo controller

**Arquivo:** `v6/app/Http/Controllers/Prefecture/RequerimentoDiariasController.php:27-38`

O controller recebe `IRequerimentoRepository` por injeção **e** cria
`new RequerimentoRepository()` separadamente. `$this->config_repository` (a
interface) é usado em `get_configuration()` para
`$this->config_repository->getModel()->find(1)` — mas o model do
`RequerimentoRepository` é `RequerimentoDiarias`, **não**
`RequerimentoDiariasConfig`. O valor passado à view como `data` é, portanto,
um requerimento (e não uma configuração).

**Impacto:** baixo — a view `configuracoes/form.blade.php` usa
`$configuracoes[0]`, não `$data`. Mas é um bug latente.

### B19 — `destroy` usa soft delete, `destroyAll` também, mas o DataTable lê a view

A exclusão individual (`destroy`) e em lote (`destroyAll`) marcam `deleted_at`.
A view `req_diarias` filtra `deleted_at IS NULL`, então a listagem do CMS
atualiza corretamente. **O Portal, porém, consulta a tabela via Eloquent**, que
também aplica o scope de soft delete. Sem inconsistência observada — registrado
aqui apenas como ponto de verificação.

## 2. Funcionalidades incompletas ou mortas

| Item | Situação |
|---|---|
| `padrao_arquivo_config` | Checkbox e update sem efeito (coluna ausente) |
| `RequerimentosConfigsRequest` | Nunca instanciado |
| `IRequerimentoRepository` | Contrato vazio |
| `RequerimentoDiariasConfig::requerimento_diarias()` | Relação `hasMany` por `requerimento_id` — nenhum trecho do código a usa |
| `Archive::requerimento_diarias()` | Relação `hasOne` — não usada |
| `RequerimentoDiarias::archives()` | `hasOne` onde deveria ser `hasMany` |
| Summernote no formulário | Carregado, sem uso |
| `tecnickcom/tcpdf` | Dependência do projeto, não usada por este módulo |
| `RequerimentoDiarias::$fillable` contém `'archive'` | Coluna inexistente |
| Filtro `data_retorno` | Compara a coluna errada |
| Coluna `requerimento_id` de `requerimento_diarias_configs` | Criada com FK, nunca preenchida pelo código |

## 3. Itens que existem na v5 e foram removidos na v6 (e vice-versa)

| Item | v5 | v6 |
|---|---|---|
| `v5/database/seeds/RequerimentoDiariasSeeder.php` | **Presente** — grava `menu_orders` e `resources` (`servidor`, `requerimento-diarias`, `certificado`) sem `modulo_id` | **Removido** — substituído por `ResourcesTableSeeder` + `MenuOrderSeeder` com `modulo_id = 36` |
| Tabela `modulos` | **Não existe** (nenhuma migration) | Existe (`2024_11_01_150206_create_modulos_table.php`), 58 slugs cadastrados |
| View `requerimentos` + 4 migrations | **Não existem** | **Novas** na v6 (2024-10-08, 2025-08-12, 2026-06-09, 2026-06-12) — módulo Publicações/Requérimentos |
| `DatatableController@get_requerimentos` | **Não existe** | **Novo** na v6 (lê a view `requerimentos`) |
| `Portal\RequerimentoController` (rotas `/requerimentos*`) | **Não existe** | **Novo** na v6 |

Nenhum item do **código do módulo de Diárias** foi alterado ou removido.

## 4. Riscos de dados (migração de schema)

| Risco | Detalhe | Ação na v7 |
|---|---|---|
| Coluna `padrao_arquivo_config` ausente | Backfill impossível sem criar a coluna | `ADD COLUMN ... DEFAULT false` + backfill a partir de `requerimento_diarias_configs` |
| `codigo_diarias` pode ter duplicatas | Sem constraint UNIQUE e com soft delete reusando `max+1` | Índice único parcial (`WHERE deleted_at IS NULL`) **após** deduplicar |
| Diretório de arquivos divergente | `leis_de_requerimento` (path gravado) vs `leis_de_requerimentos` (real) | Inventariar e normalizar o `path` |
| `archives.requerimento_id` compartilhado | Tabela global com FK `ON DELETE CASCADE` para `requerimento_diarias` | Hard delete de requerimento apaga anexos; soft delete não |
| `valor_diarias`/`numero_diarias` como `varchar` | Query/relatórios por faixa numérica exigem cast | Manter `string` na v1 ou migrar com cast |
| Ambiente sem dados | `requerimento_diarias` e `requerimento_diarias_configs` com 0 linhas | Não há dado de produção para calibrar migração — testar com seed sintético |
| `states` × `servidor` com soft delete | Requerimento pode apontar para servidor/UF logicamente excluídos | Filtrar `deleted_at IS NULL` nas relações na v7 |

## 5. Dívidas de UX e acessibilidade

| Item | Detalhe |
|---|---|
| Máscara de CPF no Portal | O `value` do input é o texto **com** máscara (ex.: `000.000.000-00`) e é reenviado ao backend, que compara com `ilike '%...'`. O formato com máscara reduz o casamento por substring |
| Mensagem de "sem resultado" | O `@else` quebrado (B12) impede a mensagem "Sem contéudo" de aparecer de forma previsível |
| Máscara de moeda | `valor_diarias` é texto livre com máscara `money`; nada impede letras |
| Rótulo "Transporte Utilizado" vs coluna `placa_transporte` | Nome da coluna e rótulo divergem |
| Sem paginação no CMS | Toda a listagem é carregada via DataTable com paginação do Yajra — OK |
| Exportação do Portal | Não há exportação na busca pública (apenas no CMS) |
| Sem ordenação por data no Portal | Ordenação fixa por `created_at DESC` |

## 6. Riscos de segurança consolidados

| # | Risco | Severidade |
|---|---|---|
| S1 | Injeção de SQL nos filtros públicos (B1) | **Alta** |
| S2 | `store`/`destroy` liberados a qualquer usuário logado (B15) | **Alta** |
| S3 | PDF do Portal expõe banco, agência, conta, matrícula e cargo do servidor | Média (decisão de negócio/jurídico) |
| S4 | Rotas destrutivas em GET (B16) | Média |
| S5 | `dd($th)` vazando stack trace (B7) | Baixa |
| S6 | Link de download da view `req_diarias` aponta para rota do CMS (requer login) | Baixa (comportamento esperado no CMS) |

## 7. Perguntas em aberto para o negócio

1. O **"data retorno"** deve filtrar por `data_entrada` (data de retorno real) ou
   por `data_saida` (comportamento atual)? Corrigir muda o resultado.
2. `valor_diarias` e `numero_diarias` devem ser numéricos? Há relatório
   somatório de despesas com diárias em outro módulo?
3. O PDF do Portal deve mascarar **banco, agência e conta** do servidor? Hoje
   são exibidos integralmente.
4. O "Usar Arquivo de Lei Padrão" deve ser persistido (coluna nova) ou a
   feature deve ser removida?
5. A edição de um requerimento **deve** exigir novo upload do arquivo da lei?
6. O `codigo_diarias` deve ser único entre requerimentos ativos? O que fazer
   em caso de colisão?
7. As configurações devem continuar num singleton na `id = 1` ou virar uma
   tabela com chave semântica (`requerimento-diarias-config`)?
8. As rotas de configuração devem ficar sob `/cms/requerimento-diarias/...`?
9. O Portal deve oferecer exportação (Excel/PDF) da busca de diárias?
10. Quem pode excluir em lote — apenas `requerimento-diarias.delete`? O
    middleware atual libera `destroy` para qualquer logado (B15).

## 8. Mapa v5 → v6

### 8.1 Código do módulo

| Camada | v5 | v6 | Mudança |
|---|---|---|---|
| Controller CMS | `v5/app/Http/Controllers/Prefecture/RequerimentoDiariasController.php` | `v6/app/Http/Controllers/Prefecture/RequerimentoDiariasController.php` | **Idêntico** (`diff` sem diferenças) |
| Controller Portal | `v5/app/Http/Controllers/Portal/BuscaDiariasController.php` | `v6/app/Http/Controllers/Portal/BuscaDiariasController.php` | **Idêntico** |
| Model | `v5/app/Http/Model/Prefecture/RequerimentoDiarias.php` | `v6/app/Http/Model/Prefecture/RequerimentoDiarias.php` | **Idêntico** |
| Model config | `v5/app/Http/Model/Prefecture/RequerimentoDiariasConfig.php` | `v6/app/Http/Model/Prefecture/RequerimentoDiariasConfig.php` | Presente nas duas (`diff` sem diferenças) |
| Repository | `v5/app/Repositories/Eloquent/Prefecture/RequerimentoDiarias/RequerimentoRepository.php` | `v6/app/Repositories/Eloquent/Prefecture/RequerimentoDiarias/RequerimentoRepository.php` | **Idêntico** |
| Requests | `RequerimentosRequest`, `DiariasRequest` | idem | **Idênticos** |
| Contrato do repositório | `v5/app/Repositories/Contracts/IRequerimentoRepository.php` | `v6/app/Repositories/Contracts/IRequerimentoRepository.php` | **Idênticos** (ambos vazios) |
| Views CMS | `v5/resources/views/prefecture/contents/requerimento-diarias/**` (7 arquivos) | `v6/resources/views/prefecture/contents/requerimento-diarias/**` (7 arquivos) | **Idênticas** |
| View Portal | `v5/resources/views/portal/layouts/busca-diarias/show.blade.php` | `v6/resources/views/portal/layouts/busca-diarias/show.blade.php` | **Idêntica** |
| JS do DataTable | `v5/public/assets/js/custom/contents/requerimentos_diarias/datatable.js` | `v6/public/assets/js/custom/contents/requerimentos_diarias/datatable.js` | **Idêntico** |
| Migrations | 6 migrations (2023-01-13 … 2023-09-19) | as mesmas 6 | **Idênticas** |

### 8.2 Rotas (mesmas URIs, linhas diferentes)

| Recurso | v5 | v6 |
|---|---|---|
| Resource CMS | `v5/routes/basepro.php:186` | `v6/routes/basepro.php:205` |
| `destroyAll` | `v5/routes/basepro.php:220` | `v6/routes/basepro.php:248` |
| DataTable | `v5/routes/basepro.php:445` | `v6/routes/basepro.php:434` |
| Configurações (4 rotas) | `v5/routes/basepro.php:564-568` | `v6/routes/basepro.php:526-530` |
| Portal | `v5/routes/web.php:188-190` | `v6/routes/web.php:210-212` |

Nomes de rota, controllers e URIs **inalterados**.

### 8.3 Registro do módulo e seeds

| Aspecto | v5 | v6 |
|---|---|---|
| Tabela `modulos` | Não existe | Criada em `v6/database/migrations/2024_11_01_150206_create_modulos_table.php` — `requerimento-de-diarias` = id 36, `status = false` |
| Seeder dedicado | `v5/database/seeds/RequerimentoDiariasSeeder.php` (chamado em `DatabaseSeeder.php:64`) | **Removido** |
| `resources` | Inserido pelo seeder dedicado, **sem** `modulo_id` | `v6/database/seeds/ResourcesTableSeeder.php:414` — `Resource::updateOrCreate(['slug' => 'requerimento-diarias'], [... 'modulo_id' => 36])` |
| `menu_orders` | Inserido pelo seeder dedicado, **sem** `modulo_id` | `v6/database/seeds/MenuOrderSeeder.php:235` — `MenuOrder::updateOrCreate(['slug' => 'requerimento-diarias'], ['clicks' => 0, 'modulo_id' => 36])` |

### 8.4 Visões SQL

| Objeto | v5 | v6 |
|---|---|---|
| `req_diarias` | Criada em `2023_04_10_100557`, recriada em `2023_05_17_091233` | **Idêntico** |
| `requerimentos` | **Não existe** | 4 migrations (2024-10-08, 2025-08-12, 2026-06-09, 2026-06-12) |

> A view `requerimentos` pertence ao **módulo 41 (Publicações/Requérimentos)**,
> não ao módulo de Diárias. É registrada aqui porque o enunciado do dossiê a
> exige e porque o `diff` de migrations entre v5 e v6 a inclui.

### 8.5 Conclusão do mapa

A evolução **v5 → v6** deste módulo é **puramente estrutural**: a v6
introduziu o conceito de *módulos* (`modulos` + `modulo_id` em `resources` e
`menu_orders`) e substituiu o seeder dedicado `RequerimentoDiariasSeeder` por
seeds genéricos parametrizados pelo `modulo_id`. **Nenhuma mudança de
comportamento, de rota, de tela, de validação ou de persistência** foi
introduzida no módulo de Requerimento de Diárias.

Os bugs listados em `08-GAPS-E-DIVIDAS.md` §1 (B1 a B19) **existem desde a
v5** e foram **herdados** pela v6 sem correção.
