# Unidades de Atendimento — Regras de Negócio e Portabilidade

---

## 1. Regras de negócio observáveis

| # | Regra | Onde está | Efeito observável |
|---|---|---|---|
| R1 | A listagem pública mostra **apenas** conteúdos da categoria `unidades-de-atendimento`. | `v6/app/Http/Controllers/Portal/ServiceUnitsController.php:26,29-30` | Conteúdos de outras categorias nunca aparecem. |
| R2 | Soft delete é respeitado na listagem (`whereNull('deleted_at')`). | `ServiceUnitsController.php:29` | Registros excluídos somem do portal (e da view SQL). |
| R3 | Ordem: `publication_date desc`, desempate `id desc`. | `ServiceUnitsController.php:31` | Unidades mais recentes primeiro; em empate, id maior primeiro. |
| R4 | Paginação fixa de **12** itens por página. | `ServiceUnitsController.php:31` | Não configurável pelo CMS. |
| R5 | O detalhe só abre para slug **da própria categoria**; caso contrário 404. | `ServiceUnitsController.php:36-39` | Slug de "Notícias" em `/unidades-de-atendimento/{slug}` devolve 404. |
| R6 | A galeria do detalhe exclui a imagem de destaque (`highlight` nulo). | `ServiceUnitsController.php:41` | A foto de capa não se repete na galeria. |
| R7 | A galeria só exibe arquivos com extensão de imagem. | `v6/resources/views/portal/components/galerias/index.blade.php:26` | PDFs e outros arquivos ficam fora da galeria. |
| R8 | O slug público é derivado do título; se já existir, recebe o sufixo `-<max(id)+1>`. | `v6/app/Http/Controllers/Prefecture/ContentController.php:663-668` | URLs antigas continuam válidas; títulos repetidos não colidem. |
| R9 | O registro é sempre criado com `clicks = 0`. | `ContentController.php:639` | Contador inicia zerado. |
| R10 | A data de publicação vem do campo `publication_date` do formulário, convertida de `dd/mm/aaaa` para timestamp; vazio → `null`. | `ContentController.php:624-631` | Conteúdo sem data fica com `publication_date` nulo (aparece no fim da ordenação do banco). |
| R11 | "Ativar Galeria" grava `contents.galery`; o portal só inclui a galeria quando `galery === true`. | `form.blade.php` (checkbox) + `show.blade.php` | Galeria cadastrada e não ativada não aparece no portal. |
| R12 | O vídeo é gravado em `links` (registro único por conteúdo), com link normalizado. | `ContentController.php:714-732` | O formulário exibe o valor de `$content->linksUnique['link']`. |
| R13 | A exclusão em lote usa `IContentRepository::destroyAll()`. | `ContentController.php:70-79` | Exclusão conjunta de vários conteúdos. |
| R14 | O layout do portal é lido de `configurations.key = 'layout'` a cada requisição. | `v6/app/Http/Traits/GetInformationsPrefectureTrait.php:92-102` | Trocar o layout em runtime muda as views servidas. |

## 2. Acoplamentos

### 2.1 Dependências de outros módulos

| Depende de | Natureza | Onde |
|---|---|---|
| Módulo **Notícias** | Compartilha o JS de edição (`v6/public/assets/js/custom/contents/noticias/edit.js`) e o editor Summernote. | `form.blade.php` |
| Módulo **Conteúdos** (genérico) | Todo o CRUD é servido por `Prefecture\ContentController`; o módulo é apenas um slug. | `v6/app/Http/Controllers/Prefecture/ContentController.php` |
| Módulo **Categorias de conteúdo** | `category_contents` define o conjunto exibido. | `CategoryContentTableSeeder.php:243-247` |
| Módulo **Imagens/Mídia** | `archives` + `select-image.js` + `cropper-min.js` para destaque e galeria. | `form.blade.php` |
| Módulo **Configurações** | `configurations.key = 'layout'` determina o layout e a view renderizada. | `GetInformationsPrefectureTrait.php:92` |
| Módulo **Mapa do site / Menu** | `menu_orders` (`modulo_id = 47`) e `MenuServiceProvider` montam o menu do CMS e as permissões. | `MenuOrderSeeder.php:332-338` |

### 2.2 Dependências do módulo para outros módulos

- Publica o recurso `unidades-de-atendimento` em `resources` (id 66) — usado na
  tela de gestão de permissões do CMS.
- Registra `menu_orders.clicks`, consumido pelos relatórios de.modules mais clicados.

## 3. Armadilhas (riscos técnicos)

| # | Armadilha | Impacto | Evidência |
|---|---|---|---|
| A1 | **Não existe model `ServiceUnit`.** | Reconstruir o módulo como entidade isolada quebra o contrato com `contents`/`category_contents`/`archives` e com a view SQL. | `v6/app/Http/Model/Prefecture/` não contém `ServiceUnit.php`; o controller usa `App\Prefecture\Content`. |
| A2 | **Paginação fixa em 12** embutida no controller. | Não há como mudar o tamanho da página sem editar código. | `ServiceUnitsController.php:31` |
| A3 | **Slug do category hardcoded** em várias camadas (`ServiceUnitsController`, `ContentController`, view SQL, seeds). | Renomear a categoria exige alterar 5+ arquivos e reexecutar a view SQL. | `ServiceUnitsController.php:26,38`; `2024_11_30_102902_create_view_unidades_de_atendimento.php:27` |
| A4 | **Nomes de permissão divergem**: kebab-case nas views (`unidades-de-atendimento.create`) vs underscore na DataTable (`unidades_de_atendimento.edit`). | Perfis de permissão podem ser configurados com a chave errada e o botão não aparecer. | `prefecture/contents/unidades-de-atendimento/index.blade.php:11` vs `DatatableController.php:477` |
| A5 | **`row_number()` na view SQL** é recalculado a cada consulta. | A coluna "ID" exibida na DataTable muda conforme a ordenação; não é um identificador estável. | migration da view |
| A6 | **Ordenações divergentes**: portal usa `publication_date desc, id desc`; view SQL usa apenas `publication_date desc`. | A ordem do CMS pode diferir da ordem do portal. | `ServiceUnitsController.php:31` vs migration da view |
| A7 | **`$content->archives[0]`** sem ordenação explícita nas views. | A miniatura exibida depende da ordem natural do banco (não necessariamente a imagem de destaque). | `v6/resources/views/portal/layouts/modelo1/unidades-de-atendimento/index.blade.php:22` |
| A8 | **`{!! $content->text !!}`** renderiza HTML sem escape. | Risco de XSS Stored por usuários do CMS. | `v6/resources/views/portal/layouts/modelo1/unidades-de-atendimento/show.blade.php:29` |
| A9 | **Link de exibição no CMS aponta para `/portal/unidades-de-atendimento/{slug}`**, rota que não existe (a real é `/unidades-de-atendimento/{slug}`). | O operador copia um link quebrado. | `form.blade.php:25` |
| A10 | **Views do módulo só existem em `modelo1`..`modelo6`**. | Layout ativo `default` (ou `newsbit`, `surveys`, `calendar-events`, `contratos`, `diario-oficial`, `dynamics-forms`, `busca-diarias`, `barra-privacidade`) gera erro de view inexistente. | `v6/resources/views/portal/layouts/` |
| A11 | **`getTemplateActived()` altera `configurations.pagina_em_manutencao`** quando a view do layout não existe e aborta com 404. | Um layout mal configurado pode colocar o portal inteiro em manutenção. | `GetInformationsPrefectureTrait.php:95-99`; duplicata em `v6/app/Http/Controllers/Admin/Controller.php:39-47` |
| A12 | **`$category->id` sem `firstOrFail()`**. | Sem o seed da categoria, o índice quebra com erro 500 em vez de 404. | `ServiceUnitsController.php:26,30` |
| A13 | **`$current_date` calculado e não usado**. | Código morto; sinaliza refactor incompleto. | `ServiceUnitsController.php:27` |
| A14 | **Código comentado no `show()`** (versão anterior da consulta). | Ruído que induz leitura errada do comportamento. | `ServiceUnitsController.php:43-48` |
| A15 | **`genericIndex` treats `atas-e-pontos` com o título "Unidades de Atendimento"**. | Bug de copy-paste do CMS: a tela de Atas aparece com o título errado. | `v6/app/Http/Controllers/Prefecture/ContentController.php:116` |
| A16 | **Exclusão não remove os `archives` em cascata** (`archives.contents_id` sem `ON DELETE CASCADE`). | Arquivos órfãos no storage após exclusão do conteúdo. | Schema `archives` no banco v6. |
| A17 | **Slug não é único no banco** (apenas `contents_pkey`). | Dois conteúdos podem ter o mesmo slug se criados fora do fluxo do controller. | Schema `contents`. |
| A18 | **A view SQL não é invalidada quando a categoria é recriada** (o `id` fica embutido apenas via subquery — ok) mas a view **não** é dropada/recriada em seeds. | Ambientes sem a migration executada não têm a view → erro 500 no DataTable do CMS. | migration `v6/database/migrations/2024_11_30_102902_create_view_unidades_de_atendimento.php` |

## 4. Código duplicado / herdado

| Trecho | Origem | Observação |
|---|---|---|
| `getTemplateActived()` | `v6/app/Http/Traits/GetInformationsPrefectureTrait.php:92` **e** `v6/app/Http/Controllers/Admin/Controller.php:39` | Duas implementações idênticas; o `ServiceUnitsController` (portal) herda da segunda. |
| Formulário do módulo | `v6/resources/views/prefecture/contents/unidades-de-atendimento/form.blade.php` | Copia/espelho do formulário de Notícias (mesmo Summernote, mesmo upload). |
| `datatable.js` | `v6/public/assets/js/custom/contents/unidades-de-atendimento/datatable.js` | Estrutura idêntica à dos demais módulos de conteúdo. |
| Listagem do portal | `v6/resources/views/portal/layouts/modelo1/unidades-de-atendimento/index.blade.php` (e as variantes `modelo2`..`modelo6`) | Seis cópias quase idênticas, uma por layout. |

## 5. Onde a v6 divergiu da v5

**A v5 não possui o módulo.** `grep -r "unidades-de-atendimento\|ServiceUnit" v5/`
não retorna nenhum resultado: nem controller, nem model, nem view, nem rota,
nem migration, nem seed. Trata-se, portanto, de um módulo **novo na v6**, sem
mapa de migração de dados — a carga inicial é feita exclusivamente pelo CMS.

Consequências para a v7:

- Não há histórico de layout de tela nem de nomenclatura a preservar.
- A primeira versão já nasce com as armadilhas A1–A18 acima; a reconstrução é a
  oportunidade de eliminá-las.

## 6. Checklist de equivalência funcional

Ao reconstruir, **não pode mudar**:

1. URL pública `/unidades-de-atendimento` e `/unidades-de-atendimento/{slug}`.
2. Nomes das rotas `unidades-de-atendimento.index` / `.show`.
3. Slug da categoria `unidades-de-atendimento` (e o `id` 44 na base atual).
4. Página de 12 itens como padrão.
5. Ordenação `publication_date desc, id desc`.
6. Comportamento 404 para slug inexistente ou de outra categoria.
7. Exclusão da imagem de destaque da galeria.
8. Slim do `contents.slug` com sufixo numérico em caso de duplicidade.
9. Chave do módulo em `modulos` (`unidades-de-atendimento`, id 47) e as
   strings de permissão `unidades-de-atendimento.*`.
10. Colunas da view SQL (`value, titulo, data_e_hora, id, galeria, slug`) caso a
    DataTable do CMS seja mantida.
