# Licitações Antigas — Gaps, Dívidas e Divergências

---

## 1. Bugs / erros conhecidos

| # | Descrição | Evidência | Severidade |
|---|---|---|---|
| B1 | **Listagem quebra por tabelas de catálogo inexistentes.** O controller executa `ModalitiesBiddingsLegacys::pluck('name','id')` e `JurisdictionalBiddingsLegacys::pluck('title','id')` sobre `biddings_legacys_modalities` e `biddings_legacys_jurisdictional`, que **não são criadas por nenhuma migration** e **não existem** na base `prefeituras_v5` (`\d` → "Did not find any relation"). | `v6/app/Http/Controllers/Portal/BiddingOldController.php:54-55` | **Alta** — a rota principal do módulo retorna 500. |
| B2 | **`file_exist(url(...))`** — `url()` gera uma URL absoluta e `file_exists()` sobre URL é sempre falso; o cartão de anexos sempre mostra "Arquivos inexistentes". | `v6/resources/views/portal/layouts/modelo1/licitacao/licitacao-antiga/show.blade.php` (`file_exist` em `v6/app/Helpers/functions.php:267`) | Média |
| B3 | **`{!! $bidding->informacoes !!}`** renderiza HTML do banco sem escape → XSS armazenado. | mesma view do detalhe | Alta |
| B4 | **`show(int $id)` sem restrição numérica na rota** → `/licitacoes-ate-2018/abc` gera erro de coerção de tipo (500) em vez de 404. | `v6/routes/web.php:180` + controller | Média |
| B5 | **Recurso inexistente devolve HTTP 200** com a view `admin.errors.conteudo-nao-encontrado` em vez de `abort(404)`. | `BiddingOldController.php:93-96` | Baixa |
| B6 | **Breadcrumb do detalhe vazio**: a view passa `$bidding->number`, mas o model só tem `numero`. | `.../licitacao-antiga/show.blade.php` + `BiddingsLegacys` | Baixa |
| B7 | **`$archive->title` sempre nulo** (coluna inexistente) → a view sempre mostra `name`. | `v6/app/Http/Model/Prefecture/ArchivesBiddingsLegacys.php` | Baixa |
| B8 | **Coluna `arquivo` declarada no `$fillable` sem existir na tabela** → falha em escrita. | `v6/app/Http/Model/Prefecture/BiddingsLegacys.php:20` | Baixa |
| B9 | **Botão "Limpar campos" não submete o formulário**: a listagem continua mostrando o resultado filtrado. | `.../licitacao-antiga/filtro.blade.php` | Baixa |
| B10 | **Erro na tradução SQL**: a string de origem do `translate()` contém um espaço e apóstrofo (`' ''àáâã...'`), mapeando o apóstrofo para `-`; o conjunto origem/destino tem tamanhos diferentes e o Postgres trunca silenciosamente. | `BiddingOldController.php:68-70` | Baixa |
| B11 | **Views órfãs**: `v6/resources/views/portal/layouts/default/pages/statics/licitacoes/legacys-index.blade.php` e `legacys-show.blade.php` não são renderizadas por nenhum controller e referenciam a rota inexistente `licitacao.old.filtro`. | arquivos citados | Baixa |
| B12 | **Layouts `default` e `newsbit` quebram o módulo**: não existe `portal.layouts.{default,newsbit}.licitacao.licitacao-antiga.*`; se `configurations.layout` for um desses, a rota retorna "View not found". | `v6/app/Http/Controllers/Admin/Controller.php:39-49` | Média |

## 2. Funcionalidades incompletas ou mortas

| # | Item | Situação |
|---|---|---|
| M1 | **Carga de dados do acervo** | Não existe nenhuma tela, importador, seeder ou job que preencha `biddings_legacys`. O acervo é alimentado manualmente. |
| M2 | **Gestão no CMS** | Inexistente. O módulo aparece apenas como chave habilitável em `modulos` (`id = 20`), **sem** `menu_orders` (0 linhas com `modulo_id = 20`), **sem** rota em `v6/routes/basepro.php` e **sem** view em `v6/resources/views/prefecture/**`. |
| M3 | **Chave `licitacoes_old`** | O módulo novo lê `Configuration::where('key','licitacoes_old')` (`v6/app/Http/Controllers/Portal/BiddingsController.php:72`), mas a base só tem `licitacoes_url` — a chave nunca foi criada. |
| M4 | **`$links` sempre `true`** | Variável pasada à view e testada nela, mas nunca pode ser `false` → código morto. |
| M5 | **`with('archives')` na listagem** | Carrega anexos de todos os registros da página sem uso na listagem (over-fetch). |
| M6 | **Exportação inexistente** | O módulo novo exporta Excel/PDF/CSV; o legado não tem rota nem export. |
| M7 | **Recorte "até 2018" inexistente** | Nem o controller, nem a tabela, nem a view limitam a data; o nome do módulo e o rótulo da URL são convenções. |

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

| Item | v5 | v6 |
|---|---|---|
| Ordenação da listagem legada | `orderByRaw` extraindo números de `numero` via regex | `orderBy('data','desc')` — **única alteração funcional** do módulo |
| Demais controllers, models, migrations, views do módulo | idênticos | idênticos |

Nenhum item do módulo foi **removido** na v6. O que a v6 adds é o conjunto de
telas do módulo novo (`licitacoes`), queCompetition compete com o legado pelo
mesmo domínio de dados.

## 4. Mapa v5 → v6

| Elemento | v5 | v6 | Status |
|---|---|---|---|
| Rota listagem | `v5/routes/web.php:145` — `licitacoes.old.index` | `v6/routes/web.php:179` — `licitacoes.old.index` | migrado sem alteração de URI/nome |
| Rota detalhe | `v5/routes/web.php:146` — `licitacoes.old.show` | `v6/routes/web.php:180` — `licitacoes.old.show` | migrado |
| Controller | `v5/app/Http/Controllers/Portal/BiddingOldController.php` | `v6/app/Http/Controllers/Portal/BiddingOldController.php` | migrado com **mudança de ordenação** |
| Models | `v5/app/Http/Model/Prefecture/{BiddingsLegacys,ArchivesBiddingsLegacys,JurisdictionalBiddingsLegacys,ModalitiesBiddingsLegacys}.php` | mesmos caminhos em `v6/` | byte a byte idênticos |
| Migrations | `v5/database/migrations/2018_11_21_121507_create_biddings_legacys_table.php`, `v5/database/migrations/2018_11_21_121854_create_archives_biddings_legacy_table.php` | idênticos em `v6/database/migrations/` | byte a byte idênticos |
| Views | `v5/resources/views/portal/layouts/modelo1..6/licitacao/licitacao-antiga/*` | idênticas em `v6/resources/views/portal/layouts/modelo1..6/licitacao/licitacao-antiga/*` | idênticas (diff recursivo vazio) |
| CMS | inexistente | inexistente | sem gestão |
| Módulo na tabela `modulos` | não aplicável (tabela criada em 2024) | `v6/database/migrations/2024_11_01_150206_create_modulos_table.php` (`id = 20`, `slug = 'licitacoes-antigas'`, `status = 1`) | novo na v6 |

**Resumo do mapa:** o módulo é um *port* literal da v5 para a v6, com uma
única alteração de comportamento (critério de ordenação da listagem) e sem
qualquer recurso novo no CMS.

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

| Risco | Detalhe | Mitigação |
|---|---|---|
| Acervo vazio na base de referência | `select count(*) from biddings_legacys` = 0; não é possível validar migração com a base atual | Importação idempotente + conferência por ano/mês |
| Tabelas de apoio não versionadas | `biddings_legacys_modalities` e `biddings_legacys_jurisdictional` não têm migration nem dados | Criar com dados de referência obtained do acervo real |
| Sem FK entre `biddings_legacys` e os catálogos | IDs órfãos quebram os filtros silenciosamente (dropdown vazio, filtro sem resultado) | Criar FKs na v7 e um comando de auditoria de órfãos |
| `archives_biddings_legacy` sem FK para `biddings_legacys` | Anexos órfãos não aparecem nem quebram a tela | Constraint `onDelete cascade` + auditoria |
| Arquivos fora do repositório | `/storage/licitacoes_old` não existe em `v6/storage/app/public/` | Inventariar e migrar os arquivos com hash |
| Colunas divergentes model↔tabela | `arquivo` e `title` no `$fillable` sem coluna | Não recriar essas colunas; corrigir o model |

## 6. Riscos de segurança

| Risco | Observação |
|---|---|
| XSS armazenado | `informacoes` renderizado sem sanitização. |
| SQL injection | Não identificada: os filtros de igualdade usam binding do Eloquent e o `whereRaw` da busca recebe o termo como **binding** (`whereRaw($sql, ['%termo%'])`), não concatenado na string SQL. |
| Exposição de dados | `cnpj` e `numero` existem na tabela mas **não** são exibidos no portal legado; decidir se devem ser exibidos (dado pessoal de terceiros). |
| Ausência de rate limit | Rotas públicas sem cache nem limitação. |

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

1. **Qual é o corte real?** O acervo legado tem `data` de anos anteriores a 2018
   em qualquer instalação? Deve existir um filtro explícito `data <= 2018-12-31`
   na v7?
2. **Quem alimenta o acervo?** Sem CMS de carga, como os avisos antigos entram?
   É possível transformar a importação em rotina automatizada?
3. **Qual o catálogo válido de modalidades e secretarias?** As tabelas de apoio
   não existem — de onde vem a lista do filtro?
4. **O acervo deve ser exibido também no CMS?** Hoje é só portal.
5. **Os anexos devem ser migrados** para o storage da v7, e em que formato
   (PDF escaneado, HTML)?
6. **O módulo deve continuar separado do `licitacoes`** ou ser unificado em uma
   listagem com ordenação por data?
7. **O status em `modulos` deve desabilitar o módulo?** Hoje não desabilita nada.

## 8. Checklist de correção antes da v7

- [ ] Criar as tabelas `legacy_modalities` e `legacy_jurisdictions` (ou
      equivalents) com dados de referência.
- [ ] Substituir `file_exist(url(...))` por verificação no storage.
- [ ] Sanitizar `body_html` na renderização.
- [ ] Trocar `show(int $id)` por route model binding com `whereNumber`.
- [ ] Resolver a indisponibilidade das views em layouts `default`/`newsbit`
      (componentizar).
- [ ] Definir e implementar a política de "até 2018".
- [ ] Migrar/auditar os arquivos de `/storage/licitacoes_old`.
- [ ] Criar suíte de testes de feature e unit descrita em `07-TESTES.md`.
