Manuale Operatore — Integrazione OfficeRnD–Aplus / STID

Versione documento: 3.3 — Ultimo aggiornamento: 2026-05-26

1. A cosa serve questo manuale

Questo documento descrive le funzionalità delle integrazioni automatiche di OfficeRnD (di seguito “ORnD”) con due sistemi esterni:

  • Aplus — sistema di controllo accessi tradizionale (sezioni 1–15).
  • STID Mobile ID — sistema di VCard mobile (sezione 16).

Le due integrazioni sono indipendenti: un member può essere su una, sull’altra, su entrambe, o nessuna delle due.

Entrambe le integrazioni sono in scrittura solo da ORnD verso il sistema esterno: tutte le modifiche fatte su ORnD vengono replicate automaticamente tramite webhook. Le modifiche fatte manualmente su Aplus o sul portale STID non vengono propagate a ORnD: ORnD resta la sorgente di verità.

2. Concetti di base

Member (ORnD) ↔ User (Aplus). Un membro di ORnD corrisponde a un utente di Aplus. Il legame è mantenuto dalla proprietà custom aplusId sul member di ORnD, che contiene l’IdUser di Aplus.

Company (ORnD) ↔ Company (Aplus). Una company/team di ORnD corrisponde a una company di Aplus. Il legame è mantenuto dalla proprietà custom aplusId sulla company di ORnD, che contiene l’IdCompany di Aplus.

Building (ORnD) ↔ Site (Aplus). Un building di ORnD corrisponde a uno o più site di Aplus. Il legame è mantenuto dalla proprietà custom AplusProfileId sul building (vedi sezione 3).

Booking → Profilo Aplus. Una prenotazione su una risorsa che ha AplusProfileId abilita per il member, nel giorno del booking, il profilo Aplus corrispondente. Per le risorse di tipo DailyPass c’è in più un setup preventivo: vedi capitolo dedicato (sezione 7).

PIN. Il PIN è generato sempre da Aplus, non lo sceglie l’operatore. Il valore generato viene riportato su ORnD nella proprietà AplusPIN del member.

3. Prerequisito fondamentale: il building deve essere abilitato

⚠️ Nessuna azione di integrazione viene eseguita se il building di riferimento del webhook non è abilitato all’integrazione Aplus. Questo è il primo filtro che Charon applica ad ogni evento ricevuto da ORnD: se il building non è abilitato, il webhook viene scartato silenziosamente.

Come si abilita un building: sulla scheda del building (Settings → Locations/Offices) impostare la proprietà custom AplusProfileId con uno o più ID site Aplus.

  • Singolo site: AplusProfileId = 12 — il building corrisponde al site 12 di Aplus.
  • Più site: AplusProfileId = 12,17,21 — il building corrisponde a più site. Quando viene creato un nuovo utente, l’utente viene piazzato sul primo ID della lista (in questo esempio, il site 12).

Conseguenze pratiche:

  • Tutte le azioni descritte in questo manuale presuppongono che il building sia abilitato.
  • Cambiare l’ordine degli ID nella lista cambia il site di default per i nuovi utenti, ma non sposta gli utenti già esistenti.
  • Se un evento (member, booking, membership, …) arriva da un building non abilitato, viene ignorato silenziosamente. Lo si vede nei log come “Building non di interesse — ignora”.

4. Quando vengono create company e member su Aplus

La creazione di una company o di un member su Aplus non avviene mai come semplice riflesso di un’azione di creazione anagrafica su ORnD. Charon crea l’entità su Aplus solo in due situazioni:

4.1 Forzatura manuale dall’operatore

L’operatore imposta aplusId = -1 sulla proprietà custom della company o del member in ORnD. Vedi sezione 5.

4.2 Necessità di abilitare un profilo

Charon crea automaticamente company e/o member su Aplus quando ne ha bisogno per assegnare un profilo, in due casi:

  • Attivazione di una membership (o pass) il cui piano/risorsa abbia la proprietà AplusProfileId valorizzata. Vedi sezione 8.
  • Booking del giorno su una risorsa con AplusProfileId valorizzato (incluse le risorse di tipo DailyPass). Vedi sezioni 6 e 7.

In sintesi: se non c’è né una forzatura né una necessità reale di abilitare un profilo, una nuova company o un nuovo member su ORnD non vengono propagati a Aplus.

5. Forzare azioni dall’interfaccia ORnD

Tutti i “trigger manuali” funzionano allo stesso modo: l’operatore imposta un valore speciale in una proprietà custom; ORnD invia il webhook a Charon; Charon riconosce il valore ed esegue l’operazione.

5.1 Forzare la creazione/sincronizzazione di una company su Aplus

  • Quando usarlo: la company esiste su ORnD ma non risulta su Aplus, oppure si vuole rigenerare il legame.
  • Cosa fare: in ORnD, aprire la company, andare nella sezione “Properties”, impostare la proprietà aplusId al valore -1 e salvare.
  • Cosa succede: Charon riceve il webhook company.updated, crea la company su Aplus (o riusa quella esistente con lo stesso nome) e scrive in aplusId l’identificativo Aplus reale.
  • Verifica: riaprire la company dopo qualche secondo e controllare che aplusId contenga un numero positivo.

5.2 Forzare la cancellazione di una company da Aplus

  • Quando usarlo: si vuole rimuovere la company da Aplus mantenendola su ORnD.
  • Cosa fare: impostare la proprietà aplusId della company al valore -2 e salvare.
  • Cosa succede: Charon riconosce -2 come marcatore di cancellazione, recupera l’identificativo Aplus precedente, cancella la company su Aplus e disabilita gli utenti collegati.
  • ⚠️ Attenzione: operazione irreversibile lato Aplus. Per ricreare la company successivamente, impostare aplusId a -1 come al punto 5.1.

5.3 Forzare la creazione/sincronizzazione di un member su Aplus

  • Quando usarlo: il member esiste su ORnD ma non risulta su Aplus, oppure si vuole rigenerare il legame senza aspettare un booking o l’attivazione di una membership.
  • Pre-requisito: la company del member deve avere un aplusId positivo. Se non ce l’ha, l’operazione viene saltata: eseguire prima il punto 5.1.
  • Cosa fare: aprire il member, impostare la proprietà aplusId al valore -1 e salvare.
  • Cosa succede: Charon crea l’utente su Aplus (piazzandolo sul primo site indicato in AplusProfileId del building) e aggiorna aplusId su ORnD. Se in passato esisteva già un utente Aplus con la stessa email, l’identificativo precedente viene ripristinato (no duplicati).

5.4 Forzare la rigenerazione del PIN di un member

  • Quando usarlo: il member ha perso il PIN, o lo si vuole resettare.
  • Pre-requisito: il member deve avere un aplusId valido (positivo).
  • Cosa fare: impostare la proprietà AplusPIN al valore -1 e salvare.
  • Cosa succede: Charon chiede a Aplus di generare un nuovo PIN a 4 cifre, lo riceve dalla risposta SOAP e lo scrive su ORnD nella stessa proprietà AplusPIN sovrascrivendo il -1.
  • Verifica: riaprire il member dopo qualche secondo: AplusPIN deve contenere un numero a 4 cifre. Lo stesso valore deve essere presente nel campo Pin dell’utente in Aplus.
  • Nota: il PIN è sempre deciso da Aplus. Non impostare valori diversi da -1: il sistema non li riconosce come trigger di rigenerazione.

5.5 Far rielaborare un member senza modifiche reali

Salvare il member su ORnD senza modifiche significative non scatena alcuna azione (Charon riconosce il caso “NOP” e lo ignora). Per forzare la rielaborazione completa, usare i trigger 5.3 / 5.4 a seconda di cosa serve.

6. Bookings — come vengono trattati

Le prenotazioni su risorse normali (cioè non DailyPass) vengono gestite con la seguente logica:

6.1 Risorsa coinvolta

Una risorsa è considerata “rilevante per Aplus” se la sua proprietà AplusProfileId è valorizzata con un ID profilo Aplus positivo. Se la proprietà manca, la risorsa è ignorata e nessuna azione viene fatta su Aplus (log: “Resource not managed”).

6.2 Webhook in tempo reale alla creazione/modifica del booking

Quando un utente crea o modifica un booking su una risorsa rilevante, Charon riceve il webhook booking.created o booking.updated. A questo punto:

  • Se il booking è per oggi: Charon procede subito ad abilitare il profilo Aplus sull’utente per la durata del booking. Se necessario, crea anche company e member su Aplus (vedi sezione 4.2).
  • Se il booking è per un giorno futuro: Charon registra l’evento ma non esegue ancora alcuna azione su Aplus. L’azione verrà eseguita dal processo today (sezione 9) la mattina del giorno del booking.

6.3 Cancellazione di un booking

Quando un booking viene cancellato (webhook booking.deleted), Charon rimuove il profilo di quel booking dal member, se era stato assegnato.

6.4 Modifica della data o del member del booking

Una modifica del booking viene trattata come un nuovo booking (rimuove il profilo precedente se necessario e riassegna). Anche qui vale la regola: l’azione su Aplus avviene solo se la data risultante è oggi; altrimenti la rielaborazione la fa il processo today.

7. DailyPass — capitolo dedicato

Una risorsa “DailyPass” è una risorsa prenotabile in ORnD che, ad ogni booking, assegna al member un profilo di accesso Aplus per l’intera giornata. È pensata per gli ingressi a giornata di non-residenti, ospiti, freelance occasionali.

7.1 Configurazione (una sola volta per risorsa)

  • Su ORnD, aprire la risorsa.
  • Impostare il type della risorsa esattamente a DailyPass (con la maiuscola, senza spazi).
  • Nella sezione Properties, aggiungere/impostare la proprietà AplusProfileId con il valore dell’IdProfile Aplus corrispondente (numero intero positivo).

Se manca anche solo una di queste due condizioni la risorsa viene trattata come una risorsa normale (sezione 6) e la logica DailyPass non si attiva.

7.2 Setup preventivo (alla creazione/modifica del booking)

A differenza delle risorse normali, per una risorsa DailyPass Charon esegue subito un “setup preventivo” anche se il booking è per un giorno futuro:

  • Se la company del member non ha aplusId, viene creata su Aplus.
  • Se il member non ha aplusId, viene creato su Aplus.
  • Se il member non ha AplusPIN, viene richiesto a Aplus un nuovo PIN e scritto su ORnD.
  • Il booking viene confermato (passa da tentative a confirmed).

Questo serve perché il membro deve poter ricevere il PIN e tutte le informazioni d’accesso prima del giorno del booking.

7.3 Abilitazione del profilo nel giorno del booking

L’aggiunta del profilo DailyPass al member avviene:

  • Subito, se il booking è per oggi (lo gestisce il webhook in tempo reale).
  • Altrimenti, la mattina del giorno del booking, attraverso il processo today (sezione 9).

7.4 Cancellazione di un booking DailyPass

Alla cancellazione, il profilo DailyPass viene rimosso dall’utente Aplus. Company e member rimangono su Aplus; PIN e legami non vengono toccati.

8. Membership e Pass — capitolo dedicato

Le membership e i pass di ORnD modellano l’iscrizione di un member a un piano (mensile, annuale, day-pass ricorrente, ecc.). Quando il piano ha la proprietà AplusProfileId valorizzata, l’attivazione/disattivazione della membership si traduce in apertura/chiusura del corrispondente profilo Aplus per tutti i membri della company.

8.1 Configurazione del piano

  • In ORnD, Settings → Plans, aprire il piano e nella sezione Properties impostare AplusProfileId con l’IdProfile Aplus che si vuole assegnare ai member di quella membership.
  • Se il piano non ha questa proprietà, l’evento di membership viene ignorato (log: “Plan has no AplusProfileId — skipping”).

8.2 Attivazione di una membership

Alla creazione di una membership (o all’attivazione di una già esistente), Charon:

  • Crea su Aplus la company del team se manca (vedi sezione 4.2).
  • Crea su Aplus ogni member del team che ancora non esiste in Aplus.
  • Assegna a tutti i member il profilo Aplus indicato in AplusProfileId del piano, con le date di inizio e fine della membership.

L’operazione è puntuale: si applica solo alla membership che ha scatenato il webhook (gli altri profili dei member non vengono toccati).

8.3 Modifica di una membership attiva

Una modifica della membership ancora attiva (per esempio cambio data fine, cambio piano se il nuovo piano ha lo stesso AplusProfileId) viene trattata come una riassegnazione dello stesso profilo con le nuove date:

  • Se le date sono cambiate, il profilo Aplus viene rimosso e ri-assegnato con le date nuove.
  • Se le date sono uguali, l’operazione è no-op.

8.4 Terminazione, cancellazione o scadenza di una membership

Una membership si chiude in tre modi diversi, tutti trattati allo stesso modo da Charon:

  • Cancellazione esplicita (membership.deleted).
  • Aggiornamento con calculatedStatus diverso da active (per esempio “ended”, “suspended”, …).
  • Scadenza naturale (la data fine è passata).

In tutti questi casi Charon rimuove il profilo Aplus indicato in AplusProfileId del piano da tutti i member del team. Company e member di Aplus non vengono toccati: continuano ad esistere ma perdono l’accesso al profilo. Se il member non ha altri profili attivi (membership o booking), di fatto non potrà più accedere.

8.5 Cambio di piano

Se la membership del member viene cambiata sostituendo il piano:

  • Se il vecchio piano aveva AplusProfileId e il nuovo no: il profilo viene rimosso.
  • Se il vecchio piano non aveva AplusProfileId e il nuovo sì: il profilo viene aggiunto.
  • Se entrambi hanno (lo stesso) AplusProfileId: nessun cambio.
  • Se entrambi hanno AplusProfileId diversi: il sistema rimuove il vecchio e aggiunge il nuovo.

9. Processo “today” — esecuzione giornaliera

Il processo today è una rielaborazione batch che gira una volta al giorno (di norma la mattina presto, da scheduler/cron) e si occupa di portare la situazione su Aplus in linea con la giornata appena iniziata. È indispensabile: senza di esso i booking creati in giorni precedenti non risulterebbero abilitati su Aplus la mattina del booking.

9.1 Cosa fa il processo today

  1. Pulizia profili scaduti. Interroga Aplus per ottenere la lista dei profili la cui data fine è ormai passata e li rimuove uno per uno dagli utenti corrispondenti.
  2. Caricamento dei booking del giorno. Legge da ORnD tutti i booking che hanno inizio nel giorno corrente.
  3. Per ogni booking:
    • Se la risorsa è DailyPass: esegue handle_daily_pass_booking (crea company/member/PIN se mancano, aggiunge il profilo per la giornata) e registra l’ingresso (timbratura su Aplus + check-in su ORnD).
    • Se la risorsa è normale ma rilevante per Aplus: esegue handle_booking_created (crea company/member se mancano, aggiunge il profilo per la durata del booking).
    • Se la risorsa non è rilevante: salta.

9.2 Conseguenze importanti per l’operatore

  • Un booking creato per oggi viene gestito subito; un booking creato per un giorno futuro verrà gestito solo dal processo today della mattina del booking.
  • Se il processo today non parte (per esempio per un blocco dello scheduler), i booking per quel giorno non vengono abilitati su Aplus finché non si esegue manualmente. Segnalare al team tecnico se ci si accorge di accessi mancanti su un certo giorno.
  • La pulizia profili scaduti è anch’essa attivata dal processo today: se non gira, profili obsoleti restano sui member fino a esecuzione successiva.

10. Mappa dei campi sincronizzati

La sincronizzazione è limitata ai campi seguenti. Altri campi (telefono, indirizzo, partita IVA, ecc.) non sono attualmente sincronizzati.

ORnD Aplus Note
Building: AplusProfileId Site ID Lista di ID separati da virgola. Il primo è il site dove vengono creati i nuovi utenti.
Member: nome / cognome User: nome / cognome Sincronizzati su create e update.
Member: email User: email Chiave univoca in Aplus, blocca duplicati.
Member: aplusId User: IdUser Legame primario gestito da Charon.
Member: AplusPIN User: Pin Generato da Aplus. -1 = trigger rigenerazione.
Company: nome Company: nome Chiave univoca in Aplus.
Company: aplusId Company: IdCompany Legame primario. -1 = trigger creazione, -2 = cancellazione.
Resource type = DailyPass + AplusProfileId Profile assignment Vedi sezione 7.
Resource: AplusProfileId (non DailyPass) Profile assignment Vedi sezione 6.
Plan: AplusProfileId Profile assignment Vedi sezione 8.

11. Modalità test (per il team tecnico)

L’integrazione supporta tre modalità di esecuzione, attivate tramite il parametro test o variabili d’ambiente:

  • test=0 (produzione): scrive realmente su Aplus.
  • test=1 (debug): scrive realmente su Aplus ma con log estesi.
  • test=2 (mock): non contatta Aplus, simula le risposte (utile per provare il flusso senza impatti).

In ambiente di sviluppo è possibile usare lo strumento lancio_charon.php per rieseguire un payload archiviato (parametri: site, fout_file, test). In produzione si lavora sempre con test=0.

12. Diagnostica — significato dei messaggi più frequenti

Messaggio Significato e azione
“Building non di interesse — ignora” / “Office N non ha AplusProfileId configurato. Skip.” Il building del webhook non ha AplusProfileId: tutta l’integrazione è disabilitata per quel building. Vedi sezione 3.
“Resource not managed” / “Resource is not a managed bookable resource — skipping” La risorsa del booking non ha AplusProfileId e non è di tipo DailyPass. Vedi sezioni 6 e 7.
“Booking is not for today, skipping” Il booking è per un giorno futuro. Verrà gestito dal processo today nel giorno corretto. Vedi sezione 9.
“NOP-aplusId” / “NOP-update” Webhook ricevuto ma senza modifiche sostanziali per Aplus. Nessuna azione, comportamento corretto.
“Plan has no AplusProfileId — skipping” Il piano della membership/pass non ha AplusProfileId. Vedi sezione 8.
“Membership plan/resource has no AplusProfileId — skipping” Variante del precedente, lato membership. Stessa azione: configurare il piano.
“Member previously had aplusId=XYZ, restoring instead of creating new” Il member ha avuto in passato un utente Aplus con la stessa email: Charon ripristina l’ID precedente invece di creare un duplicato. Comportamento corretto.
“AplusPIN=-1, assigning PIN” Trigger di rigenerazione PIN ricevuto e in lavorazione. Vedi 5.4.
“assign_PIN: APLUS rejected request” Aplus non ha generato il PIN. Cause tipiche: utente non esistente o disabilitato. Verificare che il member abbia aplusId valido.
“today.inc: Expired profiles cleanup completed” Pulizia giornaliera dei profili scaduti completata. Informativo.
“today.inc: No bookings for today” Nessun booking da elaborare oggi. Informativo.
“MOCK: …” Modalità mock attiva: nessuna scrittura reale su Aplus. Se appare in produzione, segnalare al team tecnico.
“AplusSoapClient::addProfileToUser(): Argument #4 ($expiredProfileDate) must be of type string, null given” Bug risolto il 2026-05-26: una membership/booking arrivava senza endDate. Charon ora usa 2099-12-31T23:59:59 come default convenzionale per “no expiry”. Se ricompare con altro argomento (#3 startProfileDate), segnalare al team tecnico.

13. Casi pratici (cookbook)

Caso A — Abilitare un nuovo building all’integrazione

  1. Aprire il building in ORnD.
  2. Nelle Properties impostare AplusProfileId con l’ID site Aplus (o lista separata da virgola, vedi sezione 3).
  3. Salvare. Da questo momento tutti i webhook relativi al building verranno elaborati.

Caso B — Nuovo membro che entra in una azienda esistente

  1. Creare il member in ORnD nella company corretta.
  2. Charon non crea l’utente su Aplus fino a quando non serve davvero (membership attivata, booking del giorno) oppure finché l’operatore non forza con aplusId = -1.
  3. Quando l’utente verrà creato, il PIN sarà generato automaticamente e visibile in AplusPIN sul member.

Caso C — Voglio creare subito company/member e PIN su Aplus

  1. Aprire la company in ORnD, impostare aplusId = -1, salvare e attendere che aplusId diventi positivo.
  2. Aprire il member, impostare aplusId = -1 e salvare. Attendere che aplusId diventi positivo.
  3. Sempre sul member, impostare AplusPIN = -1 e salvare. Attendere che AplusPIN contenga un numero a 4 cifre.
  4. Comunicare il PIN al membro.

Caso D — Reset del PIN su richiesta del membro

  1. Aprire il member in ORnD.
  2. Impostare AplusPIN a -1 e salvare.
  3. Attendere qualche secondo, riaprire il member, leggere il nuovo PIN dalla proprietà AplusPIN.
  4. Comunicarlo al membro.

Caso E — Cessazione di un’azienda

  1. Aprire la company in ORnD.
  2. Impostare aplusId a -2 e salvare.
  3. Charon cancella la company su Aplus e disabilita gli utenti associati.

Caso F — Nuova risorsa giornaliera (es. “DailyPass Sala B”)

  1. Chiedere al team tecnico l’IdProfile Aplus corretto.
  2. In ORnD, sulla risorsa, impostare type = DailyPass e aggiungere la proprietà AplusProfileId con quel valore.
  3. Testare con un booking di prova in giornata: il booking deve passare da tentative a confirmed e il membro deve poter accedere.

Caso G — Membership chiusa anticipatamente

  1. In ORnD chiudere/terminare la membership del member (cambio status o impostazione data fine al passato).
  2. Charon riceve il webhook membership.updated con stato non più attivo e rimuove il profilo Aplus dal member.
  3. Il member, se non ha altre membership/booking attivi, da quel momento non riuscirà più ad accedere.

Caso H — Booking per la settimana prossima che “non funziona”

  1. Verificare che la risorsa abbia AplusProfileId (o type=DailyPass + AplusProfileId).
  2. Ricordare: i booking per giorni futuri vengono abilitati su Aplus solo la mattina del giorno del booking, dal processo today.
  3. Se il giorno del booking arriva e l’accesso continua a non funzionare, segnalare al team tecnico: probabilmente il processo today non è partito.

14. Cosa NON va fatto

  • Non modificare il PIN manualmente su Aplus: la prossima rigenerazione su ORnD lo sovrascriverebbe. Per cambiare il PIN si usa sempre il trigger AplusPIN=-1 su ORnD.
  • Non impostare aplusId a valori arbitrari diversi da -1, -2 o dall’identificativo Aplus reale: il sistema rifiuta o ignora valori non previsti.
  • Non creare manualmente la stessa company o lo stesso utente sia su ORnD che su Aplus: lasciare che sia Charon a propagare. Se entrambi esistono già da prima dell’integrazione, allineare i due tramite aplusId=-1 (Charon riusa l’entità Aplus esistente con stesso nome/email).
  • Non duplicare l’email di un member: è chiave univoca su Aplus e blocca la creazione.
  • Non cambiare l’AplusProfileId di un building a integrazione attiva senza coordinarsi con il team tecnico: gli utenti già creati restano sul vecchio site.

15. Glossario rapido dei marcatori

Marcatore Posizione Effetto
AplusProfileId = <id> (o lista id1,id2,…) Building Abilita il building all’integrazione. Il primo ID è il site di default per i nuovi utenti.
aplusId = -1 Company Crea (o riusa) la company su Aplus.
aplusId = -1 Member Crea (o ripristina) l’utente su Aplus.
aplusId = -2 Company Cancella la company da Aplus.
AplusPIN = -1 Member Rigenera il PIN.
AplusProfileId = <id> Risorsa “normale” Rende la risorsa rilevante per Aplus: i booking attivano il profilo.
type = DailyPass + AplusProfileId Risorsa Abilita la logica DailyPass (setup preventivo + profilo per la giornata).
AplusProfileId = <id> Piano (membership/pass) Abilita assegnazione/rimozione profilo per chi ha quel piano.

16. STID Mobile ID — integrazione VCard

STID Mobile ID è un sistema di credenziali virtuali su smartphone (VCard). L’integrazione consente di emettere automaticamente VCard ai member di ORnD e di revocarle alla cessazione del rapporto. È separata e indipendente dall’integrazione Aplus.

16.1 Concetti di base

Tenant STID. Un tenant rappresenta l’organizzazione cliente lato STID. Contiene un site (insieme di varchi gestiti), una o più reader configuration (configurazioni dei lettori), uno o più card layout (estetica della VCard).

VCard. Una credenziale virtuale legata a un singolo utente, identificata da un UUID. Ha uno stato (Sent, Active, Revoked…) e un periodo di validità.

Credito. Ogni VCard creata consuma 5 crediti dal saldo del tenant. Crearla e cancellarla prima che l’utente la attivi non consuma crediti.

16.2 Prerequisito: spazio abilitato all’integrazione

L’integrazione STID si attiva per uno spazio (es. betakeoff, stella33) solo se il team tecnico ha valorizzato le variabili $stid_enabled = true e le credenziali nel file web/set_ornd1.inc. Senza questo, qualunque trigger su STidEnabled viene ignorato.

Al 2026-05-19 lo stato è:

  • betakeoff — STID attivo (rollout iniziale).
  • stella33 — STID disattivo (rollout previsto dopo betakeoff).

16.3 Trigger manuale: il campo STidEnabled sul member

Su ORnD, ogni member ha una property custom STidEnabled (string). Il valore di questa property guida l’integrazione:

Valore Significato
(vuoto) o 0 Member non ancora gestito da STID. Charon può emettere una VCard automaticamente quando il member viene coinvolto in un booking (vedi 16.4).
-1 Forza emissione della VCard (anche se non c’è un booking che la giustifichi).
-2 Forza revoca della VCard esistente. Dopo la revoca, STidEnabled torna a vuoto.
UUID (32 caratteri esadecimali, es. 87e1ba60893e484d8a36ffc438fc2c10) La VCard è stata emessa con questo UUID. Charon lo gestisce automaticamente.

⚠️ Importante: i valori -1/-2 li imposta l’operatore; il valore UUID viene scritto in automatico da Charon dopo la creazione. L’operatore non deve modificare manualmente l’UUID.

ℹ️ Stato dopo revoca: a partire dalla versione 3.1 del manuale, dopo una revoca (sia esplicita -2 sia automatica dal job mensile, vedi 16.9) il campo STidEnabled torna a stringa vuota. Non c’è più il “reuse” della VCard precedente: una nuova richiesta di accesso genera una nuova VCard con un nuovo UUID (consumando 5 crediti). L’utente vedrà nella sua app sia la VCard vecchia (revocata) sia la nuova.

16.4 Auto-emissione VCard al primo booking

Charon emette automaticamente una VCard a qualunque member che venga coinvolto in un booking (su qualsiasi risorsa rilevante per Aplus), purché il campo STidEnabled del member sia ancora vuoto o 0.

Condizioni:

  • L’integrazione STID è attiva per lo spazio ($stid_enabled = true in set_ornd1.inc).
  • Il member ha STidEnabled non valorizzato (vuoto o 0); se contiene già un UUID, -1 o -2, l’auto-emissione non interviene e Charon rispetta lo stato esistente.
  • Il booking è di qualunque data (anche futura): l’utente riceve la VCard subito, con tempo di installare l’app STID e attivarla prima del giorno del booking.

Casi pratici dove serve:

  • Un ospite esterno prenota una sala riunioni → riceve automaticamente la VCard per accedere il giorno del booking.
  • Un freelance prenota un DailyPass → VCard emessa al momento della prenotazione.
  • Un member interno non ancora “censito” su STID prenota una risorsa → viene preso in carico anche da STID.

Per disabilitare l’auto-emissione su un singolo member: oggi non c’è un opt-out esplicito. Se serve, basta cancellare la VCard impostando STidEnabled = -2 dopo l’emissione automatica (la VCard viene revocata e il campo torna vuoto).

16.5 Trigger a livello company (emissione/revoca di massa)

In aggiunta al trigger sul singolo member, anche la company ha la property custom STidEnabled (stringa). Permette di gestire in massa tutti i member del team con una sola azione.

Valore su company.STidEnabled Effetto
-1 Emissione massiva: ogni member del team che non ha già un UUID riceve subito una VCard (stid_handle_request). I member con UUID già esistente NON sono toccati (evita doppie emissioni). I member con marker -1 pendente vengono comunque promossi a UUID; quelli con marker -2 vengono saltati (rispetta la revoca individuale richiesta).
-2 Revoca massiva: ogni member del team con UUID viene revocato. STidEnabled su ciascun member torna a vuoto.
altri valori Ignorato.

Marker permanente: dopo l’azione, company.STidEnabled resta sul valore impostato. Questo serve per la regola di ereditarietà: ogni nuovo member creato in una company con STidEnabled = -1 riceve subito una VCard alla creazione (Charon scrive direttamente l’UUID sul nuovo member, senza passare dallo stato intermedio -1).

Casi d’uso tipici:

  • Onboarding di tutta una nuova azienda cliente: imposta company.STidEnabled = -1 una volta, e tutti i collaboratori esistenti + futuri ricevono la VCard.
  • Disdetta di un’azienda intera: imposta company.STidEnabled = -2, tutti i collaboratori vengono revocati con un’unica azione.

Costo crediti: -1 sulla company spende 5 crediti per ogni member del team senza VCard. Valutare il costo prima di salvare.

16.6 Forzare l’emissione di una VCard (manuale)

Quando usarlo: un member deve avere accesso tramite STID e non ha ancora una VCard, oppure ha una VCard revocata e la si vuole riattivare.

Pre-requisito: il member deve avere almeno una membership attiva o un booking futuro/in corso che gli garantiscano una data fine validità. Se manca, l’operazione viene rifiutata.

Cosa fare:

  1. Su ORnD, aprire il member.
  2. Nella sezione Properties, impostare STidEnabled al valore -1.
  3. Salvare.

Cosa succede:

  • Charon riceve il webhook member.updated.
  • Calcola la data fine validità VCard:
    • max(endDate) tra le membership attive del team del member;
    • oppure, in mancanza di membership, max(end del booking) + 15 minuti.
  • Chiede a STID di creare una nuova VCard, oppure (se esiste un UUID precedente revocato) chiama il path di reuse.
  • STID invia automaticamente l’email di invito all’indirizzo del member.
  • L’UUID della VCard viene scritto in STidEnabled su ORnD (sovrascrivendo il -1).
  • Vengono consumati 5 crediti dal tenant STID (solo nel caso di nuova create, il reuse non consuma crediti).

Verifica:

  1. Riaprire il member dopo qualche secondo: STidEnabled deve contenere un UUID di 32 caratteri esadecimali.
  2. Il member deve aver ricevuto l’email di invito.
  3. Cliccando il link nell’email (idealmente dall’app STID Mobile ID sullo smartphone) la VCard viene attivata e diventa visibile nel portale STID.

16.6-bis Legame STID ↔ APLUS (propagazione del codice)

Dalla versione 3.3 del manuale (2026-05-26), quando Charon emette una VCard STID, propaga automaticamente il codice di lettura (PrivateId STID, a 10 cifre) anche all’utente APLUS via la SOAP AddCodeToUser. Serve per legare la credenziale mobile all’utente APLUS: quando l’utente avvicina lo smartphone al lettore, il lettore presenta ad APLUS un codice numerico; APLUS riconosce l’utente solo se quel codice gli è stato registrato in precedenza.

Flusso:

  1. Charon chiede a STID un PrivateId di lunghezza esatta 10 cifre (max 5 retry se la prima generazione è di lunghezza diversa).
  2. Crea la VCard STID con quel PrivateId.
  3. Se il member ha aplusId valido, chiama AddCodeToUser su APLUS con: code = PrivateId STID, CodeExpired = ValidityEndDateUtc della VCard STID, UserId = aplusId.

Pre-requisito: il member deve avere aplusId positivo. Se manca, la propagazione viene saltata e loggata come skipping APLUS code propagation.

Errori non fatali: se AddCodeToUser fallisce (APLUS irraggiungibile, codice già esistente, ecc.), la VCard STID resta valida e l’email d’invito è già stata inviata. L’operatore può riprovare la propagazione: vedi caso N nel cookbook.

16.7 Forzare la revoca di una VCard (manuale)

Quando usarlo: un member non deve più accedere via STID (uscita dall’azienda, smarrimento del telefono, ecc.).

Pre-requisito: la VCard deve essere attivata (Status = Active). Le VCard ancora in stato “Sent” (mai attivate dall’utente) non possono essere revocate; vanno invece cancellate dal portale STID dal team tecnico.

Cosa fare:

  1. Su ORnD, aprire il member.
  2. Impostare STidEnabled al valore -2.
  3. Salvare.

Cosa succede:

  • Charon revoca la VCard su STID.
  • Il valore STidEnabled torna a stringa vuota.
  • Se serve riemettere una VCard in futuro per lo stesso member, basta impostare di nuovo STidEnabled = -1 (oppure lasciare che ci pensi l’auto-emissione di 16.4 al prossimo booking): verrà generata una nuova VCard con un nuovo UUID (5 crediti).

16.8 Cancellazione di un member con VCard

Quando un member viene cancellato da ORnD, se aveva un UUID valorizzato in STidEnabled, Charon chiama l’endpoint di cancellazione STID per rimuovere la VCard definitivamente.

16.9 Aggiornamento automatico della validità (extend giornaliero)

Charon ricalcola la data fine validità VCard ad ogni evento che la può aver cambiata:

  • Modifica di una membership del team → se la nuova data fine è diversa da quella in STID, la VCard viene aggiornata.
  • Creazione/cancellazione di un booking → ricalcolo della data fine se il booking concorre alla validità.
  • Ogni giorno (processo today.today): per ciascun member con UUID, ricalcolo della data fine e aggiornamento STID se diversa.

L’operatore non deve fare nulla in questi casi: tutto è automatico. L’estensione non consuma crediti.

16.10 Revoca centralizzata mensile (1° del mese)

A partire dalla versione 3.1 del manuale, la revoca delle VCard scadute non è più giornaliera ma centralizzata:

  • Il 1° giorno di ogni mese, durante l’esecuzione di today.today, Charon scansiona tutti i member con STidEnabled = UUID e revoca quelli la cui validità è scaduta nel mese precedente (cioè data fine compresa fra il 1° e l’ultimo giorno del mese passato).
  • Per i member la cui validità è scaduta nel mese corrente (es. il 15 giugno e siamo al 20 giugno), la VCard resta valida lato STID fino al 1° luglio. La VCard “scaduta” non viene presentata ai varchi nel periodo intermedio (i varchi rifiutano se endDate è passata), ma Charon non la rimuove dal portale STID prima del 1° del mese seguente.
  • Quando la revoca avviene, STidEnabled su ORnD torna a stringa vuota.

Questo permette di raggruppare la pulizia STID in un’unica passata mensile invece di farne una al giorno.

16.11 Mappa dei campi STID

ORnD STID Note
Member: nome / cognome VCard: FirstName / LastName Sincronizzati al create e su update identità.
Member: email VCard: Email Destinatario dell’email di invito.
Member: STidEnabled VCard: Uuid Quando contiene un UUID = legame attivo.
Membership: endDate VCard: ValidityEndDateUtc La data fine VCard è il max delle membership attive del team.
Booking: fine + 15 minuti VCard: ValidityEndDateUtc Solo se non ci sono membership attive.

16.12 Diagnostica messaggi STID

Messaggio Significato e azione
stid_route_member_updated: idle, no-op STidEnabled è vuoto o 0. Comportamento normale: il member non è gestito da STID.
stid_handle_request: no validity source, resetting STidEnabled to 0 Il member non ha né membership attive né booking futuri. L’integrazione non emette la VCard. Soluzione: assegnare prima una membership al team.
stid_create_premium_vcard: VCard created Emissione riuscita. Il messaggio riporta l’UUID.
stid_handle_request: reused revoked VCard Il member aveva una VCard revocata, è stata riattivata senza consumare crediti.
stid_handle_revoke: already revoked, no-op STidEnabled = -2 ma la VCard era già revocata su STID. Nessuna azione.
today.inc: STID credit balance low Il saldo crediti STID è sotto soglia (default 20). Acquistare nuovi crediti.
stid_bridge: ... HTTP 401 / 403 Problema di autenticazione STID. Verificare credenziali in set_ornd1.inc.
Card ... can't be revoked because it has status Sent Si è tentato di revocare una VCard mai attivata dall’utente. Il portale STID consente di cancellarla, non di revocarla.
stid_handle_request: code propagated to APLUS Codice STID (PrivateId) registrato anche su APLUS via AddCodeToUser. Comportamento normale. Vedi 16.6-bis.
stid_handle_request: failed to propagate code to APLUS Il codice STID non è stato registrato su APLUS. La VCard STID resta valida; ma il lettore APLUS non riconoscerà l’utente finché il codice non viene propagato. Vedi caso N.
stid_handle_request: skipping APLUS code propagation Il member non ha aplusId valido oppure STID non ha restituito un PrivateId. Verificare prima la creazione utente APLUS (vedi 5.3).
stid_generate_private_id: failed to obtain expected length after retries STID non ha restituito un PrivateId di lunghezza 10 nei 5 tentativi. Probabile cambio della reader configuration STID; segnalare al team tecnico.

16.13 Casi pratici STID (cookbook)

Caso I — Nuovo collaboratore che deve accedere tramite app STID

  1. Su ORnD: creare il member nella company corretta, assicurarsi che la company abbia una membership attiva.
  2. Aggiungere la property STidEnabled = -1 sul member.
  3. Attendere qualche secondo: l’email di invito arriva al member e STidEnabled su ORnD contiene ora un UUID.
  4. Comunicare al member di installare l’app STID Mobile ID e attivare la VCard cliccando il link nell’email.

Caso J — Cessazione di un collaboratore con VCard attiva

  1. Su ORnD: aprire il member.
  2. Impostare STidEnabled = -2.
  3. Salvare. Charon revoca la VCard su STID; l’app del member smetterà di funzionare ai varchi.

Caso K — Cancellazione completa del member

  1. Cancellare il member da ORnD.
  2. Charon cancella la VCard su STID; non è necessaria alcuna azione manuale lato STID.

Caso L — Member che non riceve l’email di invito STID

  1. Verificare che STidEnabled su ORnD contenga effettivamente un UUID (non resti -1).
  2. Se contiene -1 o 0, vuol dire che la creazione è fallita: cercare nei log il messaggio stid_handle_request: no validity source o un errore HTTP STID.
  3. Se contiene un UUID ma l’email non è arrivata: verificare la cartella spam del member; in alternativa il team tecnico può rilanciare l’invito dal portale STID.

Caso N — La VCard STID c’è ma il lettore APLUS non riconosce l’utente

  1. Probabile causa: la propagazione del codice STID → APLUS è fallita. Cercare nei log stid_handle_request: failed to propagate code to APLUS per il member in questione.
  2. Verificare che il member abbia aplusId valido su ORnD; se manca, vedi caso C.
  3. Quando il member ha aplusId valido, impostare di nuovo STidEnabled = -1 per rieseguire il flusso completo (questo riemetterà una nuova VCard, consumando 5 crediti). In alternativa il team tecnico può richiamare manualmente AddCodeToUser lato APLUS.

Caso M — Estensione della validità di una VCard quando si rinnova una membership

  1. Su ORnD: rinnovare la membership del team (allungare endDate).
  2. Charon riceve il webhook membership.updated, ricalcola la data fine VCard, e aggiorna automaticamente STID con la nuova scadenza.
  3. Nessuna azione manuale sul member.

16.14 Cosa NON va fatto

  • Non modificare manualmente l’UUID contenuto in STidEnabled: il valore è scritto da Charon a partire dalla risposta di STID. Cambiarlo manualmente crea un disallineamento tra ORnD e STID che porta a Charon a non riconoscere più la VCard.
  • Non impostare STidEnabled a valori arbitrari diversi da -1, -2, 0 o un UUID legittimo.
  • Non revocare/cancellare manualmente la VCard dal portale STID se il member è ancora attivo su ORnD: alla prossima sincronizzazione il sistema potrebbe ricrearla.

16.15 Glossario marcatori STID

Marcatore Posizione Effetto
STidEnabled = -1 Member Forza emissione VCard.
STidEnabled = -2 Member Forza revoca VCard attiva. Dopo revoca il campo torna vuoto.
STidEnabled = 0 o vuoto Member Member non ancora gestito da STID. Auto-emissione al primo booking (vedi 16.4).
STidEnabled = <uuid 32 hex> Member VCard emessa con quell’UUID (scritto da Charon, non modificare a mano).
STidEnabled = -1 Company Emissione massiva per i member del team senza UUID. Marker permanente: nuovi member ereditano l’emissione.
STidEnabled = -2 Company Revoca massiva per tutti i member del team con UUID.

17. Manutenzione del manuale

⚠️ Importante per chi modifica il codice: ogni volta che si aggiunge una nuova funzionalità all’integrazione, si modifica una regola esistente o si introducono nuovi marcatori (valori speciali nelle proprietà), questo manuale va aggiornato in modo corrispondente. Aggiornare anche il numero di versione e la data in cima al documento.

Il file sorgente è charonlite/docs/manuale-operatore-integrazione-ORnD-APLUS.md. È un file Markdown editabile da qualunque editor; è il sorgente master del manuale.

Fine documento.