# DPass → Parking Collector
## Report «Soste attive per area» — contratto 1.0, patch DPass 61I

Data: 22 settembre 2026. Stato: implementato e verificato in ambiente di preparazione; da collaudare sul locale e pubblicare. **Questa consegna modifica soltanto DPass.** L'interfaccia e il rilevamento in Parking Collector vanno sviluppati nel relativo progetto.

### 1. Scopo e collegamento

Rendere consultabile agli agenti il report aggregato già presente in DPass: una riga per area, conteggi ora/+30/+60, capienza e disponibilità stimata. **Non è un elenco targhe o un'esportazione delle singole soste.** Non sono restituiti nominativi, recapiti, documenti, pagamenti, credenziali o errori tecnici delle fonti.

Il report amministrativo e la nuova API usano `ParkingAreaOccupancyReportService`. Le regole del report e il suo CSV non sono state cambiate. Sono mantenute anche le differenze già presenti fra calcolo strada e strutture; Collector non deve ricalcolare né sommare autonomamente le righe per ricostruire i totali.

Endpoint aggiunto:

```text
POST {BASE_DPASS}/api/v2/parking/reports/active-by-area
```

`BASE_DPASS` è la stessa installazione DPass già usata per la verifica targa. Nessuna API Ditech Management e nessun nuovo token. Usare HTTPS in produzione, senza seguire redirect automaticamente.

Header:

```http
Accept: application/json
Content-Type: application/json
X-DPass-Token: <TOKEN_ESISTENTE_CONTROLLO_SOSTE>
```

Sono accettati anche `Authorization: Bearer <TOKEN>` e, per compatibilità, `X-Api-Key`. Inviare un solo meccanismo; l'ordine di precedenza esistente è Bearer, X-DPass-Token, X-Api-Key. La credenziale resta sul server Collector, mai nel browser degli agenti.

### 2. Richiesta

```json
{
  "municipality_code": "anacapri",
  "area_type": "street"
}
```

| Campo | Regola |
|---|---|
| `municipality_code` | Codice Comune **DPass** già usato dal controllo targa, non ID numerico Ditech. Facoltativo solo se la credenziale ha un unico Comune attivo. Con più Comuni è obbligatorio. Confronto senza distinzione fra maiuscole e minuscole. |
| `area_type` | `street` oppure `facility`. Omesso, nullo o vuoto: `street`. Per Anacapri usare `street`. La modalità `facility` richiede il relativo servizio e almeno una struttura attiva. |
| `source_id` | Facoltativo; ID intero positivo di una fonte restituita da **questa stessa API e installazione**, oppure null. Non utilizzare ID della configurazione Collector o di Ditech. |

Il body deve essere un oggetto JSON. Non inviare `plate`, `area_code`, `checked_at` o altre chiavi: sono rifiutate con 422. Il report è corrente, non una ricerca storica. L'istante viene fissato da DPass nel fuso Comune/tenant.

Il filtro fonte aggiunge i contatori della fonte selezionata; **i valori principali e la disponibilità continuano a usare tutte le fonti**, come nel report amministrativo.

### 3. Risposta

Una risposta riconosciuta contiene `success=true`, `ok=true`, `report="active_by_area"`, `report_version="1.0"`, `supported=true`. Sono presenti `X-DPass-Report: active_by_area` e `X-DPass-Report-Version: 1.0`, anche negli errori prodotti dopo l'ingresso nella rotta. Rimangono `X-Request-Id`, gli header del limite chiamate e `Cache-Control: no-store, private`.

| Campo della risposta | Uso nel report Collector |
|---|---|
| `checked_at`, `timezone`, `after_30`, `after_60` | Istante comune dei calcoli e degli orizzonti; date ISO 8601 con offset. `generated_at` coincide con l'istante di riferimento. |
| `municipality`, `filters` | Comune effettivo e filtri applicati. |
| `rows` | Array di aree, nell'ordine del report. Ogni `area` contiene soltanto `id`, `code`, `name`, `area_type`. |
| `capacity` | Posti monitorati; null se non configurati. |
| `active_now`, `active_after_30`, `active_after_60` | Conteggi delle targhe del report. Le proiezioni riguardano soltanto i titoli già attivi ora che saranno ancora validi; non aggiungono prenotazioni future. |
| `available_now`, `available_after_30`, `available_after_60` | Disponibilità stimata; null significa non calcolata, **non zero posti**. |
| `occupancy_percent`, `excess_now` | Percentuale ed eccedenza del report; la percentuale può superare 100, la disponibilità non è negativa. |
| `ending_within_30`, `ending_between_30_60` | Indicatori già derivati dal report; evitare calcoli su dati ottenuti in chiamate diverse. |
| `source_active_*` | Conteggi aggiuntivi della fonte selezionata; null senza filtro. |
| `totals`, `source_totals`, `capacity_totals` | Usare i totali forniti. Una targa presente in più aree può rendere il totale distinto diverso dalla somma delle righe. La capienza riguarda solo le aree configurate. |
| `sources`, `selected_source` | Opzioni del filtro e fonte selezionata, con ID DPass. Possono esserci fonti disattivate con dati storici, come nel report attuale. |
| `source_freshness` | Per le fonti attive: `source_id`, `status`, `last_successful_import_at`. Stati: `native`, `fresh`, `stale`, `never`, `error`. Non trasmette messaggi tecnici o impostazioni. |
| `refresh_after_seconds` | Indicazione di aggiornamento: 60 secondi. Il limite chiamate resta quello della credenziale. |

Gli esempi `response-success.json`, `response-source.json` e `response-empty.json` contengono risposte complete generate da test con dati fittizi. Lo schema è in `openapi-active-by-area.json`. Le chiavi future non riconosciute possono essere ignorate.

**Importante:** il report descrive titoli validi, non rilevazioni fisiche. Gli abbonamenti non sono conteggiati come presenze; le nuove chiamate non interrogano singolarmente i provider. Per strada viene preservato il `COUNT(DISTINCT plate)` del report attuale; per strutture il relativo servizio mantiene la normalizzazione già prevista. Non inventare targhe per ticket senza targa. L'ora della risposta non certifica che un flusso remoto sia privo di ritardi: visualizzare gli indicatori di aggiornamento disponibili.

### 4. Rilevamento automatico in Collector

Eseguire dal backend Collector una chiamata valida, con il Comune configurato e senza `source_id`, usando il collegamento del controllo targa. Mostrare la funzione solo dopo una risposta 200 con marcatori e struttura validi. **Un report a zero o con `rows=[]` è disponibile e va mostrato.**

| Esito | Comportamento da implementare in Collector |
|---|---|
| 200 e contratto valido | Funzione disponibile; visualizzare il report. |
| 404/405 senza marcatore report | Possibile endpoint non installato. Verificare prima URL, metodo POST e collegamento; nascondere la funzione sulle installazioni vecchie, con ricontrollo successivo. Non basta un generico 404 del proxy a provare l'assenza. |
| 401 / 403 | Credenziale, IP, ambito o servizio non autorizzati. Non trattarlo come una vecchia versione; segnalare il problema agli amministratori. |
| 404 con marcatore e `SOURCE_NOT_FOUND` / `NO_ACTIVE_FACILITIES` | Endpoint presente; filtro o configurazione non utilizzabili. |
| 400 / 415 / 422 | Errore nella richiesta del client; non ripetere identica né classificare come assenza. |
| 429 | Rispettare `Retry-After`; il budget è condiviso con il controllo targa. |
| Timeout / 5xx / 200 con HTML o schema errato | Errore temporaneo/protocollo. Non mostrare zeri e non cancellare definitivamente una disponibilità già accertata. |

Conservare temporaneamente l'esito per **collegamento + Comune + tipo area** (proposta: 10 minuti), invalidandolo se cambiano URL/credenziale/configurazione. Aggiornare i dati quando si apre il report e, se utile, ogni 60 secondi mentre resta visibile. Valutare un breve riuso lato server fra agenti dello stesso Comune per non consumare inutilmente il rate limit; non condividere cache fra Comuni o credenziali diversi. Nessun cron è necessario per questa funzione.

Gli errori applicativi hanno `success=false`, `ok=false`, `request_id`, `error.code`, `error.message` ed eventuali `error.details`. Codici principali: `UNAUTHORIZED`, `CREDENTIAL_DISABLED`, `CREDENTIAL_NOT_ACTIVE`, `CREDENTIAL_EXPIRED`, `CLIENT_DISABLED`, `TENANT_MISMATCH`, `IP_NOT_ALLOWED`, `NO_MUNICIPALITY_SCOPE`, `MUNICIPALITY_REQUIRED`, `MUNICIPALITY_NOT_ALLOWED`, `TENANT_NOT_ALLOWED`, `SERVICE_NOT_ENABLED`, `NO_ACTIVE_FACILITIES`, `SOURCE_NOT_FOUND`, `INVALID_REQUEST`, `INVALID_JSON`, `INVALID_CONTENT_TYPE`, `RATE_LIMIT_EXCEEDED`, `INTERNAL_ERROR`. Errori d'infrastruttura possono avere un formato diverso.

### 5. Perimetro e collaudo del progetto Collector

L'API riutilizza autenticazione, scadenza/revoca token, whitelist IP, limiti e Comuni assegnati. Non introduce un'abilitazione separata: le credenziali del controllo soste possono consultare l'aggregato dei Comuni autorizzati quando il servizio è attivo. Collector deve inoltre rispettare gli accessi dei propri agenti, senza offrire Comuni non assegnati.

Non implementare elenchi targhe, dettagli delle singole soste o un nuovo motore di conteggio. Riprodurre il prospetto con nomi area/fonte ricevuti e intestazioni IT/EN del client; disponibilità null resa come “—”. Il report DPass e le verifiche targa V1/V2 restano utilizzabili indipendentemente dall'aggiornamento Collector.

Collaudo minimo: numeri allineati con DPass a pari dati/istante; filtro fonte senza ricalcolo della capienza; report vuoto; fonte estranea; Comune non autorizzato; scadenza token; endpoint vecchio senza rotta; timeout/429/500; nessun accesso a dati analitici. Le chiamate sono di sola lettura sui servizi, ma alimentano il registro API e i metadati d'uso della credenziale.

**Rilascio DPass:** patch 61I prima in `~/progetti/dpass`, poi sei file dal locale alla produzione; nessuna migration, SQL, modifica `.env` o nuovo cron. Pulire la cache delle rotte dopo il caricamento. Per la sola 61I non occorre abilitare o migrare il modulo ospiti sulle produzioni che hanno già report aree/API alla base 61G5.

**Verifiche effettuate:** vedere `VERIFICHE_E_LIMITI.md`. I test SQL sono stati eseguiti in preparazione con SQLite in memoria tramite un adattatore PDO di prova e PHP 8.4, non su MySQL; il pacchetto esegue i test SQL con PDO SQLite nativo quando disponibile nel locale. Nessuna chiamata alle produzioni o ai provider. Documento basato sui sorgenti DPass ricevuti fino alla 61H2 e sui file/test della 61I, non sul codice aggiornato di Parking Collector, che non è stato modificato.
