PaxLog#
Source de cette page
Chaque affirmation est sourcée du code (chemin de fichier indiqué).
Les workflows reflètent l'état réel du MANIFEST + des routes
après les fixes de l'audit 2026-04-29 (commit
11a978c4).
Tout ce qui n'est pas vérifiable dans le code est marqué
TODO: vérifier.
Résumé en 30 secondes#
PaxLog gère les mouvements de personnel vers et depuis les sites opérationnels (offshore, sites pétroliers, chantiers isolés). Trois objets-pivots :
- ADS (Avis De Séjour) — workflow d'autorisation pour qu'une personne se rende sur un site donné
- AVM (Avis de Mission) — dossier mission orchestrant toute la préparation logistique (visas, EPI, badges, allocations)
- Profil PAX — fiche personne (interne ou externe) avec habilitations, formations, identifiants légaux
L'AVM est le conteneur d'une mission ; il génère N programmes, chaque programme génère 1 ADS. Le couplage ADS → TravelWiz crée ensuite le manifeste passager du voyage qui transporte les PAX.
Stack : 22 modèles SQLAlchemy (app/models/paxlog.py),
~50 endpoints API (app/api/routes/modules/paxlog/),
9 onglets frontend (apps/main/src/pages/paxlog/),
47 permissions, 8 événements émis, intégrations Planner / TravelWiz /
Conformité / Imputations.
1. À quoi ça sert#
Problème métier : un opérateur industriel (oil & gas, maritime, construction) doit faire venir des intervenants — internes ou sous-traitants — sur des sites éloignés ou sécurisés. Chaque intervention a des contraintes croisées :
- Conformité — la personne a-t-elle les habilitations valides (BST, induction, visite médicale, ATEX, IRATA, …) requises pour ce site et ce poste ?
- Capacité POB — la couchette est-elle disponible ce jour-là sur le site (nuit non disponible = ADS en file d'attente) ?
- Logistique — qui paye (imputation cost center / projet), comment voyager, badges, visas, EPI, allocations de déplacement ?
- Traçabilité réglementaire — qui était sur le site quand, sous quelle responsabilité, avec quelle décision écrite ?
PaxLog matérialise ce parcours, génère les références (ADS-2026-0042), contrôle automatiquement la conformité, route les approbations vers les bons acteurs, enclenche la chaîne TravelWiz pour le transport.
Pour qui :
| Rôle utilisateur | Permissions clés (depuis app/modules/paxlog/__init__.py) |
|---|---|
| Demandeur (chef projet, chef chantier, RH, sous-traitant) | paxlog.ads.create, paxlog.ads.update, paxlog.avm.create |
| Valideur conformité (CDS / HSE) | paxlog.compliance.read, paxlog.ads.read_all, paxlog.ads.approve |
| Valideur projet (chef projet d'accueil) | paxlog.ads.read_all, paxlog.ads.approve (workflow auto-route quand project_id est renseigné) |
| Validateur AVM | paxlog.avm.approve, paxlog.avm.complete |
| Gestionnaire PAX (RH, secrétariat) | paxlog.profile.read/create/update, paxlog.credential.create, paxlog.credential.validate |
| Gestionnaire rotations | paxlog.rotation.manage |
| Gestionnaire incidents | paxlog.incident.read/create/update/resolve |
L'UI déduit le profil dominant automatiquement
(PaxLogPage.tsx:62-65) :
- isAdmin — possède * ou admin.system
- isRequesterProfile — peut créer ADS/AVM mais pas voir
profiles/compliance/rotations
- isValidatorProfile — peut approuver ADS/AVM ou lire la
compliance
L'onglet "Accueil" change d'apparence selon le profil
(tabs/RequesterHomeTab.tsx
vs tabs/ValidatorHomeTab.tsx).
2. Concepts clés (vocabulaire)#
| Terme | Modèle SQLAlchemy | Table | Description |
|---|---|---|---|
| ADS (Avis De Séjour) | Ads |
ads |
L'autorisation de séjour pour 1+ PAX sur un site. Référence ADS-{YYYY}-{####}. |
| AdsPax | AdsPax |
ads_pax |
Lien ADS ↔ PAX (user OU contact externe — XOR contraint en DB). Porte le statut conformité par PAX. |
| AdsAllowedCompany | AdsAllowedCompany |
ads_allowed_companies |
Liste blanche de sociétés autorisées sur l'ADS (cross-company workflow). |
| AdsEvent | AdsEvent |
ads_events |
Audit log des transitions de statut + actions clés. |
| AVM (Avis de Mission) | MissionNotice |
mission_notices |
Dossier mission complet — orchestre tasks, programs, stakeholders. Réf MAN-{YYYY}-{####}. |
| MissionProgram | MissionProgram |
mission_programs |
Une étape d'une mission (1 site, 1 plage de dates). Génère 1 ADS via generated_ads_id. |
| MissionProgramPax | MissionProgramPax |
mission_program_pax |
PAX assignés à un programme particulier d'une AVM. |
| MissionPreparationTask | MissionPreparationTask |
mission_preparation_tasks |
Tâche de préparation (badge, visa, EPI, formation). Statuts : todo, in_progress, blocked, done. |
| MissionVisaFollowup | MissionVisaFollowup |
mission_visa_followups |
Suivi visa par PAX et par destination. |
| MissionAllowanceRequest | MissionAllowanceRequest |
mission_allowance_requests |
Demande d'allocation de déplacement (per diem, transport, hébergement). |
| MissionStakeholder | MissionStakeholder |
mission_stakeholders |
Acteurs impliqués dans une AVM (chef de mission, contact terrain, etc.). |
PaxProfile (via users/tier_contacts) |
— | — | Une personne — soit User interne, soit TierContact externe. PaxLog ne crée pas son propre modèle PAX ; il lit les utilisateurs OpsFlux + les contacts du module Tiers. |
| PaxGroup | PaxGroup |
pax_groups |
Regroupement de PAX (équipe, société, projet). Cible possible des incidents collectifs. |
| CredentialType | CredentialType |
credential_types |
Type de qualification (BST, IRATA, ATEX, …). Géré au niveau entity. |
| PaxCredential | PaxCredential |
pax_credentials |
Instance d'une qualif sur une personne (date de validité, justificatif). |
| ComplianceMatrixEntry | ComplianceMatrixEntry |
compliance_matrix_entries |
"Pour ce poste sur ce site, tel credential est obligatoire/recommandé". |
| ProfileType | ProfileType |
profile_types |
Catégorie métier (BOSCO, CARISTE, GRUTIER, HSE, ELEC, …). Taxonomie configurable. |
| PaxProfileType | PaxProfileType |
pax_profile_types |
Lien PAX ↔ profile_type avec is_primary. Une personne peut cumuler plusieurs profils. |
| ProfileHabilitationMatrix | ProfileHabilitationMatrix |
profile_habilitation_matrix |
Habilitations attendues par profile_type — alimente le seed de la matrice de conformité. |
| PaxRotationCycle | PaxRotationCycle |
pax_rotation_cycles |
Cycle off/on (ex. 28-jour offshore : 28 j sur site, 28 j off). Sert à anticiper les ADS futures. |
| StayProgram | StayProgram |
stay_programs |
Programme de séjour intra-champ (ADS multi-sites pour une même personne). |
| PaxIncident | PaxIncident |
pax_incidents |
Signalement HSE / discipline / sécurité avec décision possible (avertissement, exclusion, blacklist temp/permanent). |
| PaxCompanyGroup | PaxCompanyGroup |
pax_company_groups |
Regroupement de sociétés sous-traitantes (utile pour les bans collectifs). |
| ExternalAccessLink | ExternalAccessLink |
external_access_links |
Lien magique signé pour qu'un PAX externe complète son dossier (cf. portail ext.opsflux.io). |
Toutes ces entités vivent dans
app/models/paxlog.py(≈930 lignes). Les contraintesCHECKSQL définies y déterminent la liste autoritaire des statuts et catégories.
3. Architecture data#
graph TD
AVM[AVM<br/>MissionNotice]
PROG[MissionProgram<br/>1 site, 1 plage]
TASK[MissionPreparationTask<br/>badge, visa, EPI, …]
SH[MissionStakeholder]
ALLOW[MissionAllowanceRequest]
VISA[MissionVisaFollowup]
AVM -->|1..N| PROG
AVM -->|0..N| TASK
AVM -->|0..N| SH
AVM -->|0..N| ALLOW
AVM -->|0..N| VISA
PROG -->|génère 1| ADS[ADS<br/>Ads]
PROG -->|0..N| MPP[MissionProgramPax]
ADS -->|1..N| ADSPAX[AdsPax<br/>user XOR contact]
ADS -->|0..N| ALLOWED[AdsAllowedCompany]
ADS -->|0..N| EVT[AdsEvent<br/>audit log]
ADS -->|0..N| EXTLINK[ExternalAccessLink<br/>magic link]
USER[User interne]
CONTACT[TierContact externe]
ADSPAX -->|XOR| USER
ADSPAX -->|XOR| CONTACT
PAX[PaxProfile<br/>= User OR TierContact]
PAX -->|0..N| CRED[PaxCredential]
PAX -->|0..N| PT[PaxProfileType]
PAX -->|0..N| INC[PaxIncident]
PAX -->|0..1| ROT[PaxRotationCycle]
PAX -->|0..N| STAY[StayProgram]
CRED -->|type| CT[CredentialType]
CMM[ComplianceMatrixEntry<br/>profile×site requirement] -->|requires| CT
PT -->|profile| PROFTYPE[ProfileType]
PROFTYPE -->|expects| PHM[ProfileHabilitationMatrix]
Lecture rapide :
- Un PAX peut être interne (User OpsFlux) ou externe
(TierContact). Le XOR est imposé par
AdsPax(CHECK(user_id IS NOT NULL AND contact_id IS NULL) OR (user_id IS NULL AND contact_id IS NOT NULL)). - Une ADS transporte 1..N PAX vers 1 site (
site_entry_asset_idréférencear_installations.id). - Une AVM organise 1..N programmes, chacun générant 1 ADS
(
mission_programs.generated_ads_id). - La conformité est calculée à la soumission de l'ADS : pour
chaque PAX, l'API croise ses
PaxCredentialvalides avec laComplianceMatrixEntrydu site → statutcompliant/blockedau niveauAdsPax.
4. Workflow ADS — états et transitions#
États (CHECK constraint, app/models/paxlog.py:178-182)#
draft, submitted, pending_initiator_review, pending_project_review,
pending_compliance, pending_validation, approved, rejected, cancelled,
requires_review, pending_arbitration, in_progress, completed
Diagramme#
stateDiagram-v2
[*] --> draft
draft --> pending_initiator_review : submit (créateur ≠ requester)
draft --> pending_project_review : submit (project_id renseigné)
draft --> pending_compliance : submit (compliance fail)
draft --> pending_validation : submit (compliance OK, sans projet)
pending_initiator_review --> pending_project_review
pending_initiator_review --> pending_compliance
pending_initiator_review --> pending_validation
pending_project_review --> pending_compliance
pending_project_review --> pending_validation
pending_project_review --> rejected
pending_compliance --> pending_validation : credentials régularisés
pending_compliance --> rejected
pending_validation --> approved
pending_validation --> rejected
pending_validation --> requires_review : changement Planner / dates
pending_validation --> pending_arbitration : conflit POB / capacité
pending_arbitration --> approved
pending_arbitration --> rejected
approved --> in_progress : embarquement effectif (manifest TravelWiz)
approved --> requires_review : changement aval (dates, conformité)
approved --> cancelled
requires_review --> pending_validation : resubmit
requires_review --> cancelled
in_progress --> completed : retour effectif
in_progress --> cancelled
rejected --> [*]
cancelled --> [*]
completed --> [*]
Endpoints qui pilotent les transitions#
| Action UI | Endpoint | Source |
|---|---|---|
| Créer brouillon | POST /api/v1/pax/ads |
paxlog/__init__.py:3539 |
| Soumettre | POST /api/v1/pax/ads/{id}/submit |
paxlog/__init__.py:3930 |
| Approuver | POST /api/v1/pax/ads/{id}/approve |
paxlog/__init__.py:4291 |
| Rejeter | POST /api/v1/pax/ads/{id}/reject |
paxlog/__init__.py:4718 |
| Demander révision | POST /api/v1/pax/ads/{id}/request-review |
paxlog/__init__.py:4851 |
| Annuler | POST /api/v1/pax/ads/{id}/cancel |
paxlog/__init__.py:4932 |
| Démarrer (in_progress) | POST /api/v1/pax/ads/{id}/start-progress |
paxlog/__init__.py:5075 |
| Manuel — départ | POST /api/v1/pax/ads/{id}/manual-departure |
paxlog/__init__.py:5029 |
| Compléter | POST /api/v1/pax/ads/{id}/complete |
paxlog/__init__.py:5152 |
| Re-soumettre | POST /api/v1/pax/ads/{id}/resubmit |
paxlog/__init__.py:5226 |
| Décision par PAX | POST /api/v1/pax/ads/{id}/pax/{entry_id}/decision |
paxlog/__init__.py:5436 |
Le workflow réel est piloté par le FSM Engine
(app/services/workflow/)
+ definitions par défaut seedées via _default_workflow_definitions()
(app/services/core/seed_service.py:30).
Les routes appellent _resolve_ads_auto_transition() qui détermine
l'état suivant selon le contexte (présence d'un projet, échec
conformité, créateur ≠ requester).
Compliance check à la soumission#
Pour chaque PAX listé dans l'ADS, à submit :
sequenceDiagram
participant U as Demandeur
participant API as POST /ads/{id}/submit
participant DB as DB
participant FSM as FSM Engine
participant EVT as EventBus
U->>API: submit ADS
API->>DB: load ads + ads_pax + site_entry_asset
API->>DB: load compliance_matrix_entries (site, profile_type)
loop par AdsPax
API->>DB: load PaxCredential WHERE user_id|contact_id
API->>API: missing = required ∩ valid_credentials
alt all required present + non expirés
API->>DB: ads_pax.status = compliant
else
API->>DB: ads_pax.status = blocked
API->>DB: ads_pax.compliance_summary = JSONB(missing)
end
end
alt aucun blocked
API->>FSM: transition → pending_validation
API->>EVT: emit ads.submitted
else
API->>FSM: transition → pending_compliance
API->>EVT: emit ads.compliance_failed
end
API-->>U: 200 + AdsRead (avec compliance_summary par pax)
Détail vérifié : la docstring du handler ligne 3942 dit "If any PAX has missing/expired mandatory credentials → pending_compliance". Le statut
pending_compliancepermet aux PAX d'être régularisés (ajout/upload de credentials manquants) sans rejeter l'ADS — c'est un état "en attente de papiers" pas un rejet.
POB / Waitlist#
À l'approbation, l'API contrôle la POB (places couchage) du site sur la plage de dates. Si dépassement :
- Les PAX en surnombre passent en
waitlisted(paxlog/__init__.py:4251émetads.waitlisted) - L'onglet Waitlist (
tabs/WaitlistTab.tsx) liste les PAX en attente avec leurpriority_score - Endpoint admin :
POST /api/v1/pax/ads-waitlist/{entry_id}/priority(paxlog/__init__.py:3457) - Promotion auto : quand de la place se libère (cancel d'une autre
ADS, no_show), l'event handler
module_handlers.py:1951promeut les PAX waitlisted par ordre de priorité et émetpaxlog.waitlist_promoted.
5. Workflow AVM — états et transitions#
États (app/models/paxlog.py:397-400)#
Diagramme#
stateDiagram-v2
[*] --> draft
draft --> in_preparation : submit (tasks créées)
in_preparation --> ready : toutes tasks 'done'
in_preparation --> cancelled
ready --> active : programmes générés (ADS)
active --> completed : retour effectif
active --> cancelled
ready --> cancelled
completed --> [*]
cancelled --> [*]
Note :
activen'est pas un état terminal — l'AVM y reste tant que les ADS générées ne sont pas toutescompleted. Le module handlerevent_handlers/module_handlers.py:657propageads.completedvers la cascade : si toutes les ADS d'une AVM sont completed, l'AVM est automatiquement marquéecompleted. [TODO: vérifier le détail de cette cascade automatique dans le code.]
Endpoints AVM (avm.py)#
| Action | Endpoint | Ligne |
|---|---|---|
| Lister | GET /api/v1/pax/avm |
413 |
| Créer | POST /api/v1/pax/avm |
500 |
GET /api/v1/pax/avm/{id}/pdf |
651 | |
| Modifier | PUT /api/v1/pax/avm/{id} |
702 |
| Soumettre | POST /api/v1/pax/avm/{id}/submit |
752 |
| Approuver | POST /api/v1/pax/avm/{id}/approve |
795 |
| Compléter | POST /api/v1/pax/avm/{id}/complete |
820 |
| Annuler | POST /api/v1/pax/avm/{id}/cancel |
845 |
| Modifier après approve | POST /api/v1/pax/avm/{id}/modify |
1009 |
| Update tâche prépa | PATCH /api/v1/pax/avm/{id}/preparation-tasks/{task_id} |
1175 |
Préparation : tasks de mission#
Quand une AVM est créée, l'utilisateur ajoute des MissionPreparationTask
correspondant aux items configurés sur la mission (badges, EPI, visa,
formation). Chaque task a son propre cycle : todo → in_progress → done
(ou blocked). L'AVM ne peut passer en ready que quand toutes les
tasks obligatoires sont done.
Type de task (pax_preparation_task_type dictionnaire) :
badge, formation, visa, epi, induction, allowance, transport, autre.
6. Step-by-step utilisateur#
6.1 — Demandeur : créer et suivre une ADS#
Pré-requis#
- Permission
paxlog.ads.create(sinon le bouton "Nouvelle ADS" n'apparaît pas —PaxLogPage.tsx:104) - Connaître le site de destination (asset/installation enregistrée dans Asset Registry)
- Connaître les PAX à embarquer — soit des Users OpsFlux, soit des contacts externes du module Tiers
Procédure#
- Aller dans
/paxlog→ ongletADS - Cliquer
+ Nouvelle ADS(toolbar en haut) - Le panneau
CreateAdsPanels'ouvre à droite (panels/CreateAdsPanel.tsx) - Renseigner :
- Type :
individual(1 PAX) outeam(équipe complète) - Site d'entrée (
site_entry_asset_id) — picker installations - Catégorie de visite : project_work, maintenance, inspection,
visit, permanent_ops, other
- Si
project_work→ un picker projet apparaît, l'ADS est routée vers le chef projet pour pre-validation
- Si
- Plage de dates (start_date, end_date) — contrainte DB
end_date >= start_date - Justification (
visit_purpose) — texte libre obligatoire - Transport outbound/return (mode, base de départ) — optionnel mais recommandé
- Aller-retour sans nuitée ? — flag
is_round_trip_no_overnight, compte dans le forecast PAX du jour mais ne consomme pas de POB nuitée - PAX à embarquer — picker User ou TierContact, ajout multiple
- Sociétés autorisées (cross-company) — optionnel, restreint les contacts externes éligibles
- Enregistrer → ADS créée en
draft, référence générée automatiquement (format depuisreference_template:ADSdans Settings, défautADS-{YYYY}-{####}) - Cliquer
Soumettre→ l'ADS part en validation. Selon le contexte : - Créateur ≠ requester →
pending_initiator_review(le requester valide d'abord que l'ADS est faite en son nom) - Catégorie
project_workavecproject_id→pending_project_review - Sinon → check conformité immédiat →
pending_complianceoupending_validation - Suivre : onglet
ADS→ filtrer par "mes ADS" / par statut. L'icône statut renseigne l'étape (shared.tsx:25-39) - Si
pending_compliance: l'ongletCompliancemontre quels credentials manquent/expirent. Le PAX (s'il a accès) ou un gestionnaire RH doit les ajouter. - Approbation : l'ADS passe à
approved, événementads.approvedémis → TravelWiz ajoute automatiquement les PAX au manifest du voyage couvrant la plage de dates (event_handlers/travelwiz_handlers.py) - Embarquement : un opérateur héliport scanne le QR code de
l'ADS via
AdsBoardingScanPage(ou la captain UI). Statut PAX passe àcurrent_onboard=true. Quand toute l'équipe est onboard → ADSin_progress. - Retour :
completeune fois le débarquement scanné → statutcompleted, événementads.completed.
Cas d'erreur fréquents#
- Bouton soumettre disabled : un champ obligatoire manque (mainly
visit_purposeousite_entry_asset_id). - 400 "Impossible de soumettre" : statut courant pas dans
(draft, requires_review)(paxlog/__init__.py:3961). Re-créer un brouillon ou demander unrequires_reviewau valideur. - 403 sur certaines actions : tu n'es ni
requester_idnicreated_by, et tu n'as paspaxlog.ads.read_all/paxlog.ads.approve. C'est_can_manage_ads()qui filtre.
6.2 — Valideur : traiter la file de validation#
- Aller dans
/paxlog→ l'onglet d'accueil affiche par défaut laValidatorHomeTabsi tu as les permissions de validation (PaxLogPage.tsx:65) - Liste prioritaire :
- ADS-validation queue :
GET /api/v1/pax/ads-validation-queue(paxlog/__init__.py:3165) — toutes les ADS dans un statutpending_*, triées par date d'embarquement la plus proche - Filtres : statut, site, demandeur, date
- Cliquer une ADS → panneau
AdsDetailPanel(panels/AdsDetailPanel.tsx) avec onglets : Détails, PAX & Conformité, Imputations, Audit log, Documents - Décider PAX par PAX : le tableau PAX permet de
approve/rejectchaque ligne viaPOST /api/v1/pax/ads/{id}/pax/{entry_id}/decision(paxlog/__init__.py:5436). Utile pour approuver l'équipe principale tout en rejetant un PAX dont la formation BST a expiré. - Approuver toute l'ADS : bouton "Approuver" →
POST /ads/{id}/approve - Vérifie POB du site sur la plage
- Si dépassement → certains PAX en
waitlisted(l'UI affiche un warning) - Émet
ads.approved→ TravelWiz crée/complète le manifest - Rejeter : bouton "Rejeter" → modale demandant
rejection_reasonobligatoire (validé côté API). Émetads.rejected→ notification au demandeur. - Renvoyer pour révision :
request-review→ l'ADS passe enrequires_reviewcôté demandeur, qui peut éditer puis re-soumettre.
6.3 — Gestionnaire PAX (RH / secrétariat)#
Créer un profil PAX#
/paxlog→ ongletProfiles(visible sipaxlog.profile.read)+ Nouveau profil→CreateProfilePanel(panels/CreateProfilePanel.tsx)- Le panneau distingue :
- PAX interne → crée un User OpsFlux avec
user_type=pax - PAX externe → crée un TierContact rattaché à une société
- Anti-doublon :
POST /api/v1/pax/profiles/check-duplicates(paxlog/__init__.py:2497) compare nom/prénom/email/téléphone/identifiant légal avant de créer. - Une fois créé : ajouter les credentials (formations, visites
médicales, certifications). Le composant
PaxCredentialManagerliste les types disponibles viaGET /credential-types(paxlog/__init__.py:2744).
Profil dynamique (profile types)#
L'UI permet de tagger un PAX avec un ou plusieurs profile types
(BOSCO, CARISTE, …) — onglet Profile types du ProfileDetailPanel.
La matrice habilitation auto-suggère les credentials manquants
selon les profils du PAX (POST /api/v1/pax/pax/{id}/profile-types/{pt_id},
profile_types.py:138).
Compliance dashboard#
L'onglet Compliance (tabs/ComplianceTab.tsx)
expose deux endpoints data :
GET /api/v1/pax/compliance/expiring(compliance_dashboard.py:41) — credentials qui expirent dans < N jours (paramdays, défaut 90)GET /api/v1/pax/compliance/stats(compliance_dashboard.py:147) — KPIs globaux (total PAX, % conformes, expirés, manquants)
6.4 — Gestionnaire rotations#
L'onglet Rotations (tabs/RotationsTab.tsx)
gère les cycles offshore. Création :
- Bouton
+ Nouvelle rotation→CreateRotationPanel - Type de cycle : 28-28 (28 j on, 28 j off — standard offshore), 14-14, 21-21, custom
- Date de référence + nombre de PAX
- Le système calcule les ADS futures (auto-pré-création possible selon le projet — TODO: vérifier ce comportement dans le code)
Endpoints : GET/POST/PATCH/DELETE /api/v1/pax/rotation-cycles
(rotations.py).
6.5 — Gestionnaire incidents (HSE / discipline)#
L'onglet Signalements distingue incident PAX (objet
historique) et signalement enrichi avec workflow :
- Incident simple :
PaxIncidentavec severity et description - Signalement : ajoute
category(hse/discipline/access/security) decision(avertissement / exclusion / blacklist temp / blacklist permanent)decision_duration_days+evidence_urls
Workflow signalement :
stateDiagram-v2
[*] --> created : POST /signalements
created --> resolved : POST /signalements/{id}/resolve (+ decision)
resolved --> validated : POST /signalements/{id}/validate (CDS)
validated --> lifted : POST /signalements/{id}/lift (fin de ban)
validated --> [*] : ban permanent
Sources : signalements.py:40-279.
⚠ Quand un PAX est en
permanent_banoutemp_banactif, ses ADS sont rejetées automatiquement à la soumission. Vérification faite dans_check_pax_ban_status()lors du compliance check. [TODO: vérifier la fonction exacte dans paxlog/init.py.]
7. Permissions matrix#
47 permissions définies dans le MANIFEST
(app/modules/paxlog/__init__.py:9-54).
Visibilité des onglets vs permissions (PaxLogPage.tsx:67-80)#
| Onglet | Visible si l'utilisateur a |
|---|---|
dashboard |
Toujours visible |
ads |
paxlog.ads.read OU .create OU .update OU .approve |
waitlist |
paxlog.ads.approve |
avm |
paxlog.avm.create OU .update OU .approve OU .complete |
profiles |
paxlog.profile.read |
compliance |
paxlog.compliance.read |
signalements |
paxlog.incident.read |
rotations |
paxlog.rotation.manage |
Liste exhaustive des permissions#
paxlog.profile.{read,create,update,delete}
paxlog.credential.{read,create,update,delete,validate}
paxlog.credential_type.{read,create,update,delete}
paxlog.compliance.{read,manage}
paxlog.ads.{read,read_all,create,update,delete,submit,cancel,approve,pax.manage}
paxlog.avm.{read,read_all,create,update,submit,approve,complete,cancel}
paxlog.stay.{create,approve}
paxlog.rotation.manage
paxlog.profile_type.manage
paxlog.credtype.manage
paxlog.incident.{read,create,update,delete,resolve}
paxlog.import
paxlog.export
Distinction read vs read_all#
paxlog.ads.read→ l'utilisateur voit les ADS dont il estrequester_id,created_by, ou listé dansads_paxpaxlog.ads.read_all→ l'utilisateur voit toutes les ADS de l'entity (typiquement CDS, HSE, valideurs)
Logique : _can_manage_ads() dans paxlog/init.py.
8. Événements émis et consommés#
Émis par PaxLog#
Listés via grep event_type= dans app/api/routes/modules/paxlog/ +
app/event_handlers/paxlog_handlers.py.
| Événement | Source ligne | Quand | Payload |
|---|---|---|---|
ads.submitted |
paxlog/init.py:4220 | Soumission validée | ads_id, reference, requester_id, site_id |
ads.compliance_failed |
paxlog/init.py:4271 | Compliance check échoue | ads_id, blocked_pax: [{pax_id, missing_credentials}] |
ads.waitlisted |
paxlog/init.py:4251 | POB dépassé | ads_id, waitlisted_pax_ids |
ads.approved |
paxlog/init.py:4698 | Approbation finale | ads_id, reference, approved_pax: [...] |
ads.rejected |
paxlog/init.py:4837 | Rejet | ads_id, rejection_reason |
ads.cancelled |
paxlog/init.py:5014 | Annulation | ads_id, cancellation_reason |
ads.requires_review |
paxlog/init.py:4918 | Demande de révision | ads_id, review_reason |
ads.in_progress |
paxlog/init.py:5141 | Embarquement effectif | ads_id, departed_at |
ads.completed |
event_handlers/module_handlers.py:657 | Retour effectif | ads_id, completed_at |
ads.stay_change_requested |
paxlog/init.py:3912 | Demande de changement de séjour | ads_id, requested_change |
paxlog.waitlist_promoted |
paxlog/init.py:1582 | Promotion auto depuis waitlist | ads_id, promoted_pax_ids |
Consommés par PaxLog#
app/event_handlers/paxlog_handlers.py:824
souscrit à :
ads.submitted— notification email au requester + valideursads.rejected— notification au demandeur avec raisonads.compliance_failed— notification gestionnaire RH avec liste credentials manquantsads.cancelled— notification + libération POB
Cross-module#
module_handlers.py:1951
fait souscrire TravelWiz, Planner, Conformité à des
événements ADS pour cascader des effets :
ads.approved→ TravelWiz ajoute les PAX au manifest du voyageads.completed→ Planner activity passe àcompletedsi liéeads.cancelled→ Planner forecast recalculé
Bug historique fixé : la souscription
paxlog.ads.approvedne matchait pas l'émissionads.approved. Fix : double subscribe (commit11a978c4).
9. Pièges & FAQ#
Pourquoi mon ADS reste en pending_compliance alors que tous les credentials sont valides ?#
Cause fréquente : la compliance matrix du site exige un
credential que tu n'as pas associé au PAX. Aller voir
Compliance → Site → Profile : la liste des credentials marqués
mandatory doit être complète sur le PAX (statut valid + non
expirés à la start_date de l'ADS).
Pourquoi le bouton "Approuver" est grisé même avec paxlog.ads.approve ?#
L'API filtre côté serveur via _can_manage_ads(). Si tu n'as pas
paxlog.ads.read_all, tu ne peux approuver que les ADS où tu es
listé comme valideur explicite ou chef projet (quand
project_id est renseigné). Demande la perm read_all à un
super-admin.
Pourquoi mes PAX externes (TierContact) n'apparaissent pas dans le picker ?#
L'AdsAllowedCompany doit être renseignée. Le picker filtre les
TierContacts dont la société est dans la liste blanche de l'ADS.
Si la liste est vide, toutes les sociétés sont éligibles —
mais l'UI peut être configurée pour exiger au moins une société.
Le PDF de l'ADS retourne 404, c'est normal ?#
Oui, c'est le comportement attendu si le template PDF n'a jamais
été créé pour cette entity. Avant le fix
2726101e
ça retournait 500. Aller dans Settings → Templates → PDF et créer
un template paxlog.ads.confirmation avec le slug par défaut.
Pourquoi mon AVM reste en in_preparation alors que mes tasks sont toutes "done" ?#
Vérifier que toutes les tasks obligatoires sont done.
Les tasks marquées is_optional=true n'entrent pas dans le calcul
de readiness. La transition vers ready est manuelle :
POST /api/v1/pax/avm/{id}/submit.
ads.approved ne déclenche pas la création du manifest TravelWiz#
Bug fixé dans 11a978c4
(audit du 2026-04-29). Si tu vois encore le souci sur une instance
2026-04-30, vérifier que :
- Le voyage TravelWiz couvrant la plage
start_date / end_dateexiste (sinon TravelWiz n'a rien où ajouter le PAX) - Le PAX est bien dans
ads_paxavec statut finalapproved - Le handler
travelwiz_handlers.py:907subscribe bien aux DEUX noms (ads.approvedETpaxlog.ads.approved)
Performance : le tableau ADS rame avec > 5000 lignes#
L'endpoint GET /ads paginate (paxlog/__init__.py:3059) ;
le client fait 50 lignes par défaut. Vérifier pageSize dans
les params de l'URL.
Index existants (app/models/paxlog.py:170-175) :
(entity_id), (entity_id, status), (site_entry_asset_id),
(start_date, end_date), (requester_id), (created_by). Si
filtre custom hors index → EXPLAIN ANALYZE pour vérifier.
J'ai fait une décision PAX par erreur, comment l'annuler ?#
POST /api/v1/pax/ads/{id}/pax/{entry_id}/decision accepte les
allers-retours approved ↔ rejected ↔ pending_check tant que l'ADS
elle-même est dans un statut pending_*. Une fois l'ADS
approved globalement, les décisions individuelles sont gelées.
10. Liens#
Spécification (zone Développeur — auth requise)#
- Spec architecturale PaxLog
- Workflow ADS — niveau métier
- Workflow AVM
- Explication workflow ADS contact externe
- Explication workflow AVM
- State machine PaxLog/AVM/ADS/TravelWiz
Code#
app/modules/paxlog/__init__.py— manifest (47 perms)app/api/routes/modules/paxlog/— 9 fichiers, ~50 endpointsapp/models/paxlog.py— 22 modèles SQLAlchemyapp/services/modules/paxlog_service.py— services métierapp/event_handlers/paxlog_handlers.py— émetteur/consommateur eventsapps/main/src/pages/paxlog/— UI (PaxLogPage + 9 tabs + 8 panels)