# Feriados Municipais — Rotas e Controllers

## 1. Rotas do portal público

`v6/routes/web.php:205`, dentro do grupo `check.site.manutention`
(`v6/routes/web.php:7`):

```php
Route::get('/feriados-municipais', 'Portal\FeriadosMunicipaisController@index')->name('portal-feriados-municipais.index');
```

| Método | URI | Nome da rota | Controller@action | Middleware |
|---|---|---|---|---|
| GET | `/feriados-municipais` | `portal-feriados-municipais.index` | `Portal\FeriadosMunicipaisController@index` | `check.site.manutention` |

Observação de nomenclatura: o portal usa nomes **hifenizados**
(`portal-feriados-municipais.index`), enquanto outros módulos (por exemplo o
Comitê de Investimentos) usam nomes **pontilhados**
(`portal.comite-de-investimentos.index`). Abreadcrumb usa o nome pontilhado
(`v6/routes/breadcrumbs.php:318-321`).

## 2. Rotas do CMS (`/cms`)

`v6/routes/basepro.php:335-343`:

```php
Route::resource('feriados-municipais', 'Prefecture\FeriadosMunicipaisController')->names([
    'index' => 'painel.feriados-municipais.index',
    'create' => 'painel.feriados-municipais.create',
    'store' => 'painel.feriados-municipais.store',
    'edit' => 'painel.feriados-municipais.edit',
    'update' => 'painel.feriados-municipais.update',
    'destroy' => 'painel.feriados-municipais.destroy',
]);
Route::post('feriados-municipais/destroyAll', 'Prefecture\FeriadosMunicipaisController@destroyAll')->name('painel.feriados-municipais.destroyAll');
```

| Método | URI | Nome da rota | Controller@action |
|---|---|---|---|
| GET | `/cms/feriados-municipais` | `painel.feriados-municipais.index` | `index` |
| GET | `/cms/feriados-municipais/create` | `painel.feriados-municipais.create` | `create` |
| POST | `/cms/feriados-municipais` | `painel.feriados-municipais.store` | `store` |
| GET | `/cms/feriados-municipais/{feriados_municipal}` | **`feriados-municipais.show`** | `show` |
| GET | `/cms/feriados-municipais/{feriados_municipal}/edit` | `painel.feriados-municipais.edit` | `edit` |
| PUT | `/cms/feriados-municipais/{feriados_municipal}` | `painel.feriados-municipais.update` | `update` |
| DELETE | `/cms/feriados-municipais/{feriados_municipal}` | `painel.feriados-municipais.destroy` | `destroy` |
| POST | `/cms/feriados-municipais/destroyAll` | `painel.feriados-municipais.destroyAll` | `destroyAll` |

Notas:

- A rota `show` **não** foi renomeada no array `names`, portanto recebe o nome
  padrão do resource: `feriados-municipais.show` (sem o prefixo `painel.`).
- O parâmetro de rota é `{feriados_municipal}` (singular automático do Laravel
  5.5).
- A rota `POST /cms/feriados-municipais/destroyAll` é declarada **depois** do
  resource, evitando conflito com o `POST` da coleção.

### 2.1 DataTable

`v6/routes/basepro.php:454`, dentro do grupo `prefix => 'datatable'`:

| Método | URI | Nome | Controller@action |
|---|---|---|---|
| GET | `/cms/datatable/feriados_municipais` | (sem nome explícito) | `Prefecture\DatatableController@get_feriados` |

A URL é montada pelo JS genérico em
`v6/public/assets/js/custom/utilities/table.js:183` (`/cms/datatable/${url}`).

## 3. Controller do portal

`v6/app/Http/Controllers/Portal/FeriadosMunicipaisController.php` (idêntico na v5):

```php
namespace App\Http\Controllers\Portal;

use Illuminate\Http\Request;
use App\Http\Controllers\Admin\Controller;
use App\Http\Model\Prefecture\FeriadosMunicipais;
use Carbon\Carbon;

class FeriadosMunicipaisController extends Controller
{
    public function index()
    {
        $feriados = FeriadosMunicipais::whereYear('date', date('Y'))
        ->orderBy('date', 'ASC')
        ->get()->groupBy(function($val) {
            return Carbon::parse($val->date)->format('m');
        });

        return view('portal.layouts.'.$this->getTemplateActived().'.feriados-municipais.index', compact('feriados'));
    }
}
```

| Aspecto | Comportamento |
|---|---|
| Filtro | Apenas o ano corrente (`date('Y')`) |
| Ordenação | `date ASC` |
| Agrupamento | Em PHP, por `Carbon::parse($val->date)->format('m')` → chaves `"01"`..`"12"` |
| Paginação | **Não há** — todos os feriados do ano são carregados |
| View | `portal.layouts.{layout}.feriados-municipais.index` |
| `$request` importado mas **não usado** | Código morto |

## 4. Controller do CMS

`v6/app/Http/Controllers/Prefecture/FeriadosMunicipaisController.php`
(idêntico na v5).

### 4.1 Construtor (linhas 19-25)

```php
public function __construct()
{
    menuClicks('feriados-municipais');
    $this->title = 'Feriados Municipais';
    $this->table = app(FeriadosMunicipais::class);
    $this->middleware('check.route.permission');
    $this->repository = new FeriadosMunicipaisRepository();
}
```

- `menuClicks('feriados-municipais')` incrementa `menu_orders.clicks` **a cada
  requisição** ao controller (inclusive em `store`, `update`, `destroy`).
- O repository é instanciado **manualmente** (`new FeriadosMunicipaisRepository()`),
  e não resolvido pelo container — quebra o padrão de injeção de dependência
  adotado em `ContentController`.

### 4.2 Métodos

| Método | Linhas | Comportamento |
|---|---|---|
| `index()` | 30-35 | `view('prefecture.contents.feriados-municipais.index', ['title' => $this->title, 'content' => $this->table->orderBy('date','ASC')->get()])` — **carrega todos os registros** (sem paginação), mas a variável `$content` **não é usada** pela view (a listagem real vem do DataTable) |
| `create()` | 42-45 | Renderiza `prefecture.contents.feriados-municipais.create` |
| `store(FeriadosMunicipaisRequest)` | 53-70 | `repository->upInsert($request)`; em sucesso, `flash('Registro cadastrado com sucesso.')->success()` e redirect para `painel.feriados-municipais.index`; em exceção, `flash($exception->getMessage())->error()` e redirect `back()` |
| `show(FeriadosMunicipais)` | 79-82 | **Corpo vazio** — retorna `null`; a rota `GET /cms/feriados-municipais/{id}` responde 200 com corpo vazio |
| `edit($id)` | 89-94 | Renderiza a **mesma** view de criação, com `$data = $this->table->find($id)` |
| `update(FeriadosMunicipaisRequest, $id)` | 102-118 | `repository->upInsert($request, $id)` |
| `destroy($id)` | 126-141 | `$this->table->destroy($id)`; resposta **JSON** 200 (`success`) ou 500 (`erro`) |
| `destroyAll(Request)` | 148-160 | `$this->repository->destroyAll($request->toDelete)`; resposta JSON 200/500 |

### 4.3 TrechosLiterais relevantes

`store()`:

```php
try {
    $result = $this->repository->upInsert($request);
    if ($result) {
        flash('Registro cadastrado com sucesso.')->success();
        return redirect()
            ->route('painel.feriados-municipais.index', [$result->id])
            ->with('success', 'Registro cadastro com sucesso');
    };
} catch (\Exception $exception) {
    flash($exception->getMessage())->error();
}
flash('Ocorreu um erro ao tentar cadastrar o registro.')->error();
return redirect()->back();
```

Problemas: (a) o `flash()` de erro executa **sempre** depois do `try`, porque o
`return` só acontece no caminho de sucesso — mas o `catch` não tem `return`, de
modo que a exceção também cai no `flash` final; (b) a mensagem "Registro
**cadastro** com sucesso" está com erro de digitação; (c) `route('painel.feriados-municipais.index', [$result->id])`
passa um parâmetro extra a uma rota que não tem placeholder.

`destroy()`:

```php
if ($this->table->destroy($id)) {
    return response()->json(['success' => "O diário oficial deletado com sucesso!"], 200);
} else {
    return response()->json(['erro' => 'Ocorreu um erro! Por favor entre em contato com o suporte técnico.'], 500);
}
```

A mensagem de sucesso fala em **"diário oficial"** — texto copiado do módulo
Diário Oficial.

`destroyAll()`:

```php
if ($this->repository->destroyAll($request->toDelete)) {
    return response()->json(['success' => "Os feriados foram deletados com sucesso!"], 200);
}
```

`Repository::destroyAll` devolve um `JsonResponse`, que é **sempre truthy** em
PHP — logo, o método responde **sucesso mesmo quando nada foi apagado**
(veja `05-REGRAS-E-PORTABILIDADE.md`).

## 5. Form Request

`v6/app/Http/Requests/FeriadosMunicipaisRequest.php` (idêntico na v5):

```php
public function authorize()
{
    return true;
}

public function rules()
{
    return [
        'icon' => 'required|max:60|min:3',
        'description' => 'required|max:200',
        'date' => 'required',
    ];
}

public function messages()
{
    return [
        'description.required' => 'O campo Descrição do feriado é obrigatório.',
        'description.max' => 'Descrição do feriado não deve ter mais que 200 caracteres.',
        'date.required' => 'O campo data do feriado é obrigatório.',
        'icon.required' => 'O campo ícone é obrigatório.',
        'icon.max' => 'Ícone não deve ter mais que 60 caracteres.',
        'icon.min' => 'Ícone não deve ter menos que 3 caracteres.',
    ];
}
```

| Campo | Regra | Tipo no banco | Divergência |
|---|---|---|---|
| `icon` | required, max 60, min 3 | `varchar(191)` nullable | O banco permite nulo e 191 caracteres; o request exige 3..60 |
| `description` | required, max 200 | `text` not null | O banco permite texto maior; o request limita a 200 |
| `date` | required | `date` not null | Sem validação de formato — aceita qualquer string parseável por `strtotime` |

Observações:

- `authorize()` retorna `true`: **não há** verificação de permissão dentro do
  Form Request. A autorização depende exclusivamente do middleware
  `check.route.permission` (controlado pelo nome da rota).
- Não há validação de **data no futuro** nem de **data duplicada**.
- O `icon` não é validado contra uma lista de ícones existentes.

## 6. Repositório

### 6.1 Contrato

`v6/app/Repositories/Contracts/IFeriadosMunicipaisRepository.php` — **interface
vazia**:

```php
namespace App\Repositories\Contracts;

interface IFeriadosMunicipaisRepository {
}
```

A interface não estende `App\Repositories\Repository` nem declara métodos —
não há contrato efetivo.

### 6.2 Implementação

`v6/app/Repositories/Eloquent/Prefecture/FeriadosMunicipais/FeriadosMunicipaisRepository.php`:

```php
class FeriadosMunicipaisRepository extends Repository implements IFeriadosMunicipaisRepository {
    public function __construct()
    {
        $this->model = new FeriadosMunicipais();
    }

    protected function structureUpInsert($request): array
    {
        $request['date'] = date('Y-m-d', strtotime(str_replace('/', '-', $request['date'])));
        $fields = array_keys($request);

        if (!in_array('status', $fields)) {
            array_push($fields, 'status');
            $request['status'] = null;
        }

        return $this->filter_request($request, $fields);
    }
}
```

### 6.3 Base `Repository`

`v6/app/Repositories/Repository.php` (abstrata) fornece:

| Método | Comportamento relevante |
|---|---|
| `upInsert($request, $id = null)` | `DB::transaction` (só na normalização) + `updateOrCreate(['id' => $id], $data)`; `catch (\Throwable)` chama `dd($th)` |
| `destroy($id, $column = 'id')` | `delete()` do model; retorna `bool` |
| `destroyAll($array, $column = 'id')` | `whereIn($column, $array)->delete()`; retorna **`JsonResponse`** (sempre truthy) |
| `destroyWithRelation($id, $relation, $column)` | Apaga o registro e o relacionamento |
| `dataTrait($request)` | Converte `Request` em array via `traitRequest` (`v6/app/Http/Traits/AppTrait.php:658`) |
| `filter_request($request, $fields)` | Mantém apenas os campos listados; valores "falsy" viram `null` (`v6/app/Http/Traits/AppTrait.php:523`) |

## 7. DataTable — `DatatableController`

```php
// v6/app/Http/Controllers/Prefecture/DatatableController.php:399-403
public function get_feriados()
{
    $feriados_municipais = DB::table("lista_feriados_municipais")->get();
    return DataTables::of($this->struct_views($feriados_municipais, 'feriados-municipais'))->make(true);
}
```

`struct_views($data, 'feriados-municipais')` (linha 504) injeta em cada linha:

| Flag | Permissão |
|---|---|
| `edit` | `feriados-municipais.edit` |
| `delete` | `feriados-municipais.delete` |
| `active` | `feriados-municipais.restore` |
| `inactivate` | `feriados-municipais.inactivate` |

## 8. Permissões exigidas

| Contexto | Permissão | Origem |
|---|---|---|
| Menu do CMS | `feriados-municipais.index` + `modulos.status = true` | `v6/app/Providers/MenuServiceProvider.php:46` |
| Acesso às rotas do CMS | `painel.feriados-municipais.*` convertido para `feriados-municipais.*` | `v6/app/Http/Middleware/CheckRoutePermission.php` (regra específica deste módulo) |
| Botão "Cadastrar" | `feriados-municipais.create` | `v6/resources/views/prefecture/contents/feriados-municipais/index.blade.php` |
| Botão de exclusão em lote | `feriados-municipais.delete` | mesmo arquivo (componente `deleteByLoteSubmitButton`) |
| Botões de editar/excluir na linha | `feriados-municipais.edit` / `.delete` | `struct_views` |
| `super-admin` | Passa por tudo | `v6/app/Library/Helper/appHelpers.php:33` |

Guard: **Sentinel** (`cartalyst/sentinel`).

## 9. API Resources / presenters

**Não existem.** A saída do DataTable é o JSON padrão do
`yajra/laravel-datatables-oracle` (coleção + flags injetadas). O portal não
devolve JSON — a página é renderizada no servidor com o HTML completo.
