{"openapi":"3.1.0","info":{"title":"CareFlow API","description":"API REST de CareFlow (OPSIS Services Aéroportuaires) : prévision de la demande d'assistance PMR, planification des quarts, dispatch des missions et mesure du service à l'aéroport Montréal-Trudeau (YUL). Authentification par JWT (`Authorization: Bearer`) ; rôle déduit du référentiel central des accès. Documentation : https://careflow.opsis-aero.com/developers","termsOfService":"https://careflow.opsis-aero.com/privacy","contact":{"name":"OPSIS Services Aéroportuaires — soutien CareFlow","url":"https://careflow.opsis-aero.com/contact","email":"support.careflow@opsis.com"},"version":"0.1.0"},"servers":[{"url":"https://careflow.opsis-aero.com","description":"Production"}],"paths":{"/api/health":{"get":{"tags":["health"],"summary":"Health","description":"État du service : nom de l'application, environnement et horodatage UTC. Sans authentification.","operationId":"health_api_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}}}}},"/api/me":{"get":{"tags":["me"],"summary":"Me","description":"Identité de l'appelant : identifiant, rôle CareFlow et courriel, déduits du jeton.","operationId":"me_api_me_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MeResponse"}}}}}}},"/api/settings/email-recipients":{"get":{"tags":["courriels"],"summary":"Get Email Recipients","description":"Destinataires configurés des courriels automatiques (planning, alertes).","operationId":"get_email_recipients_api_settings_email_recipients_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailRecipientSettings"}}}}}},"put":{"tags":["courriels"],"summary":"Put Email Recipients","description":"Remplace la liste des destinataires des courriels automatiques (admin).","operationId":"put_email_recipients_api_settings_email_recipients_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailRecipientUpdate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmailRecipientSettings"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/scenarios":{"get":{"tags":["scenarios"],"summary":"List Scenarios","description":"Scénarios de planning disponibles (catalogue chargé depuis les données).","operationId":"list_scenarios_api_scenarios_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScenariosResponse"}}}}}}},"/api/scenarios/{name}/shifts":{"get":{"tags":["scenarios"],"summary":"List Shifts","description":"Quarts d'un scénario, optionnellement restreints à un jour de planning.","operationId":"list_shifts_api_scenarios__name__shifts_get","parameters":[{"name":"name","in":"path","required":true,"schema":{"type":"string","title":"Name"}},{"name":"planning_day","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"JJ par defaut auto-detecte depuis les shifts.","title":"Planning Day"},"description":"JJ par defaut auto-detecte depuis les shifts."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShiftsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/planning/rules":{"get":{"tags":["regles-metier"],"summary":"Get Rules","description":"Règles métier éditables avec leur valeur effective (défaut ou surcharge).","operationId":"get_rules_api_planning_rules_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/RuleView"},"type":"array","title":"Response Get Rules Api Planning Rules Get"}}}}}}},"/api/planning/rules/log":{"get":{"tags":["regles-metier"],"summary":"Get Rules Log","description":"Journal des modifications de règles métier (admin).","operationId":"get_rules_log_api_planning_rules_log_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/RuleLogEntry"},"type":"array","title":"Response Get Rules Log Api Planning Rules Log Get"}}}}}}},"/api/planning/rules/{key}":{"put":{"tags":["regles-metier"],"summary":"Put Rule","description":"Surcharge la valeur d'une règle métier et journalise le changement (admin).","operationId":"put_rule_api_planning_rules__key__put","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","title":"Key"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/RuleUpdate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RuleView"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["regles-metier"],"summary":"Delete Rule","description":"Retire la surcharge d'une règle métier, qui reprend sa valeur par défaut (admin).","operationId":"delete_rule_api_planning_rules__key__delete","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","title":"Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RuleView"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/planning/validate":{"post":{"tags":["planning"],"summary":"Validate Planning","description":"Valide une liste de quarts modifies : recalcule recommandations + remonte les erreurs.\n\nRoadmap §10.4 + §11. Acces : tout compte authentifie (ecriture ouverte en V1, cf. ARCH §8).","operationId":"validate_planning_api_planning_validate_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidateRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ValidateResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/planning/generate":{"post":{"tags":["planning"],"summary":"Generate Planning For Day","description":"Declenche la generation du planning d'un jour (bouton front « Generer »).\n\nLe pipeline (correction Menzies GBM + optimisation) tourne en GitHub Actions, pas\nsur Vercel : l'API relaie a l'Edge Function `generate-planning` et repond 202. Le\nplanning genere apparait dans `recommended_planning` quelques minutes plus tard.\nAcces : tout compte authentifie (ecriture ouverte en V1, cf. ARCH §8).\n\nCas J+1 sans reel : le jour vise n'a que des previsions (`forecast_without_real`) ;\nla correction GBM les sert mais les exclut de l'entrainement (flag `has_real`).","operationId":"generate_planning_for_day_api_planning_generate_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeneratePlanningRequest"}}},"required":true},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GeneratePlanningResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/planning/save":{"post":{"tags":["planning"],"summary":"Save Planning","description":"Sauvegarde une version modifiee + ecrit le log d'audit.\n\nRoadmap §13. Acces : coordonnateur et + (decision produit 2026-07-17).\nLa version est IMMUABLE (ARCH §5.3).","operationId":"save_planning_api_planning_save_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SavePlanningRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanningVersionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/planning/confirm":{"post":{"tags":["planning"],"summary":"Confirm Planning","description":"Confirme un planning propose tel quel : cree un snapshot immuable + retour `valide`.\n\nBoucle de validation. Acces : coordonnateur et + (decision produit 2026-07-17).\nLa version est IMMUABLE (ARCH §5.3).","operationId":"confirm_planning_api_planning_confirm_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConfirmPlanningRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanningVersionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/planning/feedback":{"post":{"tags":["planning"],"summary":"Submit Feedback","description":"Enregistre un retour sans snapshot : commentaire, refus, demande de\ncorrection. Acces : coordonnateur et + (decision produit 2026-07-17).","operationId":"submit_feedback_api_planning_feedback_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupervisorFeedbackRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupervisorFeedbackEntry"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/planning/status":{"get":{"tags":["planning"],"summary":"Get Planning Status","description":"Statut courant + historique des retours superviseur. Acces : superviseur OU lecteur.","operationId":"get_planning_status_api_planning_status_get","parameters":[{"name":"scenario_name","in":"query","required":true,"schema":{"type":"string","description":"Scenario propose concerne.","title":"Scenario Name"},"description":"Scenario propose concerne."},{"name":"planning_day","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Jour cible du planning.","title":"Planning Day"},"description":"Jour cible du planning."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanningStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/planning/generation-info":{"get":{"tags":["planning"],"summary":"Get Generation Info","description":"« Comment ce planning a été produit » : méthode (moteur actuel vs ancre\nsuperviseur), mode SCALED_TEMPLATE_MODE, source du template, score de\nrentabilité, heures retirées, raison du fallback. Lecture seule.\nAccès : superviseur OU lecteur.","operationId":"get_generation_info_api_planning_generation_info_get","parameters":[{"name":"planning_day","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Jour cible du planning.","title":"Planning Day"},"description":"Jour cible du planning."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenerationInfoResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/planning/suggestions":{"get":{"tags":["planning"],"summary":"Get Suggestions","description":"Suggestions d'optimisation du jour + couverture besoin/planifie + reference S-1.\n\nMoteur explicable (cf. docs/planning-suggestions.md) : patterns des corrections\nsuperviseur des memes jours de semaine precedents + besoin calcule vs planifie.\nAUCUNE application automatique : chaque suggestion attend une decision.\nAcces : superviseur OU lecteur (lecture).","operationId":"get_suggestions_api_planning_suggestions_get","parameters":[{"name":"planning_day","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Jour cible du planning.","title":"Planning Day"},"description":"Jour cible du planning."},{"name":"refresh","in":"query","required":false,"schema":{"type":"boolean","description":"Force le recalcul des suggestions.","default":false,"title":"Refresh"},"description":"Force le recalcul des suggestions."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuggestionsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/planning/patterns":{"get":{"tags":["planning"],"summary":"Get Supervisor Patterns","description":"Rapport des patterns recurrents de corrections superviseur pour ce jour.\n\nPour chaque pattern (memes jours de semaine, fenetre adaptative 4-8 semaines) :\ndescription, frequence (« 3 des 4 derniers lundis »), impact (+/- agents),\nconfiance (calibree par les outcomes du meme jour de semaine) et action future\nproposee, bornee par le seuil de rentabilite 1.15. Lecture seule, explicable.\nAcces : superviseur OU lecteur.","operationId":"get_supervisor_patterns_api_planning_patterns_get","parameters":[{"name":"planning_day","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Jour cible du planning.","title":"Planning Day"},"description":"Jour cible du planning."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SupervisorPatternsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/planning/suggestions/{suggestion_id}/review":{"post":{"tags":["planning"],"summary":"Review Suggestion","description":"Decision du superviseur sur une suggestion : accepter / modifier / refuser /\nreporter. Refus et modification exigent un motif structure (apprentissage).\nAcces : tout compte authentifie (ecriture ouverte en V1, cf. ARCH §8).","operationId":"review_suggestion_api_planning_suggestions__suggestion_id__review_post","parameters":[{"name":"suggestion_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Suggestion Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuggestionReviewRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SuggestionEntry"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/planning/{version_id}":{"get":{"tags":["planning"],"summary":"Get Planning","description":"Lecture d'une version sauvegardee. Acces : superviseur OU lecteur.","operationId":"get_planning_api_planning__version_id__get","parameters":[{"name":"version_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Version Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanningVersionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/planning/{version_id}/log":{"get":{"tags":["planning"],"summary":"Get Planning Log","description":"Log d'audit complet d'une version. Acces : superviseur OU lecteur.","operationId":"get_planning_log_api_planning__version_id__log_get","parameters":[{"name":"version_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Version Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/ModificationLogEntry"},"title":"Response Get Planning Log Api Planning  Version Id  Log Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/planning/{version_id}/export":{"post":{"tags":["planning"],"summary":"Export Planning","description":"Genere `recommended_planning_modified.csv` + `planning_modification_log.csv`\ndans Supabase Storage et retourne des URLs signees 1h. Acces : tout compte\nauthentifie (ecriture ouverte en V1, cf. ARCH §8).","operationId":"export_planning_api_planning__version_id__export_post","parameters":[{"name":"version_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Version Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ExportResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/planning/{version_id}/push-evolia":{"post":{"tags":["planning"],"summary":"Push Planning To Evolia","description":"Pousse (ou previsualise) les quarts d'une version sauvegardee vers Evolia (B-EVOLIA-01).\n\nAction explicite post confirm/save. Idempotent via `externalId`. Garde-fous : aucune\necriture si `dry_run=True` (defaut), si `EVOLIA_PUSH_ENABLED=False`, ou si le score du\njour (heures/chaises) depasse le seuil de rentabilite -> 409 (l'apercu reste dispo).\nLe 409 se leve sur une derogation acceptee pour la version, ou sur un forcage\n(`force_motif` + `force_reference`) fait par un approbateur — 403 pour tout autre\ncompte. L'envoi part alors au journal d'audit, SIGNE par l'appelant.\nAcces : coordonnateur et + (decision produit 2026-07-17).","operationId":"push_planning_to_evolia_api_planning__version_id__push_evolia_post","parameters":[{"name":"version_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Version Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/EvoliaPushRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EvoliaPushResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/charge/tableau":{"get":{"tags":["charge"],"summary":"Charge Tableau","description":"Tableau de charge d'un jour d'opération : besoin, capacité et écart par créneau (admin).","operationId":"charge_tableau_api_charge_tableau_get","parameters":[{"name":"day","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Jour d'operation (YYYY-MM-DD)","title":"Day"},"description":"Jour d'operation (YYYY-MM-DD)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Charge Tableau Api Charge Tableau Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/qualite-service/attentes":{"get":{"tags":["qualite-service"],"summary":"Attentes","description":"Attentes des passagers sur une plage de jours d'opération (qualité de service).","operationId":"attentes_api_qualite_service_attentes_get","parameters":[{"name":"debut","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Premier jour d'operation (YYYY-MM-DD)","title":"Debut"},"description":"Premier jour d'operation (YYYY-MM-DD)"},{"name":"fin","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Dernier jour d'operation (YYYY-MM-DD)","title":"Fin"},"description":"Dernier jour d'operation (YYYY-MM-DD)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Attentes Api Qualite Service Attentes Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/coordinator/shifts":{"get":{"tags":["coordinator"],"summary":"Get Shifts","description":"Quarts du jour, tous scenarios confondus (le vrai scenario data-driven du\njour est dans `scenario_name`). Remplace l'ancien appel a un scenario fixe.","operationId":"get_shifts_api_coordinator_shifts_get","parameters":[{"name":"day","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Jour cible au format YYYY-MM-DD (heure Montreal).","title":"Day"},"description":"Jour cible au format YYYY-MM-DD (heure Montreal)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShiftsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/coordinator/chairs":{"get":{"tags":["coordinator"],"summary":"Get Chairs","description":"Chaises du jour (prises en charge prévues et réelles), par vol.","operationId":"get_chairs_api_coordinator_chairs_get","parameters":[{"name":"day","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Jour cible au format YYYY-MM-DD (heure Montreal).","title":"Day"},"description":"Jour cible au format YYYY-MM-DD (heure Montreal)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ChairsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/coordinator/flights":{"get":{"tags":["coordinator"],"summary":"Get Flights","description":"Vols du jour avec leur prévision d'assistance.","operationId":"get_flights_api_coordinator_flights_get","parameters":[{"name":"day","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Jour cible au format YYYY-MM-DD (heure Montreal).","title":"Day"},"description":"Jour cible au format YYYY-MM-DD (heure Montreal)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/coordinator/rentability":{"get":{"tags":["coordinator"],"summary":"Get Rentability","description":"Rentabilite du planning du jour (facture a la chaise) : CA, masse salariale\nchargee, resultat, marge, ratio heures/chaises. Salaires agreges uniquement\n(jamais individuels). Acces : superviseur ou lecteur.","operationId":"get_rentability_api_coordinator_rentability_get","parameters":[{"name":"day","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Jour cible au format YYYY-MM-DD (heure Montreal).","title":"Day"},"description":"Jour cible au format YYYY-MM-DD (heure Montreal)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RentabilityResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/coordinator/rentability/range":{"get":{"tags":["coordinator"],"summary":"Get Rentability Range","description":"Rentabilite mensuelle sur la plage [from, to] (inclusive) : deux scenarios par\njour (planifie / reel Evolia) + totaux. Modele forfaitaire par role (aucune\nimputation). Acces : superviseur ou lecteur.","operationId":"get_rentability_range_api_coordinator_rentability_range_get","parameters":[{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Date de début (inclusive), YYYY-MM-DD heure Montreal.","title":"From"},"description":"Date de début (inclusive), YYYY-MM-DD heure Montreal."},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Date de fin (inclusive), YYYY-MM-DD heure Montreal.","title":"To"},"description":"Date de fin (inclusive), YYYY-MM-DD heure Montreal."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RentabilityRangeResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/coordinator/available-agents":{"get":{"tags":["coordinator"],"summary":"Get Available Agents","description":"Agents candidats pour reaffecter un quart, statut de dispo calcule sur le creneau\n(rh.availability_weekdays + availability_dates + absences). Mode souple : tous listes,\ndisponibles d'abord. Acces : superviseur ou lecteur (lecture).","operationId":"get_available_agents_api_coordinator_available_agents_get","parameters":[{"name":"planning_day","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Jour cible au format YYYY-MM-DD (heure Montreal).","title":"Planning Day"},"description":"Jour cible au format YYYY-MM-DD (heure Montreal)."},{"name":"shift_start_local","in":"query","required":true,"schema":{"type":"string","format":"date-time","description":"Debut du quart (heure locale Montreal, ISO).","title":"Shift Start Local"},"description":"Debut du quart (heure locale Montreal, ISO)."},{"name":"shift_end_local","in":"query","required":true,"schema":{"type":"string","format":"date-time","description":"Fin du quart (heure locale Montreal, ISO).","title":"Shift End Local"},"description":"Fin du quart (heure locale Montreal, ISO)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AvailableAgentsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/coordinator/kpi":{"get":{"tags":["coordinator"],"summary":"Get Kpi","description":"Indicateurs de fiabilité agrégés sur la plage [from, to] (inclusive).","operationId":"get_kpi_api_coordinator_kpi_get","parameters":[{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Date de début (inclusive), YYYY-MM-DD heure Montreal.","title":"From"},"description":"Date de début (inclusive), YYYY-MM-DD heure Montreal."},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Date de fin (inclusive), YYYY-MM-DD heure Montreal.","title":"To"},"description":"Date de fin (inclusive), YYYY-MM-DD heure Montreal."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/KpiResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/coordinator/model-performance":{"get":{"tags":["coordinator"],"summary":"Get Model Performance","description":"Courbe d'amélioration du modèle de correction dans le temps + marqueurs de\nchangement de génération (Menzies → LSTM → GBM). Source : vue\n`v_model_performance_timeline` + table `model_changes` (Supabase Postgres).","operationId":"get_model_performance_api_coordinator_model_performance_get","parameters":[{"name":"scope","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filtre de scope : daily | holdout | monthly. Vide = tous.","default":"daily","title":"Scope"},"description":"Filtre de scope : daily | holdout | monthly. Vide = tous."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ModelPerformanceResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/confirmations":{"get":{"tags":["confirmations"],"summary":"List Confirmations","description":"Suivi des demandes de confirmation de quart (SMS / appel vocal) sur la plage\n[from, to] (date du quart, heure Montreal). Lecture seule de rh.shift_confirmations.","operationId":"list_confirmations_api_confirmations_get","parameters":[{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Date de début (inclusive), YYYY-MM-DD heure Montreal.","title":"From"},"description":"Date de début (inclusive), YYYY-MM-DD heure Montreal."},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Date de fin (inclusive), YYYY-MM-DD heure Montreal.","title":"To"},"description":"Date de fin (inclusive), YYYY-MM-DD heure Montreal."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShiftConfirmationsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/confirmations/offer":{"post":{"tags":["confirmations"],"summary":"Send Replacement Offer","description":"Offre le quart `external_id` a un remplacant (replanification assistee).\n\nRelais vers ORION-RH (`POST /api/shift-confirmations/offer`), qui cree une\ndemande `kind='offer'` : appel « un quart est disponible » puis repli SMS.\nLa reaffectation Evolia reste a faire APRES acceptation (board -> push).\n\nLe geste est journalise (`replacement_offers`) : c'est le SEUL endroit ou\nl'identite du gestionnaire qui choisit le candidat peut etre saisie —\nl'application, elle, est faite par la machine (`decided_by = 'system'`).","operationId":"send_replacement_offer_api_confirmations_offer_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OfferRequestBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OfferRequestResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/confirmations/offer-accepted":{"post":{"tags":["confirmations"],"summary":"Offer Accepted","description":"Rappel ORION-RH : un remplacant vient d'accepter une offre.\n\nApplique le changement d'agent au planning PUIS dans Evolia. Idempotent sur\n`offer_id`. Le `member_code` du corps fait foi — ne JAMAIS le relire depuis\n`rh.shifts`, qui pointe encore le titulaire a cet instant.","operationId":"offer_accepted_api_confirmations_offer_accepted_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/OfferAcceptedBody"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/OfferAcceptedResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/overview":{"get":{"tags":["admin"],"summary":"Get Admin Overview","description":"Synthese de l'activite superviseur (validations, modifications, motifs) sur [from, to].","operationId":"get_admin_overview_api_admin_overview_get","parameters":[{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Date de debut (inclusive), YYYY-MM-DD heure Montreal.","title":"From"},"description":"Date de debut (inclusive), YYYY-MM-DD heure Montreal."},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Date de fin (inclusive), YYYY-MM-DD heure Montreal.","title":"To"},"description":"Date de fin (inclusive), YYYY-MM-DD heure Montreal."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminOverviewResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/modifications":{"get":{"tags":["admin"],"summary":"Get Admin Modifications","description":"Modifications (avant/apres) rattachees a leur jour planifie, page par page.\n\nLe plafond de page reste a 200 : une periode chargee compte des milliers\nd'entrees, que le front parcourt en enchainant les pages plutot qu'en tirant\ntout d'un coup (reponse et DOM tenables). `total` porte le compte complet.","operationId":"get_admin_modifications_api_admin_modifications_get","parameters":[{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Date de debut (inclusive), YYYY-MM-DD heure Montreal.","title":"From"},"description":"Date de debut (inclusive), YYYY-MM-DD heure Montreal."},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Date de fin (inclusive), YYYY-MM-DD heure Montreal.","title":"To"},"description":"Date de fin (inclusive), YYYY-MM-DD heure Montreal."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Taille de page.","default":50,"title":"Limit"},"description":"Taille de page."},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"description":"Entrees a sauter (pagination).","default":0,"title":"Offset"},"description":"Entrees a sauter (pagination)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminModificationsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/planning-adjustments":{"get":{"tags":["admin"],"summary":"Get Planning Adjustments","description":"Ajustements du planning propose (score de stabilite, par jour) sur [from, to].","operationId":"get_planning_adjustments_api_admin_planning_adjustments_get","parameters":[{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Date de debut (inclusive), YYYY-MM-DD heure Montreal.","title":"From"},"description":"Date de debut (inclusive), YYYY-MM-DD heure Montreal."},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Date de fin (inclusive), YYYY-MM-DD heure Montreal.","title":"To"},"description":"Date de fin (inclusive), YYYY-MM-DD heure Montreal."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlanningAdjustmentsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/admin/feedback":{"get":{"tags":["admin"],"summary":"Get Admin Feedback","description":"Retours superviseur recents (validation / modif / refus / commentaire).","operationId":"get_admin_feedback_api_admin_feedback_get","parameters":[{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Date de debut (inclusive), YYYY-MM-DD heure Montreal.","title":"From"},"description":"Date de debut (inclusive), YYYY-MM-DD heure Montreal."},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Date de fin (inclusive), YYYY-MM-DD heure Montreal.","title":"To"},"description":"Date de fin (inclusive), YYYY-MM-DD heure Montreal."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Nombre max d'entrees.","default":50,"title":"Limit"},"description":"Nombre max d'entrees."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdminFeedbackResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/agents/me/flights":{"get":{"tags":["agents"],"summary":"My Shift Flights","description":"« Vols du quart » : vols YUL du quart de l'agent, ou TOUS les vols du jour pour les\nrôles de supervision (coordinateur/superviseur, planificateur, direction, admin).","operationId":"my_shift_flights_api_agents_me_flights_get","parameters":[{"name":"day","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Jour cible YYYY-MM-DD (defaut : aujourd'hui).","title":"Day"},"description":"Jour cible YYYY-MM-DD (defaut : aujourd'hui)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentShiftFlightsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/agents/planned":{"get":{"tags":["agents"],"summary":"Planned Agents Of Day","description":"« Agents planifies » : qui travaille ce jour-la (perimetre Menzies), de quelle\nheure a quelle heure, d'apres le planning pousse dans Evolia.","operationId":"planned_agents_of_day_api_agents_planned_get","parameters":[{"name":"day","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Jour cible YYYY-MM-DD (defaut : aujourd'hui).","title":"Day"},"description":"Jour cible YYYY-MM-DD (defaut : aujourd'hui)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PlannedAgentsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/agents/on-duty":{"get":{"tags":["agents"],"summary":"Agents On Duty","description":"Quarts du jour rattaches au **profil CareFlow** — carte agents temps reel.\n\nCousin de `/agents/planned` (meme source : les quarts Evolia de `rh.shifts`), mais\nindexe par `careflow.profiles.id` : c'est la seule cle qui permette a la carte de croiser\nun quart avec une position declaree (`agent_current_locations`) ou une mission.\n\nOn renvoie les blocs du quart, PAS une liste deja filtree sur l'heure : la coupure « en\nquart maintenant » est recalculee cote client chaque minute (sinon un agent dont le quart\nfinit a 15:00 resterait affiche jusqu'au prochain poll).","operationId":"agents_on_duty_api_agents_on_duty_get","parameters":[{"name":"day","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Jour cible YYYY-MM-DD (defaut : aujourd'hui).","title":"Day"},"description":"Jour cible YYYY-MM-DD (defaut : aujourd'hui)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentsOnDutyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/agents/overrides":{"get":{"tags":["agents"],"summary":"List Overrides","description":"Liste des surcharges (temporaires + exclusions).","operationId":"list_overrides_api_agents_overrides_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentOverridesResponse"}}}}}},"post":{"tags":["agents"],"summary":"Upsert Override","description":"Cree ou met a jour une surcharge (cle = `member_code`).\n\nEcriture reservee aux roles qui atteignent la page `/equipe` qui la porte : la\nposture V1 « ouvert a tout compte authentifie » laissait un agent PMR modifier le\nroster. La LECTURE (`GET /overrides`) reste ouverte, elle alimente l'affichage.","operationId":"upsert_override_api_agents_overrides_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentOverrideUpsert"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AgentOverrideOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/agents/employees":{"get":{"tags":["agents"],"summary":"Search Employees","description":"Recherche un employe `rh.employees` pour declarer une surcharge de roster.\n\nSert la saisie des RENFORTS et des DEPARTS dans /equipe : sans elle il fallait taper\nun code Evolia de memoire, et une faute de frappe creait une ligne qui ne correspond a\npersonne — sans erreur visible, puisque tous les resolveurs ignorent un code inconnu.","operationId":"search_employees_api_agents_employees_get","parameters":[{"name":"q","in":"query","required":false,"schema":{"type":"string","description":"Code, prenom ou nom (2 caracteres minimum).","default":"","title":"Q"},"description":"Code, prenom ou nom (2 caracteres minimum)."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":25,"minimum":1,"default":15,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/EmployeeSearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/agents/roster":{"get":{"tags":["agents"],"summary":"Roster Activity","description":"Roster planifiable + dernier pointage PMR de chaque agent (inactifs signales).","operationId":"roster_activity_api_agents_roster_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/RosterActivityResponse"}}}}}}},"/api/agents/overrides/{member_code}":{"delete":{"tags":["agents"],"summary":"Delete Override","description":"Supprime une surcharge ; 404 si elle n'existe pas.\n\nMeme garde que le POST, et pour une raison de plus : supprimer un temporaire\ndetache `careflow.profiles.planning_member_code` par SQL brut, hors du chemin RLS\nou la RPC equivalente exige `is_staff()`.","operationId":"delete_override_api_agents_overrides__member_code__delete","parameters":[{"name":"member_code","in":"path","required":true,"schema":{"type":"string","title":"Member Code"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/agents/wage-rates":{"get":{"tags":["agents"],"summary":"Get Wage Rates","description":"Taux horaires forfaitaires en vigueur (masse salariale de la rentabilite).","operationId":"get_wage_rates_api_agents_wage_rates_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WageRatesOut"}}}}}},"put":{"tags":["agents"],"summary":"Update Wage Rates","description":"Met a jour les taux. Prise en compte immediate par `/coordinator/rentability`.","operationId":"update_wage_rates_api_agents_wage_rates_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WageRatesIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/WageRatesOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/agents/shift-adjustments":{"get":{"tags":["agents"],"summary":"List Shift Adjustments","description":"Modifications de fin de quart. Sans `day`, les 14 derniers jours d'operation.","operationId":"list_shift_adjustments_api_agents_shift_adjustments_get","parameters":[{"name":"day","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Jour d'operation YYYY-MM-DD.","title":"Day"},"description":"Jour d'operation YYYY-MM-DD."},{"name":"pending","in":"query","required":false,"schema":{"type":"boolean","description":"Ne garder que les non reportees.","default":false,"title":"Pending"},"description":"Ne garder que les non reportees."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShiftAdjustmentsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["agents"],"summary":"Create Shift Adjustment","description":"Saisie superviseur. Cle = (jour, agent) : ressaisir corrige et remet a traiter.","operationId":"create_shift_adjustment_api_agents_shift_adjustments_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShiftAdjustmentCreate"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShiftAdjustmentOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/agents/shift-adjustments/{adj_id}/reported":{"patch":{"tags":["agents"],"summary":"Mark Shift Adjustment Reported","description":"Report dans Evolia acte par le coordonnateur. Idempotent.","operationId":"mark_shift_adjustment_reported_api_agents_shift_adjustments__adj_id__reported_patch","parameters":[{"name":"adj_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Adj Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShiftAdjustmentOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/agents/shift-adjustments/{adj_id}/rejected":{"patch":{"tags":["agents"],"summary":"Reject Shift Adjustment","description":"Refus du coordonnateur, motif obligatoire. Idempotent.\n\n409 (et non 422) quand la ligne a deja ete validee : la demande est recevable, c'est\nl'etat de la ressource qui s'y oppose — et le distinguer permet au front de le dire.","operationId":"reject_shift_adjustment_api_agents_shift_adjustments__adj_id__rejected_patch","parameters":[{"name":"adj_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Adj Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShiftAdjustmentReject"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ShiftAdjustmentOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/agents/shift-adjustments/{adj_id}":{"delete":{"tags":["agents"],"summary":"Delete Shift Adjustment","description":"Annule une saisie erronee. Refuse si le coordonnateur a deja reporte.","operationId":"delete_shift_adjustment_api_agents_shift_adjustments__adj_id__delete","parameters":[{"name":"adj_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Adj Id"}}],"responses":{"204":{"description":"Successful Response"},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/flights/search":{"get":{"tags":["flights"],"summary":"Search Flights","description":"Candidats de `flights.flight_status` pour des criteres partiels, scores.\n\nAucun critere n'est obligatoire ; sans aucun critere exploitable la reponse est\nsimplement `match_status='none'` — jamais d'erreur bloquante pour l'agent.","operationId":"search_flights_api_flights_search_get","parameters":[{"name":"flight_number","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Numero lu, meme partiel : « TK0035 », « TK 35 », « K 0035 », « 0035 ».","title":"Flight Number"},"description":"Numero lu, meme partiel : « TK0035 », « TK 35 », « K 0035 », « 0035 »."},{"name":"airline","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Code IATA (« TK ») ou nom imprime (« TURKISH AIRLINES »).","title":"Airline"},"description":"Code IATA (« TK ») ou nom imprime (« TURKISH AIRLINES »)."},{"name":"day","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Date du vol YYYY-MM-DD (defaut : aujourd'hui a YUL).","title":"Day"},"description":"Date du vol YYYY-MM-DD (defaut : aujourd'hui a YUL)."},{"name":"origin","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Origine : code IATA ou ville (« ISTANBUL »).","title":"Origin"},"description":"Origine : code IATA ou ville (« ISTANBUL »)."},{"name":"destination","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Destination : code IATA ou ville (« MONTREAL »).","title":"Destination"},"description":"Destination : code IATA ou ville (« MONTREAL »)."},{"name":"direction","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"DEPARTURE ou ARRIVAL (cote YUL), si connu.","title":"Direction"},"description":"DEPARTURE ou ARRIVAL (cote YUL), si connu."},{"name":"time_hint","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Heure imprimee HH:MM (indice de tri — peut etre l'embarquement).","title":"Time Hint"},"description":"Heure imprimee HH:MM (indice de tri — peut etre l'embarquement)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/FlightSearchResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/flights/scan-image-retention":{"get":{"tags":["flights"],"summary":"Get Scan Image Retention","description":"Retention des preuves image du scan (jours). 0 = stockage desactive.","operationId":"get_scan_image_retention_api_flights_scan_image_retention_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScanImageRetentionOut"}}}}}},"put":{"tags":["flights"],"summary":"Put Scan Image Retention","description":"Regle la retention (0..90 jours). Prise en compte aux prochains scans.","operationId":"put_scan_image_retention_api_flights_scan_image_retention_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScanImageRetentionIn"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScanImageRetentionOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/forecast/upload":{"post":{"tags":["forecast"],"summary":"Upload Forecast","description":"Relaie le fichier (base64) a l'Edge Function pour ingestion du jour choisi.\n\nAcces : tout compte authentifie (ecriture ouverte en V1, cf. ARCH §8). Ne parse\npas l'Excel (contrainte bundle Vercel) : le traitement tourne en GitHub Actions.\nLe fichier arrive en base64 dans le JSON (pas de multipart) pour ne pas alourdir\nle bundle Vercel.","operationId":"upload_forecast_api_forecast_upload_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForecastUploadRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForecastUploadResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/forecast/reingest-latest":{"post":{"tags":["forecast"],"summary":"Reingest Latest","description":"Re-ingere `planning_day` depuis le DERNIER fichier Menzies deja en storage, puis\nlaisse le workflow enchainer la generation. Filet pour la prevision arrivee en avance\n(le fichier etant mensuel, n'importe quel jour du mois peut etre re-extrait). Acces :\ntout compte authentifie (ecriture ouverte en V1).","operationId":"reingest_latest_api_forecast_reingest_latest_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReingestLatestRequest"}}},"required":true},"responses":{"202":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReingestLatestResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/forecast/status":{"get":{"tags":["forecast"],"summary":"Forecast Status","description":"Compte les vols enregistres dans `forecast_demand` pour le jour donne.\n\nLe schema cible est celui configure cote API (`PIR_OPS_DB_SCHEMA`, = `planning_prod`\nen production, ou ecrit l'ingestion). Acces : superviseur OU lecteur.","operationId":"forecast_status_api_forecast_status_get","parameters":[{"name":"planning_date","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Jour des previsions a verifier.","title":"Planning Date"},"description":"Jour des previsions a verifier."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForecastStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/forecast/senders":{"get":{"tags":["forecast"],"summary":"Forecast Senders","description":"Liste les expediteurs Menzies deja vus, le plus recent en tete.\n\nAlimente la liste du formulaire d'import manuel : une adresse saisie a la main\nentre en base a l'ingestion, donc elle rejoint cette liste d'elle-meme au\nchargement suivant. Le regroupement est INSENSIBLE A LA CASSE (les adresses du\nflux courriel sont en casse mixte) ; la forme retenue est celle du dernier usage.\n\nAcces : `require_forecast_upload_access` -- les roles ops qui ont acces a la\npage de televersement (`/forecast/upload`, coord ET superviseur, plus\nchef_operations/direction/ADM), pas tout compte authentifie -- ce sont des\nadresses courriel d'employes Menzies (un tiers), pas des donnees\nd'exploitation anonymes.","operationId":"forecast_senders_api_forecast_senders_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ForecastSendersResponse"}}}}}}},"/api/forecast/schedule":{"post":{"tags":["forecast"],"summary":"Upload Schedule","description":"Relaie le planning mensuel (PRM Schedule) a l'Edge Function (`kind=schedule`).\n\nIngere TOUTES les dates du fichier dans `flight_schedule` (replace-by-month),\nsans comptes PMR. Acces : tout compte authentifie (ecriture ouverte en V1).","operationId":"upload_schedule_api_forecast_schedule_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleUploadRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleUploadResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/forecast/schedule-status":{"get":{"tags":["forecast"],"summary":"Schedule Status","description":"Compte les vols planifies dans `flight_schedule` pour le mois donne.","operationId":"schedule_status_api_forecast_schedule_status_get","parameters":[{"name":"month","in":"query","required":true,"schema":{"type":"string","description":"Mois a verifier (format YYYY-MM).","title":"Month"},"description":"Mois a verifier (format YYYY-MM)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ScheduleStatusResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/forecast/month-coverage":{"get":{"tags":["forecast"],"summary":"Month Coverage","description":"Couverture d'un mois : presence du schedule (total mois) + previsions par jour.\n\nUne requete agregee sur `flight_schedule` (total + presence) et une sur\n`forecast_demand` (`GROUP BY flight_date`). Alimente le calendrier de la page upload.","operationId":"month_coverage_api_forecast_month_coverage_get","parameters":[{"name":"month","in":"query","required":true,"schema":{"type":"string","description":"Mois a couvrir (format YYYY-MM).","title":"Month"},"description":"Mois a couvrir (format YYYY-MM)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/MonthCoverageResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/replanning/proposals":{"get":{"tags":["replanning"],"summary":"Get Replanning Proposals","description":"Propositions de replanification ouvertes sur [from, to] (declined/no_response).\n\nCalcul a la demande, borne par l'horizon des reglages. `source_ok=False` si rh est\ninjoignable ; `auto_replanning_enabled=false` si la fonctionnalite est desactivee\n(liste vide, aucun scan rh).","operationId":"get_replanning_proposals_api_replanning_proposals_get","parameters":[{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Date de début (inclusive), YYYY-MM-DD Montreal.","title":"From"},"description":"Date de début (inclusive), YYYY-MM-DD Montreal."},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Date de fin (inclusive), YYYY-MM-DD Montreal.","title":"To"},"description":"Date de fin (inclusive), YYYY-MM-DD Montreal."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplanningProposalsResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/replanning/proposals/apply":{"post":{"tags":["replanning"],"summary":"Apply Replanning Proposal","description":"Trace la decision `applied` (idempotent) et renvoie le payload de navigation\nboard (jour + quart + agent suggere). N'ecrit jamais rh directement.","operationId":"apply_replanning_proposal_api_replanning_proposals_apply_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReplanningProposalRef"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplyReplanningResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/replanning/proposals/dismiss":{"post":{"tags":["replanning"],"summary":"Dismiss Replanning Proposal","description":"Trace la decision `dismissed` (idempotent) avec motif optionnel.","operationId":"dismiss_replanning_proposal_api_replanning_proposals_dismiss_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DismissReplanningRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DismissReplanningResult"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/settings/auto-replanning":{"get":{"tags":["replanning"],"summary":"Get Auto Replanning Settings","description":"Reglages courants de la replanification (defauts si jamais configures).","operationId":"get_auto_replanning_settings_api_settings_auto_replanning_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutoReplanningSettings"}}}}}},"put":{"tags":["replanning"],"summary":"Set Auto Replanning Settings","description":"Active/desactive + regle la replanification. Ecriture ouverte (V1).","operationId":"set_auto_replanning_settings_api_settings_auto_replanning_put","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutoReplanningSettings"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AutoReplanningSettings"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/assistance-control":{"get":{"tags":["assistance-control"],"summary":"Assistance Control List","description":"Vols du jour a YUL : PMR prevus vs PMR reellement pris en charge, ecart et statut.","operationId":"assistance_control_list_api_assistance_control_get","parameters":[{"name":"day","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Jour cible YYYY-MM-DD (defaut : aujourd'hui).","title":"Day"},"description":"Jour cible YYYY-MM-DD (defaut : aujourd'hui)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssistanceControlResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/assistance-control/detail":{"get":{"tags":["assistance-control"],"summary":"Assistance Control Detail","description":"Detail d'un vol : chaque PMR pris en charge (scan ou saisie), et par quel agent.","operationId":"assistance_control_detail_api_assistance_control_detail_get","parameters":[{"name":"flight_code","in":"query","required":true,"schema":{"type":"string","description":"Numero de vol (ex. TU202).","title":"Flight Code"},"description":"Numero de vol (ex. TU202)."},{"name":"direction","in":"query","required":true,"schema":{"type":"string","description":"DEPARTURE ou ARRIVAL.","title":"Direction"},"description":"DEPARTURE ou ARRIVAL."},{"name":"day","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Jour cible YYYY-MM-DD (defaut : aujourd'hui).","title":"Day"},"description":"Jour cible YYYY-MM-DD (defaut : aujourd'hui)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AssistanceControlDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/studio/campaign":{"get":{"tags":["studio"],"summary":"Get Current Campaign","description":"Campagne ouverte la plus récente : journées, avancement, déblocages.","operationId":"get_current_campaign_api_studio_campaign_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StudioCampaignResponse"}}}}}}},"/api/studio/days/{day_id}":{"get":{"tags":["studio"],"summary":"Get Day Detail","description":"Détail d'une journée proposée — payload PUBLIC uniquement (anonymisé).","operationId":"get_day_detail_api_studio_days__day_id__get","parameters":[{"name":"day_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Day Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StudioDayDetail"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/studio/days/{day_id}/volume":{"post":{"tags":["studio"],"summary":"Decide Volume","description":"Niveau 1 (Sylvain) : volume d'heures / rentabilité de la journée.","operationId":"decide_volume_api_studio_days__day_id__volume_post","parameters":[{"name":"day_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Day Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StudioDecisionRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StudioDecisionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/studio/days/{day_id}/forme":{"post":{"tags":["studio"],"summary":"Decide Forme","description":"Niveau 2 (Damien) : répartition des plages — verrouillé tant que le\nvolume n'est pas validé, borné par le volume validé.","operationId":"decide_forme_api_studio_days__day_id__forme_post","parameters":[{"name":"day_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Day Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StudioDecisionRequest"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/StudioDecisionResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/analyse/bias-rolling":{"get":{"tags":["analyse"],"summary":"Get Bias Rolling","description":"Biais de prévision sur 7 jours glissants (écran Analyse, lecture seule).","operationId":"get_bias_rolling_api_analyse_bias_rolling_get","parameters":[{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"description":"Dernier jour de fenetre (defaut : J-1, heure Montreal).","title":"To"},"description":"Dernier jour de fenetre (defaut : J-1, heure Montreal)."},{"name":"days","in":"query","required":false,"schema":{"type":"integer","maximum":90,"minimum":1,"description":"Nombre de fenetres glissantes.","default":30,"title":"Days"},"description":"Nombre de fenetres glissantes."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BiasRollingResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/anomalies/scan":{"get":{"tags":["anomalies"],"summary":"Scan","description":"Balaye les prises en charge pour détecter les anomalies de saisie et alimenter la file de triage.","operationId":"scan_api_anomalies_scan_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Scan Api Anomalies Scan Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["anomalies"],"summary":"Scan","description":"Balaye les prises en charge pour détecter les anomalies de saisie et alimenter la file de triage.","operationId":"scan_api_anomalies_scan_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Scan Api Anomalies Scan Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/reconciliation":{"get":{"tags":["reconciliation"],"summary":"Lire","description":"Écarts Evolia ↔ board d'un jour d'exploitation, par famille, avec les gestes proposés.","operationId":"lire_api_reconciliation_get","parameters":[{"name":"day","in":"query","required":true,"schema":{"type":"string","format":"date","description":"Jour d'exploitation (America/Toronto).","title":"Day"},"description":"Jour d'exploitation (America/Toronto)."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ReconciliationResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/reconciliation/apply":{"post":{"tags":["reconciliation"],"summary":"Appliquer","description":"Applique les gestes de réconciliation choisis sur le board ; n'écrit jamais vers Evolia.","operationId":"appliquer_api_reconciliation_apply_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplyRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ApplyResponse"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/reconciliation/scan":{"get":{"tags":["reconciliation"],"summary":"Scan","description":"Balaye J et J+1 et envoie le courriel s'il reste un ecart. N'ECRIT RIEN.\n\nRoute montee en GET **et** POST, meme raison que `api/routers/anomalies.py::scan` :\nun Cron Job Vercel invoque toujours son `path` en GET (type `Cron` de la Build\nOutput API, sans champ methode) -- un montage POST seul renverrait 405 tous les\njours, en silence. Le bouton manuel de l'ecran continuerait d'appeler en POST.\n\n`date.today()` est en UTC : entre 20 h et minuit heure de Montreal elle renvoie deja\nle lendemain, ce qui decalerait la fenetre d'un jour -- d'ou `store.BUSINESS_TZ`,\njamais `date.today()`.\n\nReutilise `autoriser_cron_ou_planner` (extraite dans `api/auth/cron_or_planner.py`\ndepuis `api/routers/anomalies.py`) plutot que `_exiger_auth_configuree` +\n`require_planner` : ce dernier duo exige une session Supabase et refuserait donc le\ncron, dont le bearer est le secret partage (pas un JWT). Meme principe d'ordre que\nles deux routes ci-dessus : l'autorisation est declaree en PREMIER parametre, avant\n`db`, pour se resoudre avant toute connexion BDD.","operationId":"scan_api_reconciliation_scan_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Scan Api Reconciliation Scan Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["reconciliation"],"summary":"Scan","description":"Balaye J et J+1 et envoie le courriel s'il reste un ecart. N'ECRIT RIEN.\n\nRoute montee en GET **et** POST, meme raison que `api/routers/anomalies.py::scan` :\nun Cron Job Vercel invoque toujours son `path` en GET (type `Cron` de la Build\nOutput API, sans champ methode) -- un montage POST seul renverrait 405 tous les\njours, en silence. Le bouton manuel de l'ecran continuerait d'appeler en POST.\n\n`date.today()` est en UTC : entre 20 h et minuit heure de Montreal elle renvoie deja\nle lendemain, ce qui decalerait la fenetre d'un jour -- d'ou `store.BUSINESS_TZ`,\njamais `date.today()`.\n\nReutilise `autoriser_cron_ou_planner` (extraite dans `api/auth/cron_or_planner.py`\ndepuis `api/routers/anomalies.py`) plutot que `_exiger_auth_configuree` +\n`require_planner` : ce dernier duo exige une session Supabase et refuserait donc le\ncron, dont le bearer est le secret partage (pas un JWT). Meme principe d'ordre que\nles deux routes ci-dessus : l'autorisation est declaree en PREMIER parametre, avant\n`db`, pour se resoudre avant toute connexion BDD.","operationId":"scan_api_reconciliation_scan_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"title":"Response Scan Api Reconciliation Scan Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/derogations/contexte":{"get":{"tags":["derogations"],"summary":"Contexte","description":"Contexte des dérogations pour l'appelant : est-il approbateur, qui sont les approbateurs.","operationId":"contexte_api_derogations_contexte_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DerogationContexte"}}}}}}},"/api/derogations":{"get":{"tags":["derogations"],"summary":"Lister","description":"Dérogations existantes, filtrables par version de planning et par statut.","operationId":"lister_api_derogations_get","parameters":[{"name":"version_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Version Id"}},{"name":"statut","in":"query","required":false,"schema":{"anyOf":[{"enum":["pending","accepted","refused"],"type":"string"},{"type":"null"}],"title":"Statut"}},{"name":"limite","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"default":50,"title":"Limite"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"type":"array","items":{"$ref":"#/components/schemas/DerogationOut"},"title":"Response Lister Api Derogations Get"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["derogations"],"summary":"Demander","description":"Crée une demande de dérogation sur une version de planning.","operationId":"demander_api_derogations_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DerogationCreate"}}}},"responses":{"201":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DerogationCreeeOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/derogations/{derogation_id}/decision":{"post":{"tags":["derogations"],"summary":"Decider","description":"Approuve ou refuse une dérogation (approbateur uniquement).","operationId":"decider_api_derogations__derogation_id__decision_post","parameters":[{"name":"derogation_id","in":"path","required":true,"schema":{"type":"string","format":"uuid","title":"Derogation Id"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DerogationDecision"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DerogationOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/api/derogations/alerte-quotidienne":{"get":{"tags":["derogations"],"summary":"Alerte Quotidienne","description":"Appelée chaque matin par la tâche planifiée Vercel (`CRON_SECRET`).","operationId":"alerte_quotidienne_api_derogations_alerte_quotidienne_get","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlerteQuotidienneOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["derogations"],"summary":"Alerte Quotidienne","description":"Appelée chaque matin par la tâche planifiée Vercel (`CRON_SECRET`).","operationId":"alerte_quotidienne_api_derogations_alerte_quotidienne_post","parameters":[{"name":"authorization","in":"header","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Authorization"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AlerteQuotidienneOut"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"ActionCount":{"properties":{"action":{"type":"string","title":"Action","description":"added | edited | deleted | dragged | resized | reassigned."},"count":{"type":"integer","title":"Count"}},"type":"object","required":["action","count"],"title":"ActionCount","description":"Nombre de modifications pour un type d'action du log d'audit."},"AdminFeedbackResponse":{"properties":{"from":{"type":"string","format":"date","title":"From"},"to":{"type":"string","format":"date","title":"To"},"source_ok":{"type":"boolean","title":"Source Ok","default":true},"total":{"type":"integer","title":"Total","description":"Nombre total de retours sur la plage (avant limite)."},"items":{"items":{"$ref":"#/components/schemas/SupervisorFeedbackEntry"},"type":"array","title":"Items"}},"type":"object","required":["from","to","total"],"title":"AdminFeedbackResponse"},"AdminModification":{"properties":{"id":{"type":"integer","title":"Id"},"timestamp":{"type":"string","format":"date-time","title":"Timestamp"},"user_id":{"type":"string","format":"uuid","title":"User Id"},"action":{"type":"string","title":"Action"},"shift_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Shift Id"},"candidate_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Candidate Id"},"old_agent_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Old Agent Code"},"old_agent_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Old Agent Name"},"new_agent_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"New Agent Code"},"new_agent_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"New Agent Name"},"old_start":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Old Start"},"new_start":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"New Start"},"old_end":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Old End"},"new_end":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"New End"},"old_duration_hours":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Old Duration Hours"},"new_duration_hours":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"New Duration Hours"},"supervisor_note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Supervisor Note"},"reason_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason Code"},"validation_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Validation Status"},"warnings":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Warnings"},"planning_version_id":{"type":"string","format":"uuid","title":"Planning Version Id"},"planning_day":{"type":"string","format":"date","title":"Planning Day"},"base_scenario":{"type":"string","title":"Base Scenario"}},"type":"object","required":["id","timestamp","user_id","action","shift_id","candidate_id","old_start","new_start","old_end","new_end","old_duration_hours","new_duration_hours","supervisor_note","validation_status","warnings","planning_version_id","planning_day","base_scenario"],"title":"AdminModification","description":"Une entree du log de modifications, enrichie de son rattachement planning.\n\nHerite de `ModificationLogEntry` (avant/apres : agent, horaire, duree, note)\net ajoute le jour planifie + le scenario de base + l'id de version, pour\nafficher \"proposition modele -> correction superviseur\" dans le dashboard."},"AdminModificationsResponse":{"properties":{"from":{"type":"string","format":"date","title":"From"},"to":{"type":"string","format":"date","title":"To"},"source_ok":{"type":"boolean","title":"Source Ok","default":true},"total":{"type":"integer","title":"Total","description":"Nombre total de modifications sur la plage (avant limite)."},"items":{"items":{"$ref":"#/components/schemas/AdminModification"},"type":"array","title":"Items"}},"type":"object","required":["from","to","total"],"title":"AdminModificationsResponse"},"AdminOverviewResponse":{"properties":{"from":{"type":"string","format":"date","title":"From"},"to":{"type":"string","format":"date","title":"To"},"source_ok":{"type":"boolean","title":"Source Ok","description":"False si la BDD est injoignable : la page affiche un etat degrade.","default":true},"days_with_activity":{"type":"integer","title":"Days With Activity","description":"Nombre de jours planifies distincts ayant au moins un retour superviseur."},"versions_total":{"type":"integer","title":"Versions Total","description":"Nombre de versions sauvegardees (planning_versions) sur la plage."},"feedback_total":{"type":"integer","title":"Feedback Total","description":"Nombre total de retours superviseur sur la plage."},"validated_count":{"type":"integer","title":"Validated Count","description":"Retours de validation (confirm + validate_with_remarks)."},"modified_count":{"type":"integer","title":"Modified Count","description":"Retours de modification (modify)."},"rejected_count":{"type":"integer","title":"Rejected Count","description":"Refus (reject)."},"correction_requested_count":{"type":"integer","title":"Correction Requested Count","description":"Demandes de correction (request_correction)."},"comments_count":{"type":"integer","title":"Comments Count","description":"Commentaires seuls (comment)."},"modifications_total":{"type":"integer","title":"Modifications Total","description":"Nombre total d'entrees du log de modifications."},"agents_reassigned_count":{"type":"integer","title":"Agents Reassigned Count","description":"Modifications de type 'reassigned'."},"schedule_changed_count":{"type":"integer","title":"Schedule Changed Count","description":"Modifications d'horaire (edited + dragged + resized)."},"modifications_by_action":{"items":{"$ref":"#/components/schemas/ActionCount"},"type":"array","title":"Modifications By Action"},"top_reasons":{"items":{"$ref":"#/components/schemas/LabelCount"},"type":"array","title":"Top Reasons","description":"Motifs de correction les plus frequents (notes de modif + motifs de retour)."}},"type":"object","required":["from","to","days_with_activity","versions_total","feedback_total","validated_count","modified_count","rejected_count","correction_requested_count","comments_count","modifications_total","agents_reassigned_count","schedule_changed_count"],"title":"AdminOverviewResponse","description":"Synthese de l'activite superviseur sur la plage [from, to] (par jour planifie)."},"AgentFlightCard":{"properties":{"id":{"type":"string","title":"Id","description":"Identite de la carte : UID `flights.flight_status` pour un vol live, ou id synthetique `sched:{code}:{date}:{mouvement}` pour un vol previsionnel (`source='scheduled'`, vue supervision, lecture seule)."},"flight_number":{"type":"string","title":"Flight Number"},"direction":{"type":"string","enum":["DEPARTURE","ARRIVAL"],"title":"Direction"},"airline_iata":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Airline Iata"},"airline_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Airline Name"},"origin":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Origin","description":"IATA origine (YUL si depart)."},"destination":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Destination","description":"IATA destination (YUL si arrivee)."},"gate":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Gate","description":"Porte cote YUL (depart -> gate_origin, arrivee -> gate_destination)."},"scheduled_time":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Scheduled Time","description":"Heure prevue cote YUL (timestamptz)."},"estimated_time":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Estimated Time","description":"Heure estimee cote YUL."},"actual_time":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Actual Time","description":"Heure reelle cote YUL."},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"delay_min":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Delay Min"},"pmr_total":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pmr Total","description":"PMR attendus (demande PIR-OPS) si disponible."},"pmr_taken":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Pmr Taken","description":"PMR pris en charge si disponible (sinon null)."},"source":{"type":"string","enum":["live","scheduled"],"title":"Source","description":"`live` = flight_status (ORION) ; `scheduled` = prévision Menzies non confirmée.","default":"live"}},"type":"object","required":["id","flight_number","direction"],"title":"AgentFlightCard","description":"Une carte vol affichee a l'agent (cote YUL uniquement)."},"AgentOnDuty":{"properties":{"profile_id":{"type":"string","title":"Profile Id"},"member_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Member Code"},"full_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Full Name"},"source":{"type":"string","enum":["evolia","planning"],"title":"Source"},"start":{"type":"string","format":"date-time","title":"Start"},"end":{"type":"string","format":"date-time","title":"End"},"blocks":{"items":{"$ref":"#/components/schemas/ShiftBlock"},"type":"array","title":"Blocks"}},"type":"object","required":["profile_id","member_code","full_name","source","start","end","blocks"],"title":"AgentOnDuty","description":"Quart du jour d'un agent, rattache a son profil CareFlow."},"AgentOverrideOut":{"properties":{"member_code":{"type":"string","title":"Member Code","description":"Code agent : reel Evolia (exclusions, renforts) ou synthetique TEMP-xxx (temporaires)."},"kind":{"type":"string","enum":["temporary","excluded","renfort"],"title":"Kind"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name","description":"Nom affiche (requis pour un temporaire, absent de rh)."},"hourly_wage":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Hourly Wage","description":"Salaire horaire CAD (defaut 19 applique si NULL)."},"availability":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Availability","description":"Disponibilite au format Evolia `days` (7 objets jour). None = dispo continue."},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At"},"updated_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated By"},"evolia_member_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Evolia Member Code","description":"Vrai code Evolia confirme d'un temporaire onboarde (alias)."},"suggested_evolia_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Suggested Evolia Code","description":"Code Evolia propose (match de nom) ; a valider par l'admin."},"suggested_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Suggested Name","description":"Nom de l'employe propose (info d'affichage)."}},"type":"object","required":["member_code","kind"],"title":"AgentOverrideOut","description":"Une ligne de surcharge renvoyee au front."},"AgentOverrideUpsert":{"properties":{"member_code":{"type":"string","minLength":1,"title":"Member Code"},"kind":{"type":"string","enum":["temporary","excluded","renfort"],"title":"Kind"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"hourly_wage":{"anyOf":[{"type":"number","minimum":0.0},{"type":"null"}],"title":"Hourly Wage"},"availability":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Availability"},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note"},"evolia_member_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Evolia Member Code","description":"Rattacher (vrai code Evolia) ; None = detacher."}},"type":"object","required":["member_code","kind"],"title":"AgentOverrideUpsert","description":"Creation ou mise a jour d'une ligne de surcharge (cle = `member_code`)."},"AgentOverridesResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/AgentOverrideOut"},"type":"array","title":"Items"},"source_ok":{"type":"boolean","title":"Source Ok","description":"False si la table est injoignable (droits/connexion).","default":true}},"type":"object","title":"AgentOverridesResponse","description":"Liste plate des surcharges (le front regroupe par `kind`)."},"AgentShiftFlightsResponse":{"properties":{"date":{"type":"string","title":"Date","description":"Jour cible (YYYY-MM-DD, heure locale aeroport)."},"has_shift":{"type":"boolean","title":"Has Shift","description":"False -> « Aucun quart trouve pour ce jour » (mode `shift` uniquement)."},"scope":{"type":"string","enum":["shift","all"],"title":"Scope","description":"`shift` = vols du quart de l'agent ; `all` = tous les vols du jour (roles de supervision).","default":"shift"},"shift":{"anyOf":[{"$ref":"#/components/schemas/api__schemas__agent_flights__ShiftWindow"},{"type":"null"}]},"member_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Member Code","description":"Code membre (rh.employees.code) resolu, debug."},"flights":{"items":{"$ref":"#/components/schemas/AgentFlightCard"},"type":"array","title":"Flights"}},"type":"object","required":["date","has_shift"],"title":"AgentShiftFlightsResponse","description":"Reponse de `GET /agents/me/flights` : quart + vols filtres sur ce quart."},"AgentsOnDutyResponse":{"properties":{"date":{"type":"string","title":"Date"},"server_time":{"type":"string","format":"date-time","title":"Server Time"},"agents":{"items":{"$ref":"#/components/schemas/AgentOnDuty"},"type":"array","title":"Agents"}},"type":"object","required":["date","server_time","agents"],"title":"AgentsOnDutyResponse"},"AlerteQuotidienneOut":{"properties":{"jour":{"type":"string","format":"date","title":"Jour"},"marge_pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Marge Pct"},"envoye":{"type":"boolean","title":"Envoye"},"raison":{"type":"string","title":"Raison"}},"type":"object","required":["jour","marge_pct","envoye","raison"],"title":"AlerteQuotidienneOut"},"ApplyReplanningResult":{"properties":{"shift_confirmation_id":{"type":"string","title":"Shift Confirmation Id"},"status":{"type":"string","const":"applied","title":"Status","default":"applied"},"already_decided":{"type":"boolean","title":"Already Decided","description":"True si une decision existait deja (concurrence) : rien de neuf ecrit.","default":false},"planning_day":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Planning Day","description":"Jour du quart (navigation board)."},"external_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"External Id"},"suggested_member_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Suggested Member Code"},"scheduled_shift_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scheduled Shift Id","description":"Derive de external_id : quart a pre-selectionner sur le board."},"version_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Version Id","description":"Version d'origine derivee de external_id."}},"type":"object","required":["shift_confirmation_id"],"title":"ApplyReplanningResult","description":"Retour de `apply` : la proposition est tracee `applied`, le front enchaine\nen amenant le coordinateur sur le board du jour, quart + agent pre-selectionnes."},"ApplyRequest":{"properties":{"jour":{"type":"string","format":"date","title":"Jour"},"gestes":{"items":{"$ref":"#/components/schemas/GesteIn"},"type":"array","title":"Gestes"}},"type":"object","required":["jour","gestes"],"title":"ApplyRequest"},"ApplyResponse":{"properties":{"planning_version_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Planning Version Id"},"alignes":{"type":"integer","title":"Alignes"},"integres":{"type":"integer","title":"Integres"},"retires":{"type":"integer","title":"Retires"},"ignores":{"items":{"$ref":"#/components/schemas/GesteIgnoreOut"},"type":"array","title":"Ignores"}},"type":"object","required":["planning_version_id","alignes","integres","retires","ignores"],"title":"ApplyResponse"},"AssistanceCareRow":{"properties":{"mission_id":{"type":"string","title":"Mission Id"},"passenger_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Passenger Name"},"pnr":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Pnr"},"seat":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Seat"},"mission_status":{"type":"string","title":"Mission Status"},"created_from":{"type":"string","title":"Created From","description":"scan | walkin | manual | planning | handover"},"scanned_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Scanned At"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"agent_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agent Name"}},"type":"object","required":["mission_id","mission_status","created_from","created_at"],"title":"AssistanceCareRow","description":"Une prise en charge reelle (= une mission) dans le detail d'un vol."},"AssistanceControlDetail":{"properties":{"flight_code":{"type":"string","title":"Flight Code"},"direction":{"type":"string","enum":["DEPARTURE","ARRIVAL"],"title":"Direction"},"date":{"type":"string","format":"date","title":"Date"},"expected_count":{"type":"integer","title":"Expected Count","description":"PMR attendus (chaise corrigee arrondie)."},"model_count":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Model Count","description":"Valeur exacte du modele avant arrondi."},"announced_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Announced Count","description":"Annonce brute de la compagnie (chaise Menzies)."},"taken_count":{"type":"integer","title":"Taken Count"},"cares":{"items":{"$ref":"#/components/schemas/AssistanceCareRow"},"type":"array","title":"Cares"},"agents":{"items":{"type":"string"},"type":"array","title":"Agents"},"decision":{"anyOf":[{"$ref":"#/components/schemas/AssistanceDecision"},{"type":"null"}]}},"type":"object","required":["flight_code","direction","date","expected_count","taken_count"],"title":"AssistanceControlDetail","description":"Reponse de `GET /assistance-control/detail` : le detail d'un vol."},"AssistanceControlResponse":{"properties":{"date":{"type":"string","format":"date","title":"Date"},"generated_at":{"type":"string","format":"date-time","title":"Generated At"},"thresholds":{"additionalProperties":{"type":"integer"},"type":"object","title":"Thresholds","description":"Seuils appliques (delai avant alerte, ecart signalable, ecart important)."},"summary":{"$ref":"#/components/schemas/AssistanceControlSummary"},"flights":{"items":{"$ref":"#/components/schemas/AssistanceControlRow"},"type":"array","title":"Flights"}},"type":"object","required":["date","generated_at","thresholds","summary"],"title":"AssistanceControlResponse","description":"Reponse de `GET /assistance-control`."},"AssistanceControlRow":{"properties":{"flight_id":{"type":"string","title":"Flight Id"},"flight_code":{"type":"string","title":"Flight Code","description":"Code normalise (TU202) — cle metier du vol."},"direction":{"type":"string","enum":["DEPARTURE","ARRIVAL"],"title":"Direction"},"airline_iata":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Airline Iata"},"airline_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Airline Name"},"origin":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Origin"},"destination":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Destination"},"gate":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Gate"},"scheduled_time":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Scheduled Time"},"estimated_time":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Estimated Time"},"actual_time":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Actual Time"},"flight_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Flight Status"},"expected_count":{"type":"integer","title":"Expected Count","description":"PMR attendus : la « chaise corrigee » du planning, ARRONDIE pour ce vol (`round(corrected_total_assistance)`, comme `V1KpiBand.tsx`). TU203 -> 9, TU202 -> 8. C'est le chiffre compare aux prises en charge. 0 si aucune prevision."},"model_count":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Model Count","description":"Valeur exacte du modele avant arrondi (`corrected_total_assistance`, ex. 8.73)."},"announced_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Announced Count","description":"« Chaise Menzies » : annonce brute de la compagnie (`forecast_total_assistance`, ex. 10 sur TU203). Information — non comparee aux prises en charge."},"has_forecast":{"type":"boolean","title":"Has Forecast","description":"False -> vol sans prevision (tout reel y est un walk-up)."},"taken_count":{"type":"integer","title":"Taken Count","description":"PMR reellement pris en charge (missions non annulees)."},"scanned_count":{"type":"integer","title":"Scanned Count","description":"Parmi eux, ceux issus d'un scan de carte."},"manual_count":{"type":"integer","title":"Manual Count","description":"Parmi eux, ceux saisis manuellement (walk-in)."},"difference":{"type":"integer","title":"Difference","description":"reel - prevu (negatif = il en manque)."},"computed_status":{"type":"string","enum":["waiting","in_progress","ok","minor_gap","to_verify","walkup"],"title":"Computed Status"},"high_gap":{"type":"boolean","title":"High Gap","description":"Ecart >= HIGH_GAP_THRESHOLD : a traiter en priorite."},"first_care_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"First Care At","description":"Premiere prise en charge (demarre le compteur de controle)."},"decision":{"anyOf":[{"$ref":"#/components/schemas/AssistanceDecision"},{"type":"null"}]}},"type":"object","required":["flight_id","flight_code","direction","expected_count","has_forecast","taken_count","scanned_count","manual_count","difference","computed_status","high_gap"],"title":"AssistanceControlRow","description":"Un vol surveille : prevu, reel, ecart, statut, decision eventuelle."},"AssistanceControlSummary":{"properties":{"monitored_flights":{"type":"integer","title":"Monitored Flights"},"total_flights":{"type":"integer","title":"Total Flights"},"expected_total":{"type":"integer","title":"Expected Total","description":"PMR prevus du jour = ARRONDI DE LA SOMME exacte des chaises corrigees (93.7985 -> 94), comme la tuile du planning. Ce n'est PAS la somme des lignes affichees (99, chacune arrondie a son tour) : additionner 32 arrondis n'est pas arrondir leur somme. Ecart assume — le planning se comporte a l'identique."},"taken_total":{"type":"integer","title":"Taken Total"},"to_verify_count":{"type":"integer","title":"To Verify Count"},"walkup_count":{"type":"integer","title":"Walkup Count"}},"type":"object","required":["monitored_flights","total_flights","expected_total","taken_total","to_verify_count","walkup_count"],"title":"AssistanceControlSummary","description":"KPI d'en-tete."},"AssistanceDecision":{"properties":{"control_status":{"type":"string","enum":["confirmed","scan_missing","not_present","forecast_incorrect"],"title":"Control Status"},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note"},"expected_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Expected Count","description":"PMR prevus au moment de la decision (photo)."},"counted_count":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Counted Count","description":"PMR reels au moment de la decision (photo)."},"decided_at":{"type":"string","format":"date-time","title":"Decided At"},"decided_by_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Decided By Name"}},"type":"object","required":["control_status","decided_at"],"title":"AssistanceDecision","description":"Decision du superviseur sur un vol (la seule chose qu'on persiste)."},"AutoReplanningSettings":{"properties":{"enabled":{"type":"boolean","title":"Enabled","description":"Interrupteur d'activation de la fonctionnalite.","default":false},"window_hours":{"type":"integer","maximum":168.0,"minimum":1.0,"title":"Window Hours","description":"Horizon (heures) des quarts a surveiller a partir de maintenant.","default":48},"reliability_weight":{"type":"number","maximum":1.0,"minimum":0.0,"title":"Reliability Weight","description":"Poids indicatif de la fiabilite dans le departage des candidats.","default":0.3},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At"}},"type":"object","title":"AutoReplanningSettings","description":"Reglages de la replanification semi-automatique (cle `auto_replanning`)."},"AvailableAgent":{"properties":{"code":{"type":"string","title":"Code","description":"Code agent Evolia (rh.employees.code)."},"name":{"type":"string","title":"Name","description":"Nom complet (Prenom Nom), ou le code si non resolu."},"role":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Role","description":"Type de contrat / role (type_code)."},"is_available":{"type":"boolean","title":"Is Available","description":"Disponible sur le creneau selon rh.*."},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason","description":"Motif d'(in)disponibilite (En conge, Hors plage habituelle, ...)."},"available_windows":{"items":{"type":"string"},"type":"array","title":"Available Windows","description":"Creneaux de disponibilite de l'agent sur le jour demande (format 'HH:MM → HH:MM' ; 'Journee' pour une exception de date disponible). Vide si indisponible / sans donnee."},"reliability_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Reliability Score","description":"Note de fiabilite [0,1] lue cote rh (contrat `rh.v_employee_reliability`). None si le score est absent pour l'agent ou si la source est indisponible."},"reliability_sample":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Reliability Sample","description":"Taille d'echantillon du score (nb d'evenements) ; pondere la confiance."},"allow_replacements_outside":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Allow Replacements Outside","description":"Case Evolia « recevoir des remplacements hors disponibilites ». True => l'agent reste proposable meme quand ses heures declarees ne couvrent pas le creneau. None = aucune regle de dispo enregistree (question jamais posee)."}},"type":"object","required":["code","name","is_available"],"title":"AvailableAgent","description":"Un agent candidat a l'affectation d'un quart, avec son statut de disponibilite."},"AvailableAgentsResponse":{"properties":{"planning_day":{"type":"string","format":"date","title":"Planning Day"},"agents":{"items":{"$ref":"#/components/schemas/AvailableAgent"},"type":"array","title":"Agents"},"source_ok":{"type":"boolean","title":"Source Ok","description":"False si la source rh est inaccessible (droits/RLS) : afficher une erreur.","default":true},"reliability_source_ok":{"type":"boolean","title":"Reliability Source Ok","description":"True si le contrat de fiabilite `rh.v_employee_reliability` a pu etre lu. False (defaut) => aucun score : tri par disponibilite seule, badge grise.","default":false}},"type":"object","required":["planning_day"],"title":"AvailableAgentsResponse","description":"Liste des agents pour le selecteur de reaffectation (mode souple : tous listes,\ndisponibles d'abord). `source_ok=False` si les tables rh sont injoignables."},"BiasAgregat":{"properties":{"vols_prevus":{"type":"integer","title":"Vols Prevus"},"chaises_corrigees":{"type":"number","title":"Chaises Corrigees"},"chaises_reelles":{"type":"number","title":"Chaises Reelles"},"biais_dimensionnement":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Biais Dimensionnement","description":"(corrige sur TOUS les vols prevus - reel) / reel, en %. None quand la fenetre n'est pas exploitable. C'est ce ratio qui declenche l'alerte."},"biais_couverture":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Biais Couverture","description":"Meme ratio, restreint aux vols apparies. Jamais alerte."},"ratio_annonce":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Ratio Annonce","description":"Ratio de discrimination de l'ANNONCE Menzies : chaises moyennes sur les vols servis / chaises moyennes sur les vols vides. Sans dimension. Mesure l'ALLOCATION, la ou les deux biais ci-dessus mesurent des VOLUMES. None quand la fenetre n'est pas exploitable, ou qu'un des deux groupes est vide."},"ratio_final":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Ratio Final","description":"Meme ratio en SORTIE de chaine. Cible (suivi #49) : ratio_final >= ratio_annonce — la chaine preserve au moins la separation que son entree lui donne. Jamais alerte."},"statut":{"type":"string","title":"Statut","description":"ok | hors_bande | non_concluant"}},"type":"object","required":["vols_prevus","chaises_corrigees","chaises_reelles","statut"],"title":"BiasAgregat","description":"Un perimetre (global ou compagnie) sur une fenetre de 7 jours."},"BiasCompagnie":{"properties":{"vols_prevus":{"type":"integer","title":"Vols Prevus"},"chaises_corrigees":{"type":"number","title":"Chaises Corrigees"},"chaises_reelles":{"type":"number","title":"Chaises Reelles"},"biais_dimensionnement":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Biais Dimensionnement","description":"(corrige sur TOUS les vols prevus - reel) / reel, en %. None quand la fenetre n'est pas exploitable. C'est ce ratio qui declenche l'alerte."},"biais_couverture":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Biais Couverture","description":"Meme ratio, restreint aux vols apparies. Jamais alerte."},"ratio_annonce":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Ratio Annonce","description":"Ratio de discrimination de l'ANNONCE Menzies : chaises moyennes sur les vols servis / chaises moyennes sur les vols vides. Sans dimension. Mesure l'ALLOCATION, la ou les deux biais ci-dessus mesurent des VOLUMES. None quand la fenetre n'est pas exploitable, ou qu'un des deux groupes est vide."},"ratio_final":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Ratio Final","description":"Meme ratio en SORTIE de chaine. Cible (suivi #49) : ratio_final >= ratio_annonce — la chaine preserve au moins la separation que son entree lui donne. Jamais alerte."},"statut":{"type":"string","title":"Statut","description":"ok | hors_bande | non_concluant"},"compagnie":{"type":"string","title":"Compagnie"},"alertable":{"type":"boolean","title":"Alertable","description":"False pour AUTRES, dont la moyenne ne designe aucune compagnie."}},"type":"object","required":["vols_prevus","chaises_corrigees","chaises_reelles","statut","compagnie","alertable"],"title":"BiasCompagnie"},"BiasPoint":{"properties":{"jour_fin":{"type":"string","format":"date","title":"Jour Fin"},"jours_exploitables":{"type":"integer","title":"Jours Exploitables","description":"Jours de la fenetre ou un vol sans reel est comptable comme un zero (jour clos et couverture d'appariement suffisante). Sous 7, la fenetre ne conclut pas."},"global":{"$ref":"#/components/schemas/BiasAgregat"},"compagnies":{"items":{"$ref":"#/components/schemas/BiasCompagnie"},"type":"array","title":"Compagnies"}},"type":"object","required":["jour_fin","jours_exploitables","global","compagnies"],"title":"BiasPoint"},"BiasRollingResponse":{"properties":{"seuils":{"$ref":"#/components/schemas/BiasSeuils"},"points":{"items":{"$ref":"#/components/schemas/BiasPoint"},"type":"array","title":"Points"}},"type":"object","required":["seuils","points"],"title":"BiasRollingResponse"},"BiasSeuils":{"properties":{"bande_min_pct":{"type":"number","title":"Bande Min Pct"},"bande_max_pct":{"type":"number","title":"Bande Max Pct"},"plancher_chaises_reelles":{"type":"number","title":"Plancher Chaises Reelles"},"seuil_chaises_compagnie_majeure":{"type":"integer","title":"Seuil Chaises Compagnie Majeure"},"fenetre_jours":{"type":"integer","title":"Fenetre Jours"}},"type":"object","required":["bande_min_pct","bande_max_pct","plancher_chaises_reelles","seuil_chaises_compagnie_majeure","fenetre_jours"],"title":"BiasSeuils","description":"Seuils publies pour que l'ecran n'en reinvente aucun.\n\nLe corridor du biais a DEUX bornes depuis le recalibrage d'avant-bascule\n(action 2.4) : une bande symetrique ne saurait pas dire « on derive de la\ncible » le jour ou la cible n'est plus zero. L'ecran les lit, il n'en\nreinvente toujours aucune."},"ChairsResponse":{"properties":{"day":{"type":"string","format":"date","title":"Day"},"menzies_chairs":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Menzies Chairs","description":"Total chaises prevues par Menzies pour la journee. None si schema flight non branche."},"corrected_chairs":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Corrected Chairs","description":"Total chaises apres correction. None si schema flight non branche."},"real_chairs":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Real Chairs","description":"Total chaises reellement constatees pour la journee. None si pas de reel pour ce jour."}},"type":"object","required":["day"],"title":"ChairsResponse"},"ConfirmPlanningRequest":{"properties":{"scenario_name":{"type":"string","title":"Scenario Name","description":"Scenario propose a confirmer."},"planning_day":{"type":"string","format":"date","title":"Planning Day","description":"Jour cible du planning."},"comment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comment","description":"Remarque optionnelle ('valider avec remarques')."}},"type":"object","required":["scenario_name","planning_day"],"title":"ConfirmPlanningRequest","description":"Confirmation d'un planning propose tel quel (sans modification).\n\nCree un snapshot immuable (copie de la proposition) + un retour `valide`."},"CoverageBucket":{"properties":{"bucket_start_local":{"type":"string","format":"date-time","title":"Bucket Start Local"},"required_staff":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Required Staff","description":"Besoin calcule (None si non disponible pour ce bucket)."},"planned_staff":{"type":"integer","title":"Planned Staff"},"coverage_ratio":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Coverage Ratio","description":"planifie / requis (None si besoin nul ou inconnu)."},"flight_count":{"type":"integer","title":"Flight Count","default":0},"corrected_total_assistance":{"type":"number","title":"Corrected Total Assistance","default":0.0}},"type":"object","required":["bucket_start_local","planned_staff"],"title":"CoverageBucket","description":"Couverture d'un bucket de 30 min : besoin vs planifie."},"CoverageGap":{"properties":{"start_min":{"type":"integer","title":"Start Min"},"end_min":{"type":"integer","title":"End Min"},"agents":{"type":"integer","title":"Agents"}},"type":"object","required":["start_min","end_min","agents"],"title":"CoverageGap","description":"Creux de couverture : plage ou personne (ou presque) n'est planifie."},"DeletedShift":{"properties":{"id":{"type":"string","title":"Id"},"reason_code":{"type":"string","enum":["sous_effectif","surcapacite","absence","pic_vols","preparation_vol","hausse_pmr","baisse_pmr","competence_manquante","changement_gate","changement_operationnel","correction_planning","preference_recurrente","donnees_incorrectes","besoin_terrain_non_detecte","contrainte_rh","besoin_de_reserve","evenement_special","substitution_renfort","autre"],"title":"Reason Code"},"supervisor_note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Supervisor Note"}},"type":"object","required":["id","reason_code"],"title":"DeletedShift","description":"Suppression motivee d'un quart (M.3) : le motif nourrit l'apprentissage."},"DerogationContexte":{"properties":{"est_approbateur":{"type":"boolean","title":"Est Approbateur","description":"L'appelant peut accepter, refuser et forcer (liste direction + rôle)."},"seuil":{"type":"number","title":"Seuil"},"motifs":{"items":{"$ref":"#/components/schemas/MotifOut"},"type":"array","title":"Motifs"},"reference_min":{"type":"integer","title":"Reference Min"},"reference_max":{"type":"integer","title":"Reference Max"}},"type":"object","required":["est_approbateur","seuil","motifs","reference_min","reference_max"],"title":"DerogationContexte","description":"Ce que l'écran doit savoir avant d'afficher le panneau de dérogation."},"DerogationCreate":{"properties":{"planning_version_id":{"type":"string","format":"uuid","title":"Planning Version Id"},"motif":{"type":"string","enum":["client_request","extra_flights","direction_operation","training","irregularities","uncovered_slot"],"title":"Motif"},"reference":{"type":"string","maxLength":300,"minLength":3,"title":"Reference"}},"additionalProperties":false,"type":"object","required":["planning_version_id","motif","reference"],"title":"DerogationCreate"},"DerogationCreeeOut":{"properties":{"derogation":{"$ref":"#/components/schemas/DerogationOut"},"courriel_envoye":{"type":"boolean","title":"Courriel Envoye","description":"False si le courriel aux approbateurs n'a pas pu partir (la demande existe)."}},"type":"object","required":["derogation","courriel_envoye"],"title":"DerogationCreeeOut"},"DerogationDecision":{"properties":{"decision":{"type":"string","enum":["accepter","refuser"],"title":"Decision"},"note":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Note"}},"additionalProperties":false,"type":"object","required":["decision"],"title":"DerogationDecision"},"DerogationOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"planning_version_id":{"type":"string","format":"uuid","title":"Planning Version Id"},"planning_day":{"type":"string","format":"date","title":"Planning Day"},"score":{"type":"number","title":"Score"},"threshold":{"type":"number","title":"Threshold"},"motif":{"type":"string","title":"Motif"},"libelle_motif":{"type":"string","title":"Libelle Motif"},"reference":{"type":"string","title":"Reference"},"status":{"type":"string","enum":["pending","accepted","refused"],"title":"Status"},"requested_by_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Requested By Email"},"requested_at":{"type":"string","format":"date-time","title":"Requested At"},"decided_by_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Decided By Email"},"decided_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Decided At"},"decision_note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Decision Note"}},"type":"object","required":["id","planning_version_id","planning_day","score","threshold","motif","libelle_motif","reference","status","requested_by_email","requested_at","decided_by_email","decided_at","decision_note"],"title":"DerogationOut"},"DismissReplanningRequest":{"properties":{"shift_confirmation_id":{"type":"string","title":"Shift Confirmation Id"},"rh_shift_id":{"type":"string","title":"Rh Shift Id"},"external_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"External Id"},"trigger_status":{"type":"string","enum":["declined","no_response"],"title":"Trigger Status"},"declined_member_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Declined Member Code"},"declined_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Declined Name"},"shift_start_local":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Shift Start Local"},"shift_end_local":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Shift End Local"},"location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Location"},"suggested_member_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Suggested Member Code"},"suggested_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Suggested Name"},"reliability_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Reliability Score"},"reliability_source_ok":{"type":"boolean","title":"Reliability Source Ok","default":false},"dismiss_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Dismiss Reason","description":"Motif d'abandon (optionnel, trace d'audit)."}},"type":"object","required":["shift_confirmation_id","rh_shift_id","trigger_status"],"title":"DismissReplanningRequest"},"DismissReplanningResult":{"properties":{"shift_confirmation_id":{"type":"string","title":"Shift Confirmation Id"},"status":{"type":"string","const":"dismissed","title":"Status","default":"dismissed"},"already_decided":{"type":"boolean","title":"Already Decided","default":false}},"type":"object","required":["shift_confirmation_id"],"title":"DismissReplanningResult"},"EcartOut":{"properties":{"famille":{"type":"string","title":"Famille"},"debut":{"type":"string","format":"date-time","title":"Debut"},"fin":{"type":"string","format":"date-time","title":"Fin"},"scheduled_shift_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scheduled Shift Id"},"evolia_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Evolia Id"},"agent_board_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agent Board Code"},"agent_board_nom":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agent Board Nom"},"agent_evolia_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agent Evolia Code"},"agent_evolia_nom":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agent Evolia Nom"},"geste":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Geste"},"coche_par_defaut":{"type":"boolean","title":"Coche Par Defaut","default":false},"conflit_debut":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Conflit Debut","description":"Debut du quart avec lequel cette ligne est en CONFLIT : le quart du board qui la couvre (`evolia_quart_deja_couvert`), l'autre quart Evolia qui la couvre (`evolia_quart_couvert_par_un_autre_evolia`), le quart Evolia frere qu'un `evolia_inconnu_du_board` non coche a supplante, ou -- en SUBSTITUTION -- l'autre ligne du meme creneau. Dire « deja couvert » sans dire par quoi ne se verifie pas, et une case decochee sans motif se recoche d'un clic."},"conflit_fin":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Conflit Fin"},"conflit_type":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Conflit Type","description":"NATURE du quart nomme par `conflit_debut`/`conflit_fin` (`rules.CONFLIT_*`) : `quart_du_board`, `quart_evolia` ou `substitution`. Portee explicitement plutot que deduite de la famille : `evolia_inconnu_du_board` porte DEUX natures de conflit (un frere Evolia evince, ou le quart du board qu'il remplacerait), et la deduction annoncerait la mauvaise sur l'une des deux."}},"type":"object","required":["famille","debut","fin"],"title":"EcartOut"},"EmailRecipientHistory":{"properties":{"support":{"items":{"type":"string"},"type":"array","maxItems":50,"minItems":1,"title":"Support"},"super_users":{"items":{"type":"string"},"type":"array","maxItems":50,"minItems":1,"title":"Super Users"},"direction":{"items":{"type":"string"},"type":"array","maxItems":10,"title":"Direction"},"rentability_daily_copy":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":20},{"type":"null"}],"title":"Rentability Daily Copy"},"version":{"type":"integer","title":"Version"},"changed_at":{"type":"string","format":"date-time","title":"Changed At"},"actor_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Actor Email"}},"additionalProperties":false,"type":"object","required":["support","super_users","version","changed_at","actor_email"],"title":"EmailRecipientHistory"},"EmailRecipientSettings":{"properties":{"support":{"items":{"type":"string"},"type":"array","maxItems":50,"minItems":1,"title":"Support"},"super_users":{"items":{"type":"string"},"type":"array","maxItems":50,"minItems":1,"title":"Super Users"},"direction":{"items":{"type":"string"},"type":"array","maxItems":10,"minItems":1,"title":"Direction"},"rentability_daily_copy":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":20},{"type":"null"}],"title":"Rentability Daily Copy"},"version":{"type":"integer","title":"Version"},"updated_at":{"type":"string","format":"date-time","title":"Updated At"},"history":{"items":{"$ref":"#/components/schemas/EmailRecipientHistory"},"type":"array","title":"History"}},"additionalProperties":false,"type":"object","required":["support","super_users","direction","version","updated_at"],"title":"EmailRecipientSettings"},"EmailRecipientUpdate":{"properties":{"support":{"items":{"type":"string"},"type":"array","maxItems":50,"minItems":1,"title":"Support"},"super_users":{"items":{"type":"string"},"type":"array","maxItems":50,"minItems":1,"title":"Super Users"},"direction":{"items":{"type":"string"},"type":"array","maxItems":10,"minItems":1,"title":"Direction"},"rentability_daily_copy":{"anyOf":[{"items":{"type":"string"},"type":"array","maxItems":20},{"type":"null"}],"title":"Rentability Daily Copy"},"version":{"type":"integer","exclusiveMinimum":0.0,"title":"Version"}},"additionalProperties":false,"type":"object","required":["support","super_users","direction","version"],"title":"EmailRecipientUpdate"},"EmployeeSearchItem":{"properties":{"code":{"type":"string","title":"Code","description":"Code Evolia (`rh.employees.code`) — la cle a saisir."},"name":{"type":"string","title":"Name","description":"« Prenom Nom », ou le code si le nom est absent."},"type_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Type Code","description":"Type de contrat Evolia (FULL_TIME, OCCASIONAL, ...)."},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","description":"Statut Evolia. Toujours 'active' en pratique (Evolia ne signale jamais un depart) : informatif, ne filtre rien."},"last_position_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Position Code","description":"Poste du dernier pointage (033/045/098/...)."},"last_position_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Position Label","description":"Libelle du poste, ou None si le code est inconnu."},"in_contract":{"type":"boolean","title":"In Contract","description":"La personne travaille SOUS LE CONTRAT Menzies a la date du jour (`contract_membership`). True => elle ne peut pas etre declaree RENFORT : un renfort est prete par un autre contrat. False couvre aussi « aucune trace recente » : la declaration reste possible.","default":false},"last_team":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Last Team","description":"Equipe du dernier pointage (« ADM - Menzies », « ADM - Chariots »...)."},"last_worked_on":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Last Worked On","description":"Date du dernier pointage. None = aucun pointage connu."},"declared_as":{"anyOf":[{"type":"string","enum":["temporary","excluded","renfort"]},{"type":"null"}],"title":"Declared As","description":"Surcharge deja enregistree pour ce code (temporary/excluded/renfort), sinon None. Le front s'en sert pour empecher un doublon."}},"type":"object","required":["code","name"],"title":"EmployeeSearchItem","description":"Un employe `rh.employees` propose a la saisie d'une surcharge de roster.\n\nLe trio (dernier poste, equipe, date du dernier pointage) n'est pas decoratif : c'est\nce qui permet de trancher entre deux homonymes et de confirmer qu'on vise bien le bon\ncoordonnateur avant de le declarer renfort."},"EmployeeSearchResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/EmployeeSearchItem"},"type":"array","title":"Items"},"query":{"type":"string","title":"Query","description":"Requete effectivement appliquee (trim).","default":""},"source_ok":{"type":"boolean","title":"Source Ok","description":"False si `rh` est injoignable : afficher une erreur, pas une liste vide.","default":true}},"type":"object","title":"EmployeeSearchResponse","description":"Resultats de la recherche d'employes (saisie des surcharges de roster)."},"EvoliaPushItem":{"properties":{"scheduled_shift_id":{"type":"string","title":"Scheduled Shift Id"},"external_id":{"type":"string","title":"External Id","description":"Cle d'idempotence cote Evolia."},"member_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Member Code","description":"Code agent Evolia ; None si quart ouvert."},"member_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Member Name","description":"Nom complet de l'agent (rh.employees) ; None si quart ouvert ou code non resolu."},"open_shift":{"type":"boolean","title":"Open Shift","description":"True si pousse comme quart ouvert (agent non assigne)."},"starts_at":{"type":"string","title":"Starts At","description":"Debut ISO-8601 (fuseau Est)."},"ends_at":{"type":"string","title":"Ends At"},"action":{"type":"string","enum":["dry_run","created","updated","unchanged","error","skipped","duplicate","replaced","status_aligned","reassigned"],"title":"Action"},"evolia_uid":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Evolia Uid","description":"UID Evolia du quart cree/mis a jour."},"error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error"},"collision":{"type":"string","enum":["none","duplicate","overlap"],"title":"Collision","description":"Résultat de la détection contre les quarts manuels Evolia du même agent.","default":"none"},"existing_event_uid":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Existing Event Uid","description":"UID du quart Evolia en conflit (doublon/chevauchement)."},"existing_starts_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Existing Starts At","description":"Début du quart Evolia en conflit (ISO brut)."},"existing_ends_at":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Existing Ends At","description":"Fin du quart Evolia en conflit."},"existing_is_draft":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Existing Is Draft","description":"Statut brouillon (True) / publié (False) du quart Evolia en conflit ; None sinon."},"manual_action":{"anyOf":[{"type":"string","enum":["publish_state","unassign","reassign","position"]},{"type":"null"}],"title":"Manual Action","description":"Opération que l'API Evolia ne permet PAS d'appliquer automatiquement (limite connue) : le quart a bien été poussé, mais ce point est À FAIRE MANUELLEMENT dans Evolia. None si aucune action manuelle requise."},"confirmation_too_late":{"type":"boolean","title":"Confirmation Too Late","description":"La confirmation de ce quart n'a plus le temps d'arriver : il a déjà commencé, ou il commence dans moins de `CONFIRMATION_LEAD_MARGIN` (5 min). ORION-RH n'envoie que si `starts_at > now()` — verrou en base, pas un délai ; la marge couvre en plus la resynchronisation et le cycle de dispatch. L'agent doit être prévenu de vive voix.","default":false}},"type":"object","required":["scheduled_shift_id","external_id","open_shift","starts_at","ends_at","action"],"title":"EvoliaPushItem"},"EvoliaPushRequest":{"properties":{"publish":{"type":"boolean","title":"Publish","description":"True = quarts publies dans Evolia ; False = crees en brouillon (isDraft).","default":false},"dry_run":{"type":"boolean","title":"Dry Run","description":"True = apercu seul (aucun appel d'ecriture Evolia). Defaut sur True (sur).","default":true},"overlap_decisions":{"additionalProperties":{"type":"string","enum":["replace","ignore"]},"type":"object","title":"Overlap Decisions","description":"Décision par quart en chevauchement (clé = scheduled_shift_id). 'replace' = ajuster le quart Evolia existant aux heures CareFlow ; 'ignore' (défaut) = ne rien écrire."},"force_motif":{"anyOf":[{"type":"string","enum":["client_request","extra_flights","direction_operation","training","irregularities","uncovered_slot"]},{"type":"null"}],"title":"Force Motif","description":"Forcage par un APPROBATEUR de la direction, quand le score depasse le seuil : motif de la liste fermee. Refuse (403) pour tout autre compte, qui doit demander une derogation. Ignore si le score respecte le seuil ou si une derogation acceptee couvre deja la version."},"force_reference":{"anyOf":[{"type":"string","maxLength":300,"minLength":3},{"type":"null"}],"title":"Force Reference","description":"Reference verifiable du forcage (obligatoire avec `force_motif`)."},"status_decisions":{"additionalProperties":{"type":"string","enum":["align","leave"]},"type":"object","title":"Status Decisions","description":"Décision de statut par quart en écart (clé = scheduled_shift_id). 'align' = aligner isDraft sur l'action (Publier/Brouillon) ; 'leave' (défaut) = ne pas toucher au statut existant. Résolu côté front (réglage global + overrides + garde-fou anti-dépublication)."}},"type":"object","title":"EvoliaPushRequest"},"EvoliaPushResult":{"properties":{"planning_version_id":{"type":"string","format":"uuid","title":"Planning Version Id"},"dry_run":{"type":"boolean","title":"Dry Run"},"push_enabled":{"type":"boolean","title":"Push Enabled","description":"Etat du garde-fou EVOLIA_PUSH_ENABLED."},"publish":{"type":"boolean","title":"Publish"},"total":{"type":"integer","title":"Total"},"created":{"type":"integer","title":"Created"},"updated":{"type":"integer","title":"Updated"},"unchanged":{"type":"integer","title":"Unchanged","description":"Quarts déjà conformes dans Evolia : aucun appel d'écriture. Un re-push d'un planning dont un seul quart a bougé doit les compter tous ici sauf un.","default":0},"open_shifts":{"type":"integer","title":"Open Shifts"},"skipped":{"type":"integer","title":"Skipped","description":"Quarts non pousses car l'agent n'existe pas dans Evolia (temporaire/parti).","default":0},"errors":{"type":"integer","title":"Errors"},"duplicates":{"type":"integer","title":"Duplicates","description":"Quarts déjà présents à l'identique dans Evolia (ignorés).","default":0},"overlaps":{"type":"integer","title":"Overlaps","description":"Quarts en chevauchement partiel détectés.","default":0},"overlaps_replaced":{"type":"integer","title":"Overlaps Replaced","description":"Chevauchements dont l'existant a été remplacé (PATCH).","default":0},"status_mismatches":{"type":"integer","title":"Status Mismatches","description":"Doublons exacts dont le statut brouillon/publié diffère de l'action.","default":0},"statuses_aligned":{"type":"integer","title":"Statuses Aligned","description":"OBSOLÈTE (toujours 0) : l'alignement de statut via PATCH isDraft est ignoré par Evolia ; ces cas sont désormais comptés dans `manual_actions`.","default":0},"reassigned":{"type":"integer","title":"Reassigned","description":"Quarts existants dont l'agent a changé et qui ont été réaffectés (/assign).","default":0},"manual_actions":{"type":"integer","title":"Manual Actions","description":"Quarts poussés mais dont un point ne peut PAS être appliqué via l'API Evolia (cf. `EvoliaPushItem.manual_action`) : à ajuster manuellement dans Evolia.","default":0},"detection_available":{"type":"boolean","title":"Detection Available","description":"False si la lecture Evolia a échoué : push sans détection (dégradé).","default":true},"confirmation_too_late":{"type":"integer","title":"Confirmation Too Late","description":"Quarts assignés commencés ou imminents : leur confirmation n'arrivera pas à temps (cf. `EvoliaPushItem.confirmation_too_late`).","default":0},"items":{"items":{"$ref":"#/components/schemas/EvoliaPushItem"},"type":"array","title":"Items"},"hours_to_chairs_ratio":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Hours To Chairs Ratio","description":"Score du jour (heures planifiees / chaises). None si non evaluable (pas de chaises pour ce jour) : l'envoi n'est alors pas bloque."},"max_hours_to_chairs_ratio":{"type":"number","title":"Max Hours To Chairs Ratio","description":"Seuil au-dela duquel l'envoi vers Evolia est refuse."},"blocked":{"type":"boolean","title":"Blocked","description":"True si le score depasse le seuil : l'apercu reste consultable, mais toute ecriture est refusee (HTTP 409) — sauf derogation acceptee ou forcage d'un approbateur.","default":false},"forced":{"type":"boolean","title":"Forced","description":"True si cet envoi a franchi le seuil sur forcage motive : le score reste au-dessus (`blocked` aussi), mais l'exception a ete accordee et CONSIGNEE (journal d'audit du planning). Toujours False sur un apercu.","default":false},"confirmations_requested":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Confirmations Requested","description":"Nombre de demandes de confirmation créées suite au push (auto). None = non demandé (flag off / dry-run) ou échec — voir `confirmations_status`."},"confirmations_status":{"anyOf":[{"type":"string","enum":["requested","not_configured","failed"]},{"type":"null"}],"title":"Confirmations Status","description":"Issue de la demande automatique de confirmations. None = AUCUNE tentative (dry-run, envoi bloqué, ou déclenchement auto désactivé) — un état normal. Sans ce champ, un échec était indiscernable d'une non-tentative : le front n'affichait rien et le coordonnateur croyait ses agents prévenus."}},"type":"object","required":["planning_version_id","dry_run","push_enabled","publish","total","created","updated","open_shifts","errors","max_hours_to_chairs_ratio"],"title":"EvoliaPushResult"},"ExportResponse":{"properties":{"planning_version_id":{"type":"string","format":"uuid","title":"Planning Version Id"},"shifts_csv_url":{"type":"string","title":"Shifts Csv Url"},"log_csv_url":{"type":"string","title":"Log Csv Url"},"expires_at":{"type":"string","format":"date-time","title":"Expires At"}},"type":"object","required":["planning_version_id","shifts_csv_url","log_csv_url","expires_at"],"title":"ExportResponse"},"Flight":{"properties":{"flight_number":{"type":"string","title":"Flight Number"},"airline":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Airline"},"scheduled_time_local":{"type":"string","format":"date-time","title":"Scheduled Time Local"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status","description":"Sens du mouvement (arrival|departure) ou 'cancelled'."},"origin":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Origin","description":"Aeroport d'origine (IATA)."},"destination":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Destination","description":"Aeroport de destination (IATA)."},"c_count":{"type":"integer","title":"C Count","description":"Nombre de passagers assistance C (critique) sur ce vol (Menzies).","default":0},"forecast_pax":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Forecast Pax","description":"Passagers assistance PREVUS par Menzies sur ce vol (forecast_total_assistance). None si absent."},"corrected_pax":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Corrected Pax","description":"Passagers assistance APRES correction du modele (corrected_total_assistance) ; repli sur la prevision Menzies si pas de correction pour le vol. None si aucune prevision."},"estimated_time_local":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Estimated Time Local","description":"Heure estimee (Montreal naif) du mouvement, temps reel FR24."},"delay_min":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Delay Min","description":"Retard en minutes (positif = en retard) ; None si inconnu."},"rt_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rt Status","description":"Statut FR24 (scheduled|active|landed|cancelled|diverted|unknown)."},"coverage_pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Coverage Pct","description":"Couverture du creneau du vol = agents planifies / requis (0..1+). None si requis inconnu."},"at_risk":{"type":"boolean","title":"At Risk","description":"Retard > 15 min OU statut FR24 cancelled/diverted.","default":false},"understaffed":{"type":"boolean","title":"Understaffed","description":"Creneau sous-couvert (coverage_pct < 1).","default":false}},"type":"object","required":["flight_number","scheduled_time_local"],"title":"Flight","description":"Vol de la fenetre du jour, enrichi (couverture creneau + temps reel FR24).\n\nLes champs `estimated_time_local`, `delay_min`, `rt_status`, `origin`/`destination`\nproviennent de `flights.flight_status` (poller FR24, temps reel) quand la BDD est\njoignable ; sinon ils restent None (`FlightsResponse.realtime_ok=False`). `at_risk`\net `understaffed` sont decides cote serveur (source unique de verite)."},"FlightSearchCandidate":{"properties":{"id":{"type":"string","title":"Id"},"flight_number":{"type":"string","title":"Flight Number"},"direction":{"type":"string","enum":["DEPARTURE","ARRIVAL"],"title":"Direction"},"airline_iata":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Airline Iata"},"airline_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Airline Name"},"origin":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Origin"},"destination":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Destination"},"gate":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Gate"},"scheduled_time":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Scheduled Time"},"op_time":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Op Time"},"status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Status"},"score":{"type":"number","title":"Score"}},"type":"object","required":["id","flight_number","direction","airline_iata","airline_name","origin","destination","gate","scheduled_time","op_time","status","score"],"title":"FlightSearchCandidate","description":"Un vol de `flights.flight_status` candidat au rattachement, avec son score."},"FlightSearchResponse":{"properties":{"date":{"type":"string","title":"Date"},"match_status":{"type":"string","enum":["exact","multiple","none"],"title":"Match Status"},"candidates":{"items":{"$ref":"#/components/schemas/FlightSearchCandidate"},"type":"array","title":"Candidates"}},"type":"object","required":["date","match_status","candidates"],"title":"FlightSearchResponse","description":"Candidats scores + verdict global : l'agent choisit, rien n'est impose."},"FlightsResponse":{"properties":{"day":{"type":"string","format":"date","title":"Day"},"window_start_local":{"type":"string","format":"date-time","title":"Window Start Local"},"window_end_local":{"type":"string","format":"date-time","title":"Window End Local"},"flights":{"items":{"$ref":"#/components/schemas/Flight"},"type":"array","title":"Flights"},"realtime_ok":{"type":"boolean","title":"Realtime Ok","description":"True si le temps reel (flights.flight_status) a pu etre lu pour ce jour.","default":false}},"type":"object","required":["day","window_start_local","window_end_local"],"title":"FlightsResponse"},"ForecastSender":{"properties":{"email":{"type":"string","title":"Email","description":"Adresse courriel de l'expediteur."},"last_used_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Used At","description":"Derniere ingestion attribuee a cet expediteur (UTC)."}},"type":"object","required":["email"],"title":"ForecastSender","description":"Une personne ayant deja envoye une prevision (liste du formulaire d'import)."},"ForecastSendersResponse":{"properties":{"senders":{"items":{"$ref":"#/components/schemas/ForecastSender"},"type":"array","title":"Senders"}},"type":"object","title":"ForecastSendersResponse","description":"Expediteurs deja vus, du plus recemment utilise au plus ancien."},"ForecastStatusResponse":{"properties":{"planning_day":{"type":"string","format":"date","title":"Planning Day"},"vols":{"type":"integer","title":"Vols","description":"Nombre de vols enregistres pour ce jour."},"vols_avec_assistance":{"type":"integer","title":"Vols Avec Assistance","description":"Vols dont l'assistance prevue est > 0."},"total_assistance":{"type":"integer","title":"Total Assistance","description":"Somme de l'assistance prevue (R+S+C) sur le jour."},"derniere_ecriture":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Derniere Ecriture","description":"Horodatage du dernier import pour ce jour (UTC)."},"vols_calendrier":{"type":"integer","title":"Vols Calendrier","description":"Vols au calendrier mensuel (`flight_schedule`) pour ce jour. > 0 sans prevision = le perimetre est connu, l'amorcage est possible.","default":0},"amorce_depuis_calendrier":{"type":"boolean","title":"Amorce Depuis Calendrier","description":"Vrai si les lignes du jour proviennent d'un amorcage depuis le calendrier (aucune prevision Menzies recue).","default":false}},"type":"object","required":["planning_day","vols","vols_avec_assistance","total_assistance"],"title":"ForecastStatusResponse","description":"Etat de `forecast_demand` pour un jour donne (bouton « Verifier »)."},"ForecastUploadRequest":{"properties":{"filename":{"type":"string","title":"Filename","description":"Nom du fichier (doit se terminer par .xlsx)."},"content_base64":{"type":"string","title":"Content Base64","description":"Contenu du fichier `.xlsx` encode en base64."},"planning_date":{"type":"string","format":"date","title":"Planning Date","description":"Jour des previsions a ingerer (YYYY-MM-DD)."},"email_from":{"type":"string","maxLength":254,"title":"Email From","description":"Courriel de la personne qui a envoye le fichier chez Menzies. Requis : c'est la seule trace de provenance sur le chemin manuel."}},"type":"object","required":["filename","content_base64","planning_date","email_from"],"title":"ForecastUploadRequest","description":"Upload d'un fichier de previsions encode en base64 (pas de multipart).\n\nLe front lit le `.xlsx` et l'envoie en base64 dans un JSON : l'API n'a alors\npas besoin de `python-multipart` (qui alourdirait le bundle Vercel), et relaie\nsimplement le contenu a l'Edge Function."},"ForecastUploadResponse":{"properties":{"status":{"type":"string","title":"Status","description":"Toujours `accepted` si la passerelle a pris le fichier.","default":"accepted"},"planning_day":{"type":"string","format":"date","title":"Planning Day","description":"Jour des previsions a ingerer."},"object_path":{"type":"string","title":"Object Path","description":"Chemin du fichier depose dans le bucket Storage."}},"type":"object","required":["planning_day","object_path"],"title":"ForecastUploadResponse","description":"Reponse a un upload : le traitement reel est asynchrone (GitHub Actions)."},"GeneratePlanningRequest":{"properties":{"planning_day":{"type":"string","format":"date","title":"Planning Day","description":"Jour a planifier (YYYY-MM-DD, heure Montreal)."},"scenario":{"anyOf":[{"type":"string","enum":["low_cost","balanced","safe","auto"]},{"type":"null"}],"title":"Scenario","description":"Profil impose par le superviseur (low_cost/balanced/safe) ou 'auto' (le moteur compare les scenarios et choisit). None = profil par defaut du pipeline (economique)."},"seed_from_schedule":{"type":"boolean","title":"Seed From Schedule","description":"Amorcer le perimetre depuis `flight_schedule` si le jour n'a aucune prevision Menzies. Sans effet si une prevision reelle existe : le script du pipeline refuse alors d'ecrire.","default":false}},"type":"object","required":["planning_day"],"title":"GeneratePlanningRequest","description":"Demande de generation du planning d'un jour (correction Menzies + optimisation).\n\nLa generation tourne en GitHub Actions (pipeline trop lourd pour Vercel) : l'API\nne fait que declencher le workflow et repond immediatement. Le resultat (quarts\nrecommandes) apparait dans `recommended_planning` quelques minutes plus tard."},"GeneratePlanningResponse":{"properties":{"status":{"type":"string","title":"Status","description":"`accepted` si le workflow a ete declenche.","default":"accepted"},"planning_day":{"type":"string","format":"date","title":"Planning Day","description":"Jour a planifier."},"schema_name":{"type":"string","title":"Schema Name","description":"Schema BDD cible (= schema lu par l'API)."},"scenario":{"anyOf":[{"type":"string","enum":["low_cost","balanced","safe","auto"]},{"type":"null"}],"title":"Scenario","description":"Profil relaye au pipeline (None = profil par defaut)."}},"type":"object","required":["planning_day","schema_name"],"title":"GeneratePlanningResponse","description":"Reponse a une demande de generation : le traitement reel est asynchrone."},"GenerationInfoResponse":{"properties":{"planning_day":{"type":"string","format":"date","title":"Planning Day"},"generation_method":{"type":"string","title":"Generation Method"},"mode":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Mode"},"anchored":{"type":"boolean","title":"Anchored","default":false},"recommended_scenario":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Recommended Scenario"},"status":{"type":"string","title":"Status","default":"generated"},"source_days":{"items":{"type":"string"},"type":"array","title":"Source Days"},"history_weeks":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"History Weeks"},"reference_days_used":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Reference Days Used"},"rentability_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Rentability Score"},"hours_removed_for_rentability":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Hours Removed For Rentability"},"fallback_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Fallback Reason"},"alerts":{"items":{"type":"string"},"type":"array","title":"Alerts"},"message":{"type":"string","title":"Message"},"created_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Created At"}},"type":"object","required":["planning_day","generation_method","message"],"title":"GenerationInfoResponse","description":"« Comment ce planning a été produit » — dernière ligne `schedule_generations`.\n\nAlimenté par le runner à chaque génération (méthode, mode SCALED_TEMPLATE_MODE,\nmetrics.scaled_template). `message` est la phrase prête pour la bannière."},"GesteIgnoreOut":{"properties":{"designation":{"type":"string","title":"Designation"},"cause":{"type":"string","title":"Cause"}},"type":"object","required":["designation","cause"],"title":"GesteIgnoreOut","description":"Une designation ecartee, avec sa cause EXACTE.\n\n`apply.appliquer_gestes` rend desormais une liste de `apply.GesteIgnore`\n(designation + cause), plus une simple liste de chaines : le coordonnateur qui lit\n`ignores` doit pouvoir distinguer « quelqu'un d'autre a deja traite ce cas,\nrafraichissez » (cause `ecart_disparu`) de « donnee Evolia incomplete » (cause\n`evolia_sans_matricule`), deux causes qui n'appellent pas la meme action de sa part.\nVoir les constantes `CAUSE_*` de `api/services/evolia_reconcile/apply.py`."},"GesteIn":{"properties":{"famille":{"type":"string","title":"Famille"},"scheduled_shift_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scheduled Shift Id"},"evolia_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Evolia Id"}},"type":"object","required":["famille"],"title":"GesteIn"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"HealthResponse":{"properties":{"status":{"type":"string","title":"Status"},"app":{"type":"string","title":"App"},"environment":{"type":"string","title":"Environment"},"timestamp":{"type":"string","format":"date-time","title":"Timestamp"}},"type":"object","required":["status","app","environment","timestamp"],"title":"HealthResponse"},"KpiCard":{"properties":{"level":{"type":"integer","title":"Level","description":"1 = direction, 2 = data team, 3 = operations."},"label":{"type":"string","title":"Label"},"audience":{"type":"string","title":"Audience"},"description":{"type":"string","title":"Description"},"unit":{"type":"string","title":"Unit"},"higher_is_better":{"type":"boolean","title":"Higher Is Better"},"available":{"type":"boolean","title":"Available"},"value":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Value"},"baseline":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Baseline","description":"Valeur de reference (Menzies brut) sur la meme plage."},"delta":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Delta","description":"value - baseline (signe = sens du gain)."},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note"}},"type":"object","required":["level","label","audience","description","unit","higher_is_better","available"],"title":"KpiCard","description":"Un indicateur de la hierarchie a 3 niveaux."},"KpiCompanyPoint":{"properties":{"airline":{"type":"string","title":"Airline","description":"Code compagnie derive du numero de vol (WS, AT...)."},"days_with_data":{"type":"integer","title":"Days With Data","description":"Jours de la plage ou la compagnie a au moins un vol evalue."},"flights_evaluated":{"type":"integer","title":"Flights Evaluated","description":"Vols entrant dans la precision et le RMSE."},"menzies":{"type":"number","title":"Menzies","description":"Chaises annoncees par Menzies, sommees sur la plage."},"corrected":{"type":"number","title":"Corrected","description":"Chaises corrigees, sommees sur la plage."},"real":{"type":"number","title":"Real","description":"Chaises reelles, vols sans PEC comptes a 0."},"precision_menzies_pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Precision Menzies Pct","description":"1 - |menzies - reel| / reel sur les TOTAUX de la plage, en %. Pas une moyenne journaliere : une compagnie a peu de vols n'a pas de journee mesurable. None sans aucune chaise reelle."},"precision_corrected_pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Precision Corrected Pct","description":"Idem pour le corrige."},"rmse_menzies":{"type":"number","title":"Rmse Menzies","description":"RMSE par vol de l'annonce sur la plage."},"rmse_corrected":{"type":"number","title":"Rmse Corrected","description":"RMSE par vol du corrige sur la plage."},"sample_ok":{"type":"boolean","title":"Sample Ok","description":"Au moins PLANCHER_CHAISES_REELLES chaises reelles sur la plage. En dessous, la ligne est publiee mais son verdict ne vaut pas conclusion."},"verdict":{"type":"string","title":"Verdict","description":"mieux | pire_precision | pire_rmse | pire_les_deux — la correction contre l'annonce, sur cette compagnie (cf. bias_rolling.verdict_vs_menzies)."}},"type":"object","required":["airline","days_with_data","flights_evaluated","menzies","corrected","real","rmse_menzies","rmse_corrected","sample_ok","verdict"],"title":"KpiCompanyPoint","description":"Precision et RMSE de la correction contre l'annonce Menzies, pour UNE compagnie.\n\nCe que le global cache : sur 16 jours d'octobre 2026, 90 % de precision globale\nrecouvraient un WestJet a 60 % et un TK degrade sur les deux criteres. Chaque\nligne porte son propre verdict, calcule cote serveur par\n`bias_rolling.verdict_vs_menzies` — la MEME regle que le signal de veille\n`correction_vs_menzies`."},"KpiDailyPoint":{"properties":{"day":{"type":"string","format":"date","title":"Day"},"menzies":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Menzies"},"corrected":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Corrected"},"real":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Real"},"precision_menzies_pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Precision Menzies Pct","description":"100 - |menzies - real| / real, en %, arrondi a 0.1."},"precision_corrected_pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Precision Corrected Pct","description":"100 - |corrected - real| / real, en %, arrondi a 0.1."},"flights_forecast":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Flights Forecast","description":"Nombre de vols prevus (corrected_demand) sur le jour."},"flights_matched":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Flights Matched","description":"Nombre de vols prevus ayant au moins une PEC / un reel constate."},"real_coverage_pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Real Coverage Pct","description":"flights_matched / flights_forecast, en %. Sous le seuil de couverture, un vol sans reel est traite comme non mesure (exclu) plutot que comme un zero."},"rmse_menzies":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Rmse Menzies","description":"RMSE par vol de la prevision Menzies sur CE jour : sqrt(somme des carres d'ecarts / flights_evaluated), arrondi a 1e-4."},"rmse_corrected":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Rmse Corrected","description":"Idem pour la prevision corrigee par le modele."},"flights_evaluated":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Flights Evaluated","description":"Nombre de vols entrant dans le RMSE du jour. Egal a flights_forecast quand les zeros sont appliques, a flights_matched sinon. Publie pour rendre le RMSE re-agregeable : sqrt(SUM(rmse^2 * n) / SUM(n))."},"real_zeros_applied":{"type":"boolean","title":"Real Zeros Applied","description":"True si les vols prevus sans reel ont ete comptes a 0 (jour suffisamment couvert). False = repli sur les seuls vols apparies, precision optimiste.","default":false}},"type":"object","required":["day"],"title":"KpiDailyPoint","description":"Une ligne par jour de la plage demandee."},"KpiResponse":{"properties":{"from":{"type":"string","format":"date","title":"From"},"to":{"type":"string","format":"date","title":"To"},"days_count":{"type":"integer","title":"Days Count","description":"Nombre de jours dans la plage [from, to]."},"days_with_data":{"type":"integer","title":"Days With Data","description":"Nombre de jours pour lesquels au moins une donnee existe."},"daily":{"items":{"$ref":"#/components/schemas/KpiDailyPoint"},"type":"array","title":"Daily"},"cards":{"items":{"$ref":"#/components/schemas/KpiCard"},"type":"array","title":"Cards","description":"3 cards KPI (niveaux 1, 2, 3)."},"companies":{"items":{"$ref":"#/components/schemas/KpiCompanyPoint"},"type":"array","title":"Companies","description":"Precision et RMSE contre Menzies PAR COMPAGNIE, triees par chaises reelles decroissantes. Vide quand aucun jour de la plage n'a de reel exploitable."}},"type":"object","required":["from","to","days_count","days_with_data"],"title":"KpiResponse","description":"Indicateurs de fiabilite agreges sur une plage de dates.\n\nHierarchie a 3 niveaux :\n  - Niveau 1 (direction) : precision moyenne journaliere du corrige.\n  - Niveau 2 (data team) : RMSE inter-vols du corrige.\n  - Niveau 3 (operations) : precision par bucket de 30 min (a construire)."},"LabelCount":{"properties":{"label":{"type":"string","title":"Label"},"count":{"type":"integer","title":"Count"}},"type":"object","required":["label","count"],"title":"LabelCount","description":"Compteur generique etiquete (ex : motif de correction le plus frequent)."},"MeResponse":{"properties":{"user_id":{"type":"string","title":"User Id"},"role":{"type":"string","enum":["superviseur","lecteur"],"title":"Role"},"email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Email"}},"type":"object","required":["user_id","role"],"title":"MeResponse"},"ModelChangeMarker":{"properties":{"model_version":{"type":"string","title":"Model Version"},"model_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model Label"},"generation":{"type":"integer","title":"Generation"},"changed_on":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Changed On"},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Description"},"mae_holdout":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Mae Holdout"},"rmse_holdout":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Rmse Holdout"},"wape_holdout":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Wape Holdout"},"bias_holdout":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Bias Holdout"},"n_scored":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"N Scored","description":"Nombre de vols evalues sur le holdout pour cette generation."},"is_active":{"type":"boolean","title":"Is Active","default":false}},"type":"object","required":["model_version","generation"],"title":"ModelChangeMarker","description":"Marqueur vertical : passage d'une generation de modele a la suivante."},"ModelPerformancePoint":{"properties":{"model_version":{"type":"string","title":"Model Version"},"model_label":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Model Label"},"generation":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Generation","description":"0=Menzies, 1=LSTM, 2=GBM... (ordre des generations)."},"scope":{"type":"string","title":"Scope","description":"daily | holdout | monthly."},"evaluation_date":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Evaluation Date"},"mae":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Mae"},"rmse":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Rmse"},"wape":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Wape"},"bias":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Bias"},"mae_baseline":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Mae Baseline","description":"MAE de la prevision Menzies brute (reference) sur le meme jour."},"rmse_baseline":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Rmse Baseline","description":"RMSE de la prevision Menzies brute (reference) sur le meme jour."},"rmse_improvement_pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Rmse Improvement Pct","description":"Gain RMSE vs baseline Menzies, en %."}},"type":"object","required":["model_version","scope"],"title":"ModelPerformancePoint","description":"Un point de la courbe d'amelioration du modele (une evaluation datee).\n\nSource : vue `v_model_performance_timeline` (runs + libelle/generation modele)."},"ModelPerformanceResponse":{"properties":{"points":{"items":{"$ref":"#/components/schemas/ModelPerformancePoint"},"type":"array","title":"Points"},"changes":{"items":{"$ref":"#/components/schemas/ModelChangeMarker"},"type":"array","title":"Changes"}},"type":"object","title":"ModelPerformanceResponse","description":"Courbe d'amelioration du modele de correction + marqueurs de changement.\n\n`points` : serie temporelle a tracer (scope `daily` par defaut, ou tous scopes).\n`changes`: generations de modele (Menzies -> LSTM -> GBM) pour les marqueurs."},"ModificationLogEntry":{"properties":{"id":{"type":"integer","title":"Id"},"timestamp":{"type":"string","format":"date-time","title":"Timestamp"},"user_id":{"type":"string","format":"uuid","title":"User Id"},"action":{"type":"string","title":"Action"},"shift_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Shift Id"},"candidate_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Candidate Id"},"old_agent_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Old Agent Code"},"old_agent_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Old Agent Name"},"new_agent_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"New Agent Code"},"new_agent_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"New Agent Name"},"old_start":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Old Start"},"new_start":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"New Start"},"old_end":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Old End"},"new_end":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"New End"},"old_duration_hours":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"Old Duration Hours"},"new_duration_hours":{"anyOf":[{"type":"string","pattern":"^(?!^[-+.]*$)[+-]?0*\\d*\\.?\\d*$"},{"type":"null"}],"title":"New Duration Hours"},"supervisor_note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Supervisor Note"},"reason_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason Code"},"validation_status":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Validation Status"},"warnings":{"anyOf":[{"items":{"additionalProperties":true,"type":"object"},"type":"array"},{"type":"null"}],"title":"Warnings"}},"type":"object","required":["id","timestamp","user_id","action","shift_id","candidate_id","old_start","new_start","old_end","new_end","old_duration_hours","new_duration_hours","supervisor_note","validation_status","warnings"],"title":"ModificationLogEntry"},"MonthCoverageDay":{"properties":{"date":{"type":"string","format":"date","title":"Date","description":"Jour (YYYY-MM-DD)."},"vols":{"type":"integer","title":"Vols","description":"Nombre de vols prevus pour ce jour."},"vols_avec_assistance":{"type":"integer","title":"Vols Avec Assistance","description":"Vols dont l'assistance prevue est > 0."},"derniere_ecriture":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Derniere Ecriture","description":"Horodatage du dernier import pour ce jour (UTC)."}},"type":"object","required":["date","vols","vols_avec_assistance"],"title":"MonthCoverageDay","description":"Previsions enregistrees pour un jour (case verte du calendrier)."},"MonthCoverageResponse":{"properties":{"month":{"type":"string","title":"Month","description":"Mois interroge (YYYY-MM)."},"schedule":{"$ref":"#/components/schemas/MonthCoverageSchedule"},"days":{"items":{"$ref":"#/components/schemas/MonthCoverageDay"},"type":"array","title":"Days","description":"Uniquement les jours ayant des previsions ; le front comble le reste."}},"type":"object","required":["month","schedule","days"],"title":"MonthCoverageResponse","description":"Couverture d'un mois : schedule (mois) + previsions (par jour)."},"MonthCoverageSchedule":{"properties":{"present":{"type":"boolean","title":"Present","description":"Vrai si au moins un vol planifie pour le mois."},"vols":{"type":"integer","title":"Vols","description":"Nombre de vols planifies sur le mois."},"derniere_ecriture":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Derniere Ecriture","description":"Horodatage du dernier import schedule du mois (UTC)."}},"type":"object","required":["present","vols"],"title":"MonthCoverageSchedule","description":"Presence du planning mensuel (PRM Schedule) pour le mois (titre du calendrier)."},"MotifOut":{"properties":{"code":{"type":"string","enum":["client_request","extra_flights","direction_operation","training","irregularities","uncovered_slot"],"title":"Code"},"libelle":{"type":"string","title":"Libelle"},"libelle_reference":{"type":"string","title":"Libelle Reference"}},"type":"object","required":["code","libelle","libelle_reference"],"title":"MotifOut"},"OfferAcceptedBody":{"properties":{"offer_id":{"type":"string","format":"uuid","title":"Offer Id","description":"Id rh.shift_confirmations (kind='offer')."},"rh_shift_id":{"type":"string","format":"uuid","title":"Rh Shift Id","description":"Id rh.shifts du quart concerne (colonne rh_shift_id de la trace)."},"external_id":{"type":"string","title":"External Id","description":"pir-ops-{jour}-{scheduled_shift_id}."},"member_code":{"type":"string","title":"Member Code","description":"Code Evolia du REMPLACANT, resolu depuis l'offre (jamais rh.shifts)."},"titulaire_member_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Titulaire Member Code","description":"Code du titulaire, pour detecter un conflit."}},"type":"object","required":["offer_id","rh_shift_id","external_id","member_code"],"title":"OfferAcceptedBody","description":"Rappel ORION-RH : un remplaçant vient d'accepter une offre de quart."},"OfferAcceptedResult":{"properties":{"applied":{"type":"boolean","title":"Applied"},"already_applied":{"type":"boolean","title":"Already Applied","default":false},"planning_version_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Planning Version Id"},"evolia_outcome":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Evolia Outcome"},"evolia_error":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Evolia Error"},"evolia_is_draft":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Evolia Is Draft"},"conflict_member_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Conflict Member Code"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"}},"type":"object","required":["applied"],"title":"OfferAcceptedResult","description":"Issue de l'application d'une offre acceptee. Champs alignes un pour un sur\n`OfferApplicationResult` (api/services/offer_application.py) — construction\nexplicite cote routeur, jamais `**vars(...)`."},"OfferRequestBody":{"properties":{"external_id":{"type":"string","title":"External Id","description":"external_id Evolia du quart (pir-ops-…)."},"member_code":{"type":"string","title":"Member Code","description":"Code Evolia du remplaçant à contacter."},"channel":{"anyOf":[{"type":"string","enum":["voice","sms"]},{"type":"null"}],"title":"Channel","description":"Canal forcé ; défaut ORION-RH (voice) sinon."}},"type":"object","required":["external_id","member_code"],"title":"OfferRequestBody","description":"Demande d'OFFRE de quart à un remplaçant (replanification assistée)."},"OfferRequestResult":{"properties":{"external_id":{"type":"string","title":"External Id"},"member_code":{"type":"string","title":"Member Code"},"outcome":{"type":"string","enum":["offered","already_active","no_phone","not_found","employee_not_found","error"],"title":"Outcome","description":"offered = offre `pending` créée ; already_active = offre déjà en cours pour ce quart ; no_phone / employee_not_found / not_found = pré-requis manquant ; error = erreur inattendue côté ORION-RH."}},"type":"object","required":["external_id","member_code","outcome"],"title":"OfferRequestResult","description":"Issue d'une offre relayée à ORION-RH."},"PlannedAgent":{"properties":{"member_code":{"type":"string","title":"Member Code"},"full_name":{"type":"string","title":"Full Name"},"position":{"type":"string","enum":["prepose","superviseur","coordonnateur","autre"],"title":"Position"},"has_careflow_account":{"type":"boolean","title":"Has Careflow Account"},"hours":{"type":"number","title":"Hours"},"shifts":{"items":{"$ref":"#/components/schemas/PlannedShift"},"type":"array","title":"Shifts"},"is_reinforcement":{"type":"boolean","title":"Is Reinforcement","default":false},"planning_only":{"type":"boolean","title":"Planning Only","default":false},"has_evolia_account":{"type":"boolean","title":"Has Evolia Account","default":true}},"type":"object","required":["member_code","full_name","position","has_careflow_account","hours","shifts"],"title":"PlannedAgent","description":"Une personne planifiee ce jour-la, avec tous ses blocs."},"PlannedAgentsResponse":{"properties":{"date":{"type":"string","format":"date","title":"Date"},"agents":{"items":{"$ref":"#/components/schemas/PlannedAgent"},"type":"array","title":"Agents"},"shift_count":{"type":"integer","title":"Shift Count"},"total_hours":{"type":"number","title":"Total Hours"},"coverage":{"items":{"type":"integer"},"type":"array","title":"Coverage"},"gaps":{"items":{"$ref":"#/components/schemas/CoverageGap"},"type":"array","title":"Gaps"},"reinforcements":{"items":{"$ref":"#/components/schemas/PlannedAgent"},"type":"array","title":"Reinforcements","default":[]}},"type":"object","required":["date","agents","shift_count","total_hours","coverage","gaps"],"title":"PlannedAgentsResponse"},"PlannedShift":{"properties":{"start":{"type":"string","format":"date-time","title":"Start"},"end":{"type":"string","format":"date-time","title":"End"},"start_min":{"type":"integer","maximum":1440.0,"minimum":0.0,"title":"Start Min"},"end_min":{"type":"integer","maximum":1440.0,"minimum":0.0,"title":"End Min"},"position":{"type":"string","enum":["prepose","superviseur","coordonnateur","autre"],"title":"Position"},"pushed_by_careflow":{"type":"boolean","title":"Pushed By Careflow"},"team":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Team"},"source":{"type":"string","enum":["evolia","planning"],"title":"Source","default":"evolia"}},"type":"object","required":["start","end","start_min","end_min","position","pushed_by_careflow"],"title":"PlannedShift","description":"Un bloc de quart. Un agent peut en avoir plusieurs (quart fractionne)."},"PlanningAdjustmentsResponse":{"properties":{"from":{"type":"string","format":"date","title":"From"},"to":{"type":"string","format":"date","title":"To"},"source_ok":{"type":"boolean","title":"Source Ok","description":"False si la BDD est injoignable : la page affiche un etat degrade.","default":true},"stability_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Stability Score","description":"Score de stabilite global = part des quarts repris tels quels sur l'union(genere, final) de tous les jours comparables (0..100). None si aucun jour n'a de planning genere ET final a comparer."},"days_analyzed":{"type":"integer","title":"Days Analyzed","description":"Jours planifies distincts ayant au moins une decision."},"days_with_corrections":{"type":"integer","title":"Days With Corrections","description":"Jours ayant au moins un planning modifie."},"validated_total":{"type":"integer","title":"Validated Total","description":"Total plannings valides sans modification."},"modified_total":{"type":"integer","title":"Modified Total","description":"Total retours de modification."},"rejected_total":{"type":"integer","title":"Rejected Total","description":"Total refus."},"correction_requested_total":{"type":"integer","title":"Correction Requested Total","description":"Total corrections demandees."},"comments_total":{"type":"integer","title":"Comments Total","description":"Total commentaires seuls."},"decisions_total":{"type":"integer","title":"Decisions Total","description":"Total decisions structurantes."},"shifts_unchanged_total":{"type":"integer","title":"Shifts Unchanged Total","description":"Total quarts repris tels quels.","default":0},"shifts_total":{"type":"integer","title":"Shifts Total","description":"Total quarts distincts = somme des union(genere, final) par jour.","default":0},"shifts_changed_total":{"type":"integer","title":"Shifts Changed Total","description":"Total quarts modifies, ajoutes ou supprimes.","default":0},"per_day":{"items":{"$ref":"#/components/schemas/PlanningDayAdjustment"},"type":"array","title":"Per Day"},"hours_per_day":{"items":{"$ref":"#/components/schemas/PlanningDayHours"},"type":"array","title":"Hours Per Day","description":"Heures planifiees par jour : genere vs apres corrections. Vide si les tables de planning sont injoignables (section masquee cote front)."}},"type":"object","required":["from","to","days_analyzed","days_with_corrections","validated_total","modified_total","rejected_total","correction_requested_total","comments_total","decisions_total"],"title":"PlanningAdjustmentsResponse","description":"Synthese des ajustements du planning propose sur la plage [from, to]."},"PlanningDayAdjustment":{"properties":{"planning_day":{"type":"string","format":"date","title":"Planning Day"},"validated":{"type":"integer","title":"Validated","description":"Plannings valides sans modification (confirm + validate_with_remarks)."},"modified":{"type":"integer","title":"Modified","description":"Retours de modification (modify)."},"rejected":{"type":"integer","title":"Rejected","description":"Refus (reject)."},"correction_requested":{"type":"integer","title":"Correction Requested","description":"Demandes de correction (request_correction)."},"comments":{"type":"integer","title":"Comments","description":"Commentaires seuls (comment)."},"decisions":{"type":"integer","title":"Decisions","description":"Decisions structurantes = valides + modifies + refuses + corrections."},"shifts_generated":{"type":"integer","title":"Shifts Generated","description":"Quarts du planning genere (proposition du moteur).","default":0},"shifts_final":{"type":"integer","title":"Shifts Final","description":"Quarts du planning final (derniere version enregistree).","default":0},"shifts_unchanged":{"type":"integer","title":"Shifts Unchanged","description":"Quarts repris tels quels (meme agent ET meme horaire).","default":0},"shifts_changed":{"type":"integer","title":"Shifts Changed","description":"Quarts modifies, ajoutes ou supprimes = union(genere, final) - conserves.","default":0},"stability_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Stability Score","description":"Part des quarts repris tels quels sur l'union(genere, final), en % (0..100). None si le jour n'a pas de planning genere ET final a comparer."},"not_comparable_reason":{"anyOf":[{"type":"string","enum":["no_generated","no_final"]},{"type":"null"}],"title":"Not Comparable Reason","description":"Pourquoi le diff quart-par-quart n'a pas pu etre calcule pour ce jour. None quand le jour EST comparable. Sans ce motif, un jour non comparable arrive au front avec `shifts_changed = 0`, indiscernable d'un jour reellement non corrige."}},"type":"object","required":["planning_day","validated","modified","rejected","correction_requested","comments","decisions"],"title":"PlanningDayAdjustment","description":"Decisions superviseur d'un jour planifie (boucle de validation).\n\nLes compteurs `validated/modified/...` restent le decompte des RETOURS\nsuperviseur (une ligne par decision). Le score de stabilite, lui, ne se\nfonde PLUS dessus (un « modify » = une sauvegarde, pas un quart) mais sur un\ndiff QUART PAR QUART entre le planning genere et le planning final."},"PlanningDayHours":{"properties":{"planning_day":{"type":"string","format":"date","title":"Planning Day"},"generated_hours":{"type":"number","title":"Generated Hours","description":"Heures du planning genere (propose)."},"current_hours":{"type":"number","title":"Current Hours","description":"Heures apres les corrections du superviseur."}},"type":"object","required":["planning_day","generated_hours","current_hours"],"title":"PlanningDayHours","description":"Heures planifiees d'un jour : plan GENERE vs plan APRES le superviseur.\n\n`generated_hours` = sortie brute du modele (`recommended_planning`), le planning\npropose. `current_hours` = derniere version sauvegardee du jour\n(`planning_versions`), c'est-a-dire ce que le superviseur a garde -- le meme\nchiffre que la page « Planning du jour ». Aucune version sauvegardee => le\nsuperviseur n'a rien change, les deux valeurs sont egales.\n\nOn ne lit PAS `rh.shifts` (roster Evolia) ici : il porte aussi les editions faites\ndirectement dans Evolia, hors CareFlow, qui ne sont pas des corrections du planning\npropose."},"PlanningStatusResponse":{"properties":{"scenario_name":{"type":"string","title":"Scenario Name"},"planning_day":{"type":"string","format":"date","title":"Planning Day"},"current_status":{"type":"string","enum":["propose","en_attente","valide","modifie","refuse","a_corriger"],"title":"Current Status"},"latest_version_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Latest Version Id"},"feedback_history":{"items":{"$ref":"#/components/schemas/SupervisorFeedbackEntry"},"type":"array","title":"Feedback History"}},"type":"object","required":["scenario_name","planning_day","current_status"],"title":"PlanningStatusResponse"},"PlanningVersionResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"base_scenario":{"type":"string","title":"Base Scenario"},"planning_day":{"type":"string","format":"date","title":"Planning Day"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"created_by":{"type":"string","format":"uuid","title":"Created By"},"shifts":{"items":{"$ref":"#/components/schemas/Shift"},"type":"array","title":"Shifts"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes"},"log":{"items":{"$ref":"#/components/schemas/ModificationLogEntry"},"type":"array","title":"Log"}},"type":"object","required":["id","base_scenario","planning_day","created_at","created_by","shifts","notes"],"title":"PlanningVersionResponse"},"ProposedShiftChange":{"properties":{"target_shift_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Shift Id","description":"Quart cible (None pour une creation)."},"before_start_local":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Before Start Local"},"before_end_local":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Before End Local"},"after_start_local":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"After Start Local"},"after_end_local":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"After End Local"},"positions":{"type":"integer","title":"Positions","description":"Nombre de postes concernes (quarts a creer/retirer).","default":1},"agent_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agent Id","description":"Agent concerne (reaffectation), sinon None."}},"type":"object","title":"ProposedShiftChange","description":"Changement propose sur un quart : etat avant (si cible existante) et apres."},"ReconciliationResponse":{"properties":{"jour":{"type":"string","format":"date","title":"Jour"},"ecarts":{"items":{"$ref":"#/components/schemas/EcartOut"},"type":"array","title":"Ecarts"},"fantomes_ecartes":{"type":"integer","title":"Fantomes Ecartes","description":"Lignes `rh.shifts` ecartees parce que leur event n'existe plus dans Evolia. Un compteur qui gonfle signale que le seuil de 6 h doit etre revu."},"jour_pousse":{"type":"boolean","title":"Jour Pousse","description":"Au moins un quart du board est apparie a un event Evolia. A false, la famille « absent d'Evolia » n'est pas calculee : c'est le JOUR qui n'a pas ete pousse, pas les quarts qui manquent."},"board_comparable":{"type":"boolean","title":"Board Comparable","description":"A false, le board lui-meme est vide (rien a comparer) : c'est benin, l'etat normal de J+1 avant sa generation. A true avec `jour_pousse=false`, le board a un planning complet mais AUCUN quart n'est dans Evolia -- une journee entiere de travail absente de l'outil de paie, ce qui est grave."}},"type":"object","required":["jour","ecarts","fantomes_ecartes","jour_pousse","board_comparable"],"title":"ReconciliationResponse"},"ReferenceDaySummary":{"properties":{"day":{"type":"string","format":"date","title":"Day"},"has_generated":{"type":"boolean","title":"Has Generated"},"has_published":{"type":"boolean","title":"Has Published"},"generated_shift_count":{"type":"integer","title":"Generated Shift Count","default":0},"published_shift_count":{"type":"integer","title":"Published Shift Count","default":0},"supervisor_added_buckets":{"type":"integer","title":"Supervisor Added Buckets","description":"Buckets ou le superviseur a renforce (+1 agent ou plus).","default":0},"supervisor_removed_buckets":{"type":"integer","title":"Supervisor Removed Buckets","description":"Buckets ou le superviseur a retire de la couverture.","default":0},"total_assistance":{"type":"number","title":"Total Assistance","default":0.0}},"type":"object","required":["day","has_generated","has_published"],"title":"ReferenceDaySummary","description":"Resume d'un jour de reference (meme jour de semaine, semaines precedentes)."},"ReingestLatestRequest":{"properties":{"planning_day":{"type":"string","format":"date","title":"Planning Day","description":"Jour a re-ingerer puis generer (YYYY-MM-DD)."}},"type":"object","required":["planning_day"],"title":"ReingestLatestRequest","description":"Re-ingestion d'un jour depuis le dernier fichier Menzies deja en storage."},"ReingestLatestResponse":{"properties":{"status":{"type":"string","title":"Status","default":"accepted"},"planning_day":{"type":"string","format":"date","title":"Planning Day","description":"Jour re-ingere puis genere."},"object_path":{"type":"string","title":"Object Path","description":"Dernier fichier reutilise dans le bucket Storage."}},"type":"object","required":["planning_day","object_path"],"title":"ReingestLatestResponse","description":"Reponse : traitement asynchrone (ingestion + generation en GitHub Actions)."},"RentabilityDay":{"properties":{"day":{"type":"string","format":"date","title":"Day"},"hours_source":{"type":"string","enum":["clocked","estimated","unavailable"],"title":"Hours Source","default":"unavailable"},"hours_read_error":{"type":"boolean","title":"Hours Read Error","default":false},"is_current_or_future":{"type":"boolean","title":"Is Current Or Future","description":"True si le jour est aujourd'hui ou dans le futur (heure Montreal) : le scenario reel n'est pas encore observable."},"menzies_chairs":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Menzies Chairs","description":"Chaises prevues par Menzies (repli si pas de correction)."},"corrected_chairs":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Corrected Chairs","description":"Chaises apres correction du modele."},"real_chairs":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Real Chairs","description":"Chaises reellement constatees. None si jour futur ou aucun reel."},"planned_hours_db":{"type":"number","title":"Planned Hours Db","description":"Heures planifiees du jour calendaire (quarts decoupes a minuit).","default":0.0},"evolia_hours":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Evolia Hours","description":"Heures Evolia pointees ou estimees selon hours_source. None si indisponible ou jour futur."},"evolia_hours_coordinator":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Evolia Hours Coordinator","description":"Part de `evolia_hours` tenue au poste de coordonnateur (045). INCLUSE dans `evolia_hours` : l'onglet l'affiche a part, il ne l'additionne pas."},"hours_delta":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Hours Delta","description":"evolia_hours - planned_hours_db (ecart planifie/reel)."},"score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Score","description":"evolia_hours / chaises (reelles si dispo, sinon corrigees) — heures payees par chaise servie."},"plan_ca":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Plan Ca","description":"CA planifie = plan_chairs x tarif."},"plan_cost":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Plan Cost","description":"Cout charge planifie = masse salariale planifiee x charges."},"plan_profit":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Plan Profit","description":"plan_ca - plan_cost."},"plan_margin_pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Plan Margin Pct","description":"plan_profit * 100 / plan_ca."},"real_ca":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Real Ca","description":"CA reel = real_chairs x tarif. None si jour futur."},"real_cost":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Real Cost","description":"Cout charge reel = masse salariale Evolia x charges. None si jour futur."},"real_profit":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Real Profit","description":"real_ca - real_cost. None si jour futur."},"real_margin_pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Real Margin Pct","description":"real_profit * 100 / real_ca. None si jour futur."},"ecart_profit":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Ecart Profit","description":"real_profit - plan_profit. None si l'un des deux est indisponible."},"avg_hourly_wage":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Avg Hourly Wage","description":"Taux horaire moyen pondere du scenario planifie (mix des roles)."}},"type":"object","required":["day","is_current_or_future"],"title":"RentabilityDay","description":"Rentabilite d'UN jour calendaire, DEUX scenarios cote a cote (mois).\n\n`plan_*` : engagement (heures planifiees x taux forfaitaire du role) — calculable\npour tout jour de la plage, passe OU futur. `real_*` : observation (heures Evolia\neffectivement payees x meme taux) — uniquement pour un jour PASSE OU EN COURS\n(`is_current_or_future=False`) ; reste `None` pour un jour futur (aucune heure payee\nne peut encore exister), jamais 0 (ne pas laisser croire a une rentabilite reelle\nnulle). Modele forfaitaire par role (cf. `services/wage_rates.py`) : toute heure a un\ncout connu, plus d'agent « sans salaire » ni imputation."},"RentabilityRangeResponse":{"properties":{"from":{"type":"string","format":"date","title":"From"},"to":{"type":"string","format":"date","title":"To"},"days":{"items":{"$ref":"#/components/schemas/RentabilityDay"},"type":"array","title":"Days"},"totals":{"$ref":"#/components/schemas/RentabilityTotals"}},"type":"object","required":["from","to","totals"],"title":"RentabilityRangeResponse","description":"Rentabilite mensuelle : une ligne par jour (2 scenarios) + totaux de la plage."},"RentabilityResponse":{"properties":{"day":{"type":"string","format":"date","title":"Day"},"chairs":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Chairs","description":"C4 : chaises servies (corrigees, repli Menzies)."},"planned_hours":{"type":"number","title":"Planned Hours","description":"F4 : heures planifiees du jour CALENDAIRE (00:00->24:00 local, quarts decoupes a minuit).","default":0.0},"carryover_hours":{"type":"number","title":"Carryover Hours","description":"Heures du petit matin de J provenant du quart de nuit de J-1 (00:00->fin), incluses dans planned_hours. Constantes vis-a-vis des modifs de J : permet au front de recalculer F4 en direct sans recharger J-1.","default":0.0},"ratio":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Ratio","description":"E10 = heures/chaise (objectif < 1), HEURES HORS CONTRAT EXCLUES du numerateur : `(planned_hours - off_contract_hours) / chairs`. C'est aussi le score qui bloque l'envoi vers Evolia au-dela de 1,15."},"wage_cost":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Wage Cost","description":"E4 : masse salariale (Somme salaire/h x heures)."},"off_contract_hours":{"type":"number","title":"Off Contract Hours","description":"Part de `planned_hours` dont LE QUART n'appartient pas au contrat Menzies, jugee a la date du quart (`services/contract_membership.py`) : renfort prete par un autre contrat, ou personne passee dans une autre equipe. Ces heures COMPTENT dans `planned_hours` — la personne est bien sur le terrain — mais n'entrent ni dans `ratio` ni dans `wage_cost` : elles ne sont pas imputees a 0 $, elles ne sont pas imputees du tout. Affichee a part pour qu'une marge ainsi amelioree ne se lise jamais comme une marge gagnee sur le contrat.","default":0.0},"off_contract_agents_total":{"type":"integer","title":"Off Contract Agents Total","description":"Nb de personnes distinctes hors contrat planifiees sur le jour.","default":0},"avg_hourly_wage":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Avg Hourly Wage","description":"Taux horaire moyen pondere : mix des roles sur les heures A LA CHARGE DU CONTRAT (`wage_cost / (planned_hours - off_contract_hours)`). Le denominateur exclut les heures hors contrat, sinon le front — qui recalcule le cout en direct quand le superviseur edite — diluerait le taux et sous-estimerait le cout des heures restantes. Agrege, jamais individuel."},"agent_hourly_wage":{"type":"number","title":"Agent Hourly Wage","description":"Taux forfaitaire agent (prepose 098). Couvre aussi les temporaires, les quarts non assignes et tout poste tenu hors du perimetre Menzies."},"supervisor_hourly_wage":{"type":"number","title":"Supervisor Hourly Wage","description":"Taux forfaitaire superviseur (poste Evolia 033, tenu dans Menzies)."},"coordinator_hourly_wage":{"type":"number","title":"Coordinator Hourly Wage","description":"Taux forfaitaire coordonnateur (poste Evolia 045). 0 = non impute."},"revenue_per_chair":{"type":"number","title":"Revenue Per Chair","description":"Tarif facture par chaise servie (CA = chaises x tarif)."},"wage_charge_multiplier":{"type":"number","title":"Wage Charge Multiplier","description":"Multiplicateur de charges sociales (cout = masse x mult)."},"ca":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Ca","description":"C7 : chiffre d'affaires = chaises x tarif."},"loaded_cost":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Loaded Cost","description":"E7 : cout charge = masse salariale x charges."},"profit":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Profit","description":"C10 = C7 - E7 : resultat (marge en $)."},"margin_pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Margin Pct","description":"D10 = C10*100/C7 : marge en %."},"agents_total":{"type":"integer","title":"Agents Total","description":"Nb total d'intervenants planifies (distincts).","default":0},"supervisors_total":{"type":"integer","title":"Supervisors Total","description":"Nb de superviseurs (poste 033 dans Menzies) parmi eux. 0 si rh est injoignable : tout le monde est alors compte au taux agent.","default":0},"coordinators_total":{"type":"integer","title":"Coordinators Total","description":"Nb de coordonnateurs (poste 045 dans Menzies) parmi eux.","default":0}},"type":"object","required":["day","agent_hourly_wage","supervisor_hourly_wage","coordinator_hourly_wage","revenue_per_chair","wage_charge_multiplier"],"title":"RentabilityResponse","description":"Rentabilite du planning du jour (modele FACTURE A LA CHAISE).\n\nReproduit la feuille SIMULATION OPSIS : CA (chaises x tarif) moins le cout\ncharge (masse salariale x charges).\n\nLa masse salariale est calculee au taux FORFAITAIRE par role (`agent_hourly_wage` /\n`supervisor_hourly_wage`) et non plus au taux Evolia, incomplet et souvent a 0 $.\nToute heure planifiee a donc un cout : la rentabilite est toujours calculable (plus\nd'agent « sans salaire », plus de valeur masquee). Seuls des agregats sortent d'ici :\nle cout total et le taux moyen pondere (`avg_hourly_wage`), ce dernier permettant au\nfront de recalculer la rentabilite en direct quand le superviseur modifie les heures."},"RentabilityTotals":{"properties":{"days_clocked":{"type":"integer","title":"Days Clocked","default":0},"days_estimated":{"type":"integer","title":"Days Estimated","default":0},"days_unavailable":{"type":"integer","title":"Days Unavailable","default":0},"days_read_error":{"type":"integer","title":"Days Read Error","default":0},"days_without_revenue":{"type":"integer","title":"Days Without Revenue","default":0},"score_avg":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Score Avg","description":"Heures Evolia totales / chaises reelles totales, jours realises."},"menzies_chairs":{"type":"integer","title":"Menzies Chairs","default":0},"corrected_chairs":{"type":"integer","title":"Corrected Chairs","default":0},"real_chairs":{"type":"integer","title":"Real Chairs","default":0},"planned_hours_db":{"type":"number","title":"Planned Hours Db","default":0.0},"evolia_hours":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Evolia Hours"},"evolia_hours_coordinator":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Evolia Hours Coordinator","description":"Part de `evolia_hours` tenue au poste de coordonnateur, sur les jours realises. INCLUSE dans `evolia_hours`."},"hours_delta":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Hours Delta"},"plan_profit":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Plan Profit","description":"Somme sur les jours PLANIFIES (`days_planned`)."},"real_ca":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Real Ca","description":"CA reel cumule sur les jours realises — denominateur de `real_margin_pct`."},"real_profit":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Real Profit","description":"Somme sur les jours realises."},"ecart_profit":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Ecart Profit","description":"real_profit - plan_profit (jours realises)."},"plan_margin_pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Plan Margin Pct"},"real_margin_pct":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Real Margin Pct"},"days_realized":{"type":"integer","title":"Days Realized","description":"Nombre de jours passes/en cours avec un reel calculable.","default":0}},"type":"object","title":"RentabilityTotals","description":"Agregats sur toute la plage demandee (mois).\n\nVolumes (chaises, heures) sommes sur TOUTE la plage. `plan_profit`/`real_profit`/\n`ecart_profit`/marges ne portent que sur les jours REALISES (`days_realized`) :\nun jour futur n'a pas de reel a comparer, l'inclure fausserait la moyenne."},"ReplanningProposal":{"properties":{"shift_confirmation_id":{"type":"string","title":"Shift Confirmation Id","description":"Cle naturelle : id rh.shift_confirmations declencheur."},"rh_shift_id":{"type":"string","title":"Rh Shift Id","description":"id rh.shifts du quart concerne."},"external_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"External Id","description":"external_id Evolia (pir-ops-{version}-{shift})."},"trigger_status":{"type":"string","enum":["declined","no_response"],"title":"Trigger Status","description":"Reponse de confirmation ayant declenche la proposition."},"declined_member_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Declined Member Code","description":"Code de l'agent ayant refuse / non repondu."},"declined_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Declined Name"},"shift_start_local":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Shift Start Local"},"shift_end_local":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Shift End Local"},"location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Location"},"suggested":{"anyOf":[{"$ref":"#/components/schemas/AvailableAgent"},{"type":"null"}],"description":"Meilleur remplacant disponible. En DERNIER RECOURS seulement (aucun disponible prouve sur le creneau) : un candidat « Disponibilité non déclarée », `is_available=False` -> badge d'avertissement cote front. None si aucun candidat."},"alternatives":{"items":{"$ref":"#/components/schemas/AvailableAgent"},"type":"array","title":"Alternatives","description":"Autres candidats (override manuel), top N suivants du MEME rang que `suggested` : jamais un melange de disponibles et de non declares."},"available_count":{"type":"integer","title":"Available Count","description":"Nombre de candidats DISPONIBLES PROUVES sur le creneau (hors agent declinant) : premier rang seul, hors « disponibilité non déclarée ». 0 avec un `candidate_count` non nul => la liste est un repli.","default":0},"candidate_count":{"type":"integer","title":"Candidate Count","description":"Nombre de candidats reellement proposables, avant troncature a suggested + alternatives : les disponibles prouves, ou — a defaut — les « dispo non déclarée ». Le front en deduit le compteur « X autres » et va chercher la liste complete a la demande (GET /coordinator/available-agents).","default":0},"reliability_source_ok":{"type":"boolean","title":"Reliability Source Ok","description":"True si la fiabilite a pondere le tri des candidats.","default":false},"application":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Application","description":"Resultat d'une application AUTOMATIQUE (offre acceptee) : status, member_code, evolia_outcome, evolia_error, evolia_is_draft, applied_at (instant de l'application par la machine), plus offered_by_name / offered_at (le GESTE HUMAIN : qui a valide le candidat et quand — None pour les offres anterieures au journal `replacement_offers`). None si la proposition est encore ouverte.\n\nETAT DE PUBLICATION — trois champs, a lire ensemble : `etat_publication` (`publiee` | `brouillon` | `disparue` | `inconnue`, ou None si la lecture a echoue), et le dernier relevé qui l'etablit, `publication_relevee_le` + `publication_brouillon_au_releve`. C'est `etat_publication` qui pilote l'affichage, JAMAIS `evolia_is_draft` : ce dernier est fige a l'application et n'est jamais relu — il annoncait « a publier » sur 22 quarts publies depuis (mesure du 2026-08-14). Sur `disparue`, le relevé garde son interet : il dit si la reaffectation avait pris avant que l'event ne s'evapore."}},"type":"object","required":["shift_confirmation_id","rh_shift_id","trigger_status"],"title":"ReplanningProposal","description":"Une proposition de replanification calculee pour un quart a risque."},"ReplanningProposalRef":{"properties":{"shift_confirmation_id":{"type":"string","title":"Shift Confirmation Id"},"rh_shift_id":{"type":"string","title":"Rh Shift Id"},"external_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"External Id"},"trigger_status":{"type":"string","enum":["declined","no_response"],"title":"Trigger Status"},"declined_member_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Declined Member Code"},"declined_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Declined Name"},"shift_start_local":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Shift Start Local"},"shift_end_local":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Shift End Local"},"location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Location"},"suggested_member_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Suggested Member Code"},"suggested_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Suggested Name"},"reliability_score":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Reliability Score"},"reliability_source_ok":{"type":"boolean","title":"Reliability Source Ok","default":false}},"type":"object","required":["shift_confirmation_id","rh_shift_id","trigger_status"],"title":"ReplanningProposalRef","description":"Identifiants + snapshot d'une proposition, envoyes lors d'une decision.\n\nLe front possede deja la proposition calculee : il la renvoie telle quelle pour\ntracer la decision (audit) sans recalcul serveur."},"ReplanningProposalsResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ReplanningProposal"},"type":"array","title":"Items"},"from":{"type":"string","format":"date","title":"From"},"to":{"type":"string","format":"date","title":"To"},"source_ok":{"type":"boolean","title":"Source Ok","description":"False si rh (confirmations) est injoignable : afficher une erreur.","default":true},"reliability_source_ok":{"type":"boolean","title":"Reliability Source Ok","default":false},"auto_replanning_enabled":{"type":"boolean","title":"Auto Replanning Enabled","description":"Toujours true : les suggestions sont permanentes (champ conserve pour compat).","default":true}},"type":"object","required":["from","to"],"title":"ReplanningProposalsResponse","description":"Propositions ouvertes sur la plage + drapeaux de source et d'activation."},"RosterActivityResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/RosterAgentOut"},"type":"array","title":"Items"},"source_ok":{"type":"boolean","title":"Source Ok","description":"False si `rh` est injoignable (droits/connexion).","default":true},"stale_after_days":{"type":"integer","title":"Stale After Days","description":"Seuil d'inactivite applique (le front ne le code jamais en dur)."}},"type":"object","required":["stale_after_days"],"title":"RosterActivityResponse","description":"Roster planifiable (departs declares exclus), du plus inactif au plus recent."},"RosterAgentOut":{"properties":{"member_code":{"type":"string","title":"Member Code","description":"Code agent Evolia."},"name":{"type":"string","title":"Name","description":"Nom rh, ou le code si l'employe est introuvable."},"last_pmr_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Last Pmr At","description":"Debut du dernier quart PMR Menzies connu (rh.time_entries), quarts a venir compris. None si aucun."},"days_since_pmr":{"anyOf":[{"type":"integer"},{"type":"null"}],"title":"Days Since Pmr","description":"Jours depuis ce quart (0 si a venir). None si aucun pointage."},"is_stale":{"type":"boolean","title":"Is Stale","description":"Inactivite au-dela du seuil : signale, jamais retire du roster.","default":false}},"type":"object","required":["member_code","name"],"title":"RosterAgentOut","description":"Un agent du roster de planification et son activite PMR recente."},"RuleLogEntry":{"properties":{"id":{"type":"integer","title":"Id"},"key":{"type":"string","title":"Key"},"action":{"type":"string","title":"Action"},"old_value":{"title":"Old Value"},"new_value":{"title":"New Value"},"changed_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Changed By"},"changed_at":{"type":"string","format":"date-time","title":"Changed At"}},"type":"object","required":["id","key","action","changed_at"],"title":"RuleLogEntry"},"RuleUpdate":{"properties":{"value":{"title":"Value"}},"type":"object","required":["value"],"title":"RuleUpdate"},"RuleView":{"properties":{"key":{"type":"string","title":"Key"},"categorie":{"type":"string","title":"Categorie"},"statut":{"type":"string","title":"Statut"},"type":{"type":"string","title":"Type"},"unite":{"type":"string","title":"Unite"},"libelle":{"type":"string","title":"Libelle"},"description_metier":{"type":"string","title":"Description Metier"},"bornes":{"anyOf":[{"additionalProperties":{"type":"number"},"type":"object"},{"type":"null"}],"title":"Bornes"},"contraintes_croisees":{"items":{"type":"string"},"type":"array","title":"Contraintes Croisees","default":[]},"defaut":{"title":"Defaut"},"valeur_effective":{"title":"Valeur Effective"},"surchargee":{"type":"boolean","title":"Surchargee","default":false},"updated_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Updated By"},"updated_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Updated At"}},"type":"object","required":["key","categorie","statut","type","unite","libelle","description_metier"],"title":"RuleView"},"SavePlanningRequest":{"properties":{"scenario_name":{"type":"string","title":"Scenario Name","description":"Scenario d'origine (safe / balanced / low_cost)."},"planning_day":{"type":"string","format":"date","title":"Planning Day","description":"Jour cible du planning sauvegarde."},"shifts":{"items":{"$ref":"#/components/schemas/Shift"},"type":"array","title":"Shifts","description":"Quarts dans leur etat final."},"deleted_ids":{"items":{"type":"string"},"type":"array","title":"Deleted Ids","description":"Ids de quarts supprimes par le superviseur."},"deleted":{"items":{"$ref":"#/components/schemas/DeletedShift"},"type":"array","title":"Deleted"},"notes":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Notes","description":"Note libre du superviseur."},"feedback_type":{"type":"string","enum":["confirm","modify","comment","request_correction","validate_with_remarks","reject"],"title":"Feedback Type","description":"Type de retour associe a la sauvegarde (modify par defaut).","default":"modify"},"comment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comment","description":"Commentaire superviseur associe au retour (boucle de validation)."},"applied_suggestion_ids":{"items":{"type":"string","format":"uuid"},"type":"array","title":"Applied Suggestion Ids","description":"Suggestions d'optimisation appliquees dans cette sauvegarde : elles passent au statut `applied` et sont liees a la version creee (meme transaction, tracabilite complete)."}},"type":"object","required":["scenario_name","planning_day","shifts"],"title":"SavePlanningRequest"},"ScanImageRetentionIn":{"properties":{"days":{"type":"integer","title":"Days"}},"type":"object","required":["days"],"title":"ScanImageRetentionIn"},"ScanImageRetentionOut":{"properties":{"days":{"type":"integer","title":"Days"}},"type":"object","required":["days"],"title":"ScanImageRetentionOut","description":"Retention des preuves image du scan, en jours (0 = pas de stockage)."},"Scenario":{"properties":{"name":{"type":"string","title":"Name","description":"Identifiant interne (safe / balanced / low_cost)."},"label":{"type":"string","title":"Label","description":"Libelle affiche au superviseur."},"description":{"type":"string","title":"Description"},"planning_day":{"anyOf":[{"type":"string","format":"date"},{"type":"null"}],"title":"Planning Day","description":"Jour cible du planning (auto-detecte depuis les shifts)."},"metrics":{"$ref":"#/components/schemas/ScenarioMetrics"}},"type":"object","required":["name","label","description","metrics"],"title":"Scenario"},"ScenarioMetrics":{"properties":{"total_shifts":{"type":"integer","title":"Total Shifts"},"total_hours":{"type":"number","title":"Total Hours"},"night_shifts":{"type":"integer","title":"Night Shifts"},"cross_midnight_shifts":{"type":"integer","title":"Cross Midnight Shifts"},"high_or_critical_risk_buckets":{"type":"integer","title":"High Or Critical Risk Buckets"},"c_assistance_buckets":{"type":"integer","title":"C Assistance Buckets"}},"type":"object","required":["total_shifts","total_hours","night_shifts","cross_midnight_shifts","high_or_critical_risk_buckets","c_assistance_buckets"],"title":"ScenarioMetrics","description":"Metriques fiables agregeables a partir du seul planning recommande.\n\nVolontairement, les indicateurs de couverture / sous-staffing / sur-staffing\nsont exclus : ils ne sont pas fiables sans confrontation a la demande reelle\n(roadmap §5.3)."},"ScenariosResponse":{"properties":{"scenarios":{"items":{"$ref":"#/components/schemas/Scenario"},"type":"array","title":"Scenarios"}},"type":"object","required":["scenarios"],"title":"ScenariosResponse"},"ScheduleStatusResponse":{"properties":{"month":{"type":"string","title":"Month","description":"Mois interroge (YYYY-MM)."},"vols":{"type":"integer","title":"Vols","description":"Nombre de vols planifies pour ce mois."},"derniere_ecriture":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Derniere Ecriture","description":"Horodatage du dernier import pour ce mois (UTC)."}},"type":"object","required":["month","vols"],"title":"ScheduleStatusResponse","description":"Etat de `flight_schedule` pour un mois donne (bouton « Verifier »)."},"ScheduleUploadRequest":{"properties":{"filename":{"type":"string","title":"Filename","description":"Nom du fichier (doit se terminer par .xlsx)."},"content_base64":{"type":"string","title":"Content Base64","description":"Contenu du fichier `.xlsx` encode en base64."}},"type":"object","required":["filename","content_base64"],"title":"ScheduleUploadRequest","description":"Upload du planning mensuel (PRM Schedule) en base64 (pas de date : mensuel)."},"ScheduleUploadResponse":{"properties":{"status":{"type":"string","title":"Status","default":"accepted"},"object_path":{"type":"string","title":"Object Path","description":"Chemin du fichier depose dans le bucket Storage."}},"type":"object","required":["object_path"],"title":"ScheduleUploadResponse"},"Shift":{"properties":{"scheduled_shift_id":{"type":"string","title":"Scheduled Shift Id"},"shift_start_local":{"type":"string","format":"date-time","title":"Shift Start Local"},"shift_end_local":{"type":"string","format":"date-time","title":"Shift End Local"},"duration_hours":{"type":"number","title":"Duration Hours"},"crosses_midnight":{"type":"boolean","title":"Crosses Midnight"},"is_night_shift":{"type":"boolean","title":"Is Night Shift"},"high_or_critical_risk_bucket_count":{"type":"integer","title":"High Or Critical Risk Bucket Count"},"c_assistance_bucket_count":{"type":"integer","title":"C Assistance Bucket Count"},"has_risk_exception":{"type":"boolean","title":"Has Risk Exception"},"utilization_hint":{"type":"number","title":"Utilization Hint"},"recommendation":{"type":"string","enum":["OK","Suggestion","A verifier","Critique"],"title":"Recommendation","description":"Niveau de recommandation (roadmap §12 : OK / Suggestion / A verifier / Critique)."},"recommendation_reasons":{"items":{"type":"string"},"type":"array","title":"Recommendation Reasons","description":"Raisons humainement lisibles qui justifient le niveau."},"is_new":{"type":"boolean","title":"Is New","description":"Quart cree par le superviseur (absent du scenario d'origine).","default":false},"is_context":{"type":"boolean","title":"Is Context","description":"Quart de CONTEXTE : il appartient au jour PRECEDENT (bande 22h -> minuit) et n'est affiche sur ce jour que pour donner la continuite de la nuit. LECTURE SEULE : jamais editable, jamais enregistre dans le snapshot, jamais pousse vers Evolia, jamais compte dans les KPI du jour. Le defaut False est indispensable : les snapshots `planning_versions` deja figes n'ont pas la cle.","default":false},"is_reinforcement":{"type":"boolean","title":"Is Reinforcement","description":"Quart tenu par un RENFORT : un coordonnateur deja facture sur un autre contrat, substitue a un agent. Ses heures SORTENT du score h/chaise comme du cout (imputees au taux `coordinator` de /equipe, 0 $ par defaut) : elles sont facturees ailleurs. Le quart n'est JAMAIS pousse vers Evolia ni sollicite par une confirmation SMS. DERIVE : toujours recalcule cote serveur depuis `agent_id` (cf. `scenario_loader._tag_reinforcements`), jamais lu depuis le client — sinon un appelant pourrait decreter qu'un quart ne coute rien. Le defaut False est indispensable : les snapshots `planning_versions` deja figes n'ont pas la cle.","default":false},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note","description":"Note libre du superviseur, persistee avec la version."},"reason_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason Code","description":"Motif STRUCTURE de la derniere modification d'HORAIRE du quart (taxonomie de schemas/suggestions.py : sous_effectif, surcapacite, preparation_vol, ...). Alimente l'apprentissage des patterns superviseur."},"agent_reason_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agent Reason Code","description":"Motif STRUCTURE du changement d'AGENT, distinct de `reason_code`. Un meme quart peut etre retaille ET reaffecte dans la meme sauvegarde : sans champ dedie, la ligne `reassigned` du log heriterait du motif de l'horaire (« sous-effectif » attribue a une substitution). Defaut None : les snapshots `planning_versions` deja figes n'ont pas la cle."},"agent_reason_note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Agent Reason Note","description":"Precision libre accompagnant `agent_reason_code` (obligatoire si « autre »)."},"agent_id":{"type":"string","title":"Agent Id","description":"Identifiant de l'agent affecte au quart (synthetique en V1 : `agent_01`...)."},"agent_label":{"type":"string","title":"Agent Label","description":"Libelle court de l'agent affiche sur la timeline (`A-01`...)."}},"type":"object","required":["scheduled_shift_id","shift_start_local","shift_end_local","duration_hours","crosses_midnight","is_night_shift","high_or_critical_risk_bucket_count","c_assistance_bucket_count","has_risk_exception","utilization_hint","recommendation","agent_id","agent_label"],"title":"Shift"},"ShiftAdjustmentCreate":{"properties":{"shift_date":{"type":"string","format":"date","title":"Shift Date"},"member_code":{"type":"string","minLength":1,"title":"Member Code"},"actual_end":{"type":"string","format":"date-time","title":"Actual End"},"reason_code":{"type":"string","enum":["flight","late_relief","meeting","low_traffic","other"],"title":"Reason Code"},"flight_no":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Flight No"},"comment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comment"}},"type":"object","required":["shift_date","member_code","actual_end","reason_code"],"title":"ShiftAdjustmentCreate","description":"Saisie superviseur.\n\n`planned_end` est ABSENT a dessein : le serveur le resout depuis les agents\nplanifies du jour. Si l'heure planifiee etait declarative, la comparaison\nplanifie/reel ne prouverait plus rien. `direction` est derive, jamais recu."},"ShiftAdjustmentOut":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"shift_date":{"type":"string","format":"date","title":"Shift Date"},"member_code":{"type":"string","title":"Member Code"},"display_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Display Name"},"planned_end":{"type":"string","format":"date-time","title":"Planned End"},"actual_end":{"type":"string","format":"date-time","title":"Actual End"},"direction":{"type":"string","enum":["shortened","extended"],"title":"Direction"},"reason_code":{"type":"string","enum":["flight","late_relief","meeting","low_traffic","other"],"title":"Reason Code"},"flight_no":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Flight No"},"comment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comment"},"created_at":{"type":"string","format":"date-time","title":"Created At"},"created_by_email":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Created By Email"},"reported_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Reported At"},"reported_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reported By"},"rejected_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Rejected At"},"rejected_by":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rejected By"},"rejection_reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Rejection Reason"}},"type":"object","required":["id","shift_date","member_code","display_name","planned_end","actual_end","direction","reason_code","flight_no","comment","created_at","created_by_email","reported_at","reported_by"],"title":"ShiftAdjustmentOut"},"ShiftAdjustmentReject":{"properties":{"reason":{"type":"string","maxLength":500,"minLength":1,"title":"Reason"}},"type":"object","required":["reason"],"title":"ShiftAdjustmentReject","description":"Refus du coordonnateur. Le motif est OBLIGATOIRE et non vide.\n\nC'est la seule chose que le superviseur lira : sans lui, il ressaisit a\nl'identique et la ligne revient. `min_length` porte sur la chaine brute ; le\nservice refuse en plus une suite d'espaces."},"ShiftAdjustmentsResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ShiftAdjustmentOut"},"type":"array","title":"Items"},"pending_count":{"type":"integer","title":"Pending Count"}},"type":"object","required":["items","pending_count"],"title":"ShiftAdjustmentsResponse"},"ShiftBlock":{"properties":{"start":{"type":"string","format":"date-time","title":"Start"},"end":{"type":"string","format":"date-time","title":"End"}},"type":"object","required":["start","end"],"title":"ShiftBlock","description":"Bloc continu de quart (un quart fractionne en compte plusieurs)."},"ShiftConfirmationItem":{"properties":{"id":{"type":"string","title":"Id"},"shift_external_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Shift External Id"},"kind":{"type":"string","title":"Kind","description":"'confirmation' (titulaire) ou 'offer' (offre a un remplacant).","default":"confirmation"},"employee_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Employee Name"},"member_code":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Member Code","description":"Code Evolia du destinataire (rh.employees.code)."},"phone":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Phone"},"shift_start_local":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Shift Start Local"},"shift_end_local":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Shift End Local"},"location":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Location"},"channel":{"type":"string","title":"Channel","default":"sms"},"status":{"type":"string","title":"Status"},"attempts":{"type":"integer","title":"Attempts","default":0},"response_text":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Response Text"},"sent_body":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Sent Body","description":"Corps reellement envoye (SMS : texte final ; voice : script). None si anterieur a l'archivage ORION-RH."},"requested_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Requested At"},"sent_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Sent At"},"responded_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Responded At"},"closed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Closed At"},"error_detail":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Error Detail"},"is_draft":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Is Draft","description":"Statut Evolia du quart (miroir `rh.shifts`) : True = brouillon, donc INVISIBLE pour l'agent. Permet de repérer les quarts confirmés restés à publier. None si la colonne n'a pas pu être lue."}},"type":"object","required":["id","status"],"title":"ShiftConfirmationItem"},"ShiftConfirmationsResponse":{"properties":{"items":{"items":{"$ref":"#/components/schemas/ShiftConfirmationItem"},"type":"array","title":"Items"},"from_date":{"type":"string","format":"date","title":"From Date"},"to_date":{"type":"string","format":"date","title":"To Date"},"source_ok":{"type":"boolean","title":"Source Ok","description":"False si le schema rh (Supabase) est injoignable : items vide, pas d'erreur 500."}},"type":"object","required":["items","from_date","to_date","source_ok"],"title":"ShiftConfirmationsResponse"},"ShiftInput":{"properties":{"scheduled_shift_id":{"type":"string","title":"Scheduled Shift Id"},"shift_start_local":{"type":"string","format":"date-time","title":"Shift Start Local"},"shift_end_local":{"type":"string","format":"date-time","title":"Shift End Local"},"note":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Note"},"is_new":{"type":"boolean","title":"Is New","description":"True si le quart est cree par le superviseur (id absent du scenario).","default":false}},"type":"object","required":["scheduled_shift_id","shift_start_local","shift_end_local"],"title":"ShiftInput"},"ShiftsResponse":{"properties":{"scenario_name":{"type":"string","title":"Scenario Name"},"window":{"$ref":"#/components/schemas/api__schemas__scenarios__ShiftWindow"},"shifts":{"items":{"$ref":"#/components/schemas/Shift"},"type":"array","title":"Shifts"}},"type":"object","required":["scenario_name","window","shifts"],"title":"ShiftsResponse"},"StudioCampaignResponse":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"label":{"type":"string","title":"Label"},"status":{"type":"string","title":"Status"},"total_days":{"type":"integer","title":"Total Days"},"completed_days":{"type":"integer","title":"Completed Days"},"days":{"items":{"$ref":"#/components/schemas/StudioDaySummary"},"type":"array","title":"Days"}},"type":"object","required":["id","label","status","total_days","completed_days","days"],"title":"StudioCampaignResponse"},"StudioDayDetail":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"position":{"type":"integer","title":"Position"},"display_label":{"type":"string","title":"Display Label"},"charge_class":{"type":"string","title":"Charge Class"},"payload":{"additionalProperties":true,"type":"object","title":"Payload"},"volume_decision":{"anyOf":[{"type":"string","enum":["valide","valide_ajuste","refuse"]},{"type":"null"}],"title":"Volume Decision"},"forme_decision":{"anyOf":[{"type":"string","enum":["valide","valide_ajuste","refuse"]},{"type":"null"}],"title":"Forme Decision"},"forme_unlocked":{"type":"boolean","title":"Forme Unlocked","default":false},"validated_volume_hours":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Validated Volume Hours"},"hourly_rate":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Hourly Rate"},"revenue_per_chair":{"anyOf":[{"type":"number"},{"type":"null"}],"title":"Revenue Per Chair"}},"type":"object","required":["id","position","display_label","charge_class","payload"],"title":"StudioDayDetail","description":"Détail d'une journée : uniquement le payload PUBLIC du rejeu."},"StudioDaySummary":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"position":{"type":"integer","title":"Position"},"display_label":{"type":"string","title":"Display Label"},"charge_class":{"type":"string","title":"Charge Class"},"volume_decision":{"anyOf":[{"type":"string","enum":["valide","valide_ajuste","refuse"]},{"type":"null"}],"title":"Volume Decision"},"forme_decision":{"anyOf":[{"type":"string","enum":["valide","valide_ajuste","refuse"]},{"type":"null"}],"title":"Forme Decision"},"forme_unlocked":{"type":"boolean","title":"Forme Unlocked","default":false},"completed":{"type":"boolean","title":"Completed","default":false}},"type":"object","required":["id","position","display_label","charge_class"],"title":"StudioDaySummary","description":"Une journée de la campagne, vue liste (aucune date réelle)."},"StudioDecisionRequest":{"properties":{"decision":{"type":"string","enum":["valide","valide_ajuste","refuse"],"title":"Decision"},"payload":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Payload"},"reason":{"anyOf":[{"type":"string","maxLength":500},{"type":"null"}],"title":"Reason"},"comment":{"anyOf":[{"type":"string","maxLength":2000},{"type":"null"}],"title":"Comment"},"seconds_spent":{"anyOf":[{"type":"integer","minimum":0.0},{"type":"null"}],"title":"Seconds Spent"}},"type":"object","required":["decision"],"title":"StudioDecisionRequest"},"StudioDecisionResponse":{"properties":{"day_id":{"type":"string","format":"uuid","title":"Day Id"},"level":{"type":"string","enum":["volume","forme"],"title":"Level"},"decision":{"type":"string","enum":["valide","valide_ajuste","refuse"],"title":"Decision"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["day_id","level","decision","created_at"],"title":"StudioDecisionResponse"},"SuggestionEntry":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"planning_day":{"type":"string","format":"date","title":"Planning Day"},"scenario_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scenario Name"},"suggestion_type":{"type":"string","enum":["add_shift","extend_shift","reduce_shift","remove_shift","move_shift","reassign_agent","convert_standby"],"title":"Suggestion Type"},"target_shift_id":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Target Shift Id"},"time_window_start":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Time Window Start"},"time_window_end":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Time Window End"},"proposed_shift_data":{"anyOf":[{"$ref":"#/components/schemas/ProposedShiftChange"},{"type":"null"}]},"reason":{"type":"string","title":"Reason","description":"Justification lisible (francais)."},"evidence":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Evidence","description":"Donnees sources : occurrences historiques, charge, couverture..."},"confidence_score":{"type":"number","title":"Confidence Score"},"confidence_label":{"type":"string","enum":["elevee","moyenne","faible"],"title":"Confidence Label"},"recommendation_level":{"type":"string","enum":["recommande","suggere","a_examiner","non_recommande"],"title":"Recommendation Level"},"status":{"type":"string","enum":["pending","accepted","modified","rejected","deferred","expired","applied"],"title":"Status"},"reviewed_by":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Reviewed By"},"reviewed_at":{"anyOf":[{"type":"string","format":"date-time"},{"type":"null"}],"title":"Reviewed At"},"review_reason_code":{"anyOf":[{"type":"string","enum":["sous_effectif","surcapacite","absence","pic_vols","preparation_vol","hausse_pmr","baisse_pmr","competence_manquante","changement_gate","changement_operationnel","correction_planning","preference_recurrente","donnees_incorrectes","besoin_terrain_non_detecte","contrainte_rh","besoin_de_reserve","evenement_special","substitution_renfort","autre"]},{"type":"null"}],"title":"Review Reason Code"},"review_comment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Review Comment"},"outcome":{"anyOf":[{"type":"string","enum":["correcte","sous_estimation","surestimation","indeterminee"]},{"type":"null"}],"title":"Outcome"},"outcome_details":{"anyOf":[{"additionalProperties":true,"type":"object"},{"type":"null"}],"title":"Outcome Details"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","planning_day","scenario_name","suggestion_type","target_shift_id","time_window_start","time_window_end","proposed_shift_data","reason","confidence_score","confidence_label","recommendation_level","status","created_at"],"title":"SuggestionEntry"},"SuggestionReviewRequest":{"properties":{"action":{"type":"string","enum":["accept","modify","reject","defer"],"title":"Action"},"reason_code":{"anyOf":[{"type":"string","enum":["sous_effectif","surcapacite","absence","pic_vols","preparation_vol","hausse_pmr","baisse_pmr","competence_manquante","changement_gate","changement_operationnel","correction_planning","preference_recurrente","donnees_incorrectes","besoin_terrain_non_detecte","contrainte_rh","besoin_de_reserve","evenement_special","substitution_renfort","autre"]},{"type":"null"}],"title":"Reason Code","description":"Motif structure — requis pour reject et modify."},"comment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comment","description":"Commentaire libre."},"final_change":{"anyOf":[{"$ref":"#/components/schemas/ProposedShiftChange"},{"type":"null"}],"description":"Changement reellement retenu quand action=modify."}},"type":"object","required":["action"],"title":"SuggestionReviewRequest"},"SuggestionsResponse":{"properties":{"planning_day":{"type":"string","format":"date","title":"Planning Day"},"scenario_name":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Scenario Name"},"generated_at":{"type":"string","format":"date-time","title":"Generated At"},"requirements_source":{"type":"string","enum":["staffing_requirements","approximation","aucune"],"title":"Requirements Source","description":"Source du besoin : table persistee, approximation productivite, ou aucune donnee."},"coverage":{"items":{"$ref":"#/components/schemas/CoverageBucket"},"type":"array","title":"Coverage"},"suggestions":{"items":{"$ref":"#/components/schemas/SuggestionEntry"},"type":"array","title":"Suggestions"},"reference_days":{"items":{"$ref":"#/components/schemas/ReferenceDaySummary"},"type":"array","title":"Reference Days","description":"Memes jours de semaine precedents utilises comme reference."}},"type":"object","required":["planning_day","scenario_name","generated_at","requirements_source"],"title":"SuggestionsResponse"},"SupervisorFeedbackEntry":{"properties":{"id":{"type":"string","format":"uuid","title":"Id"},"scenario_name":{"type":"string","title":"Scenario Name"},"planning_day":{"type":"string","format":"date","title":"Planning Day"},"planning_version_id":{"anyOf":[{"type":"string","format":"uuid"},{"type":"null"}],"title":"Planning Version Id"},"status":{"type":"string","enum":["propose","en_attente","valide","modifie","refuse","a_corriger"],"title":"Status"},"feedback_type":{"type":"string","enum":["confirm","modify","comment","request_correction","validate_with_remarks","reject"],"title":"Feedback Type"},"comment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comment"},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason"},"created_by":{"type":"string","format":"uuid","title":"Created By"},"created_at":{"type":"string","format":"date-time","title":"Created At"}},"type":"object","required":["id","scenario_name","planning_day","planning_version_id","status","feedback_type","comment","reason","created_by","created_at"],"title":"SupervisorFeedbackEntry"},"SupervisorFeedbackRequest":{"properties":{"scenario_name":{"type":"string","title":"Scenario Name","description":"Scenario propose concerne."},"planning_day":{"type":"string","format":"date","title":"Planning Day","description":"Jour cible du planning."},"feedback_type":{"type":"string","enum":["confirm","modify","comment","request_correction","validate_with_remarks","reject"],"title":"Feedback Type","description":"comment | request_correction | reject (un snapshot n'est PAS cree)."},"comment":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Comment","description":"Commentaire libre du superviseur."},"reason":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Reason","description":"Motif d'un refus ou d'une demande de correction."}},"type":"object","required":["scenario_name","planning_day","feedback_type"],"title":"SupervisorFeedbackRequest","description":"Retour superviseur sans snapshot : commentaire, refus, demande de correction."},"SupervisorPatternEntry":{"properties":{"kind":{"type":"string","enum":["add","remove"],"title":"Kind","description":"add = renfort recurrent ; remove = retrait recurrent."},"window_start":{"type":"string","format":"date-time","title":"Window Start"},"window_end":{"type":"string","format":"date-time","title":"Window End"},"pattern":{"type":"string","title":"Pattern","description":"Description lisible (« Sur 3 des 4 derniers lundis, ... »)."},"frequency":{"type":"string","title":"Frequency","description":"Frequence (« 3 des 4 derniers lundis »)."},"occurrences":{"type":"integer","title":"Occurrences"},"history_size":{"type":"integer","title":"History Size","description":"Jours de reference AVEC version publiee."},"impact_agents":{"type":"number","title":"Impact Agents","description":"Ampleur moyenne, signee (+renfort / -retrait), en agents."},"confidence_score":{"type":"number","title":"Confidence Score"},"confidence_label":{"type":"string","enum":["elevee","moyenne","faible"],"title":"Confidence Label"},"load_similar":{"anyOf":[{"type":"boolean"},{"type":"null"}],"title":"Load Similar","description":"Charge de J similaire aux jours de reference (None = inconnue)."},"suggestion":{"type":"string","title":"Suggestion","description":"Action future proposee, bornee par le seuil de rentabilite 1.15."},"reference_days":{"items":{"type":"string","format":"date"},"type":"array","title":"Reference Days"},"supervisor_notes":{"items":{"type":"string"},"type":"array","title":"Supervisor Notes"},"reason_codes":{"additionalProperties":{"type":"integer"},"type":"object","title":"Reason Codes","description":"Motifs structures rencontres sur ces corrections (taxonomie)."}},"type":"object","required":["kind","window_start","window_end","pattern","frequency","occurrences","history_size","impact_agents","confidence_score","confidence_label","suggestion"],"title":"SupervisorPatternEntry","description":"Pattern recurrent des corrections superviseur, au format rapport explicable."},"SupervisorPatternsResponse":{"properties":{"planning_day":{"type":"string","format":"date","title":"Planning Day"},"weekday_label":{"type":"string","title":"Weekday Label","description":"Jour de semaine analyse (francais)."},"history_window_weeks":{"type":"integer","title":"History Window Weeks","description":"Profondeur reellement utilisee (4 a 8 semaines, adaptative)."},"patterns":{"items":{"$ref":"#/components/schemas/SupervisorPatternEntry"},"type":"array","title":"Patterns"}},"type":"object","required":["planning_day","weekday_label","history_window_weeks"],"title":"SupervisorPatternsResponse"},"ValidateRequest":{"properties":{"scenario_name":{"type":"string","title":"Scenario Name","description":"Scenario d'origine (safe / balanced / low_cost)."},"shifts":{"items":{"$ref":"#/components/schemas/ShiftInput"},"type":"array","title":"Shifts","description":"Liste des quarts modifies (ids existants dans le scenario)."}},"type":"object","required":["scenario_name","shifts"],"title":"ValidateRequest"},"ValidateResponse":{"properties":{"scenario_name":{"type":"string","title":"Scenario Name"},"shifts":{"items":{"$ref":"#/components/schemas/Shift"},"type":"array","title":"Shifts","description":"Quarts modifies, enrichis avec recommandation + raisons recalculees."},"errors":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Errors","description":"Erreurs bloquantes (la sauvegarde sera refusee tant qu'il en reste)."}},"type":"object","required":["scenario_name","shifts"],"title":"ValidateResponse"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"WageRatesIn":{"properties":{"agent":{"type":"number","minimum":0.0,"title":"Agent","description":"Prepose (098). S'applique aussi aux agents temporaires, aux quarts non assignes et a tout poste tenu hors du perimetre Menzies."},"supervisor":{"type":"number","minimum":0.0,"title":"Supervisor","description":"Superviseur (poste Evolia 033)."},"coordinator":{"type":"number","minimum":0.0,"title":"Coordinator","description":"Coordonnateur (poste Evolia 045). 0 = heures non imputees au cout."}},"type":"object","required":["agent","supervisor","coordinator"],"title":"WageRatesIn","description":"Taux horaires soumis par /equipe (CAD, >= 0)."},"WageRatesOut":{"properties":{"agent":{"type":"number","minimum":0.0,"title":"Agent","description":"Prepose (098). S'applique aussi aux agents temporaires, aux quarts non assignes et a tout poste tenu hors du perimetre Menzies."},"supervisor":{"type":"number","minimum":0.0,"title":"Supervisor","description":"Superviseur (poste Evolia 033)."},"coordinator":{"type":"number","minimum":0.0,"title":"Coordinator","description":"Coordonnateur (poste Evolia 045). 0 = heures non imputees au cout."},"source_ok":{"type":"boolean","title":"Source Ok","description":"False si les reglages sont injoignables : les valeurs par defaut (19 / 21 / 0) sont alors appliquees.","default":true}},"type":"object","required":["agent","supervisor","coordinator"],"title":"WageRatesOut","description":"Taux en vigueur, tels qu'appliques a la masse salariale."},"api__schemas__agent_flights__ShiftWindow":{"properties":{"start":{"type":"string","format":"date-time","title":"Start"},"end":{"type":"string","format":"date-time","title":"End"},"source":{"type":"string","enum":["roster","evolia","planning"],"title":"Source"}},"type":"object","required":["start","end","source"],"title":"ShiftWindow","description":"Enveloppe du quart (debut le plus tot, fin la plus tard) pour l'affichage."},"api__schemas__scenarios__ShiftWindow":{"properties":{"planning_day":{"type":"string","format":"date","title":"Planning Day"},"window_start_local":{"type":"string","format":"date-time","title":"Window Start Local"},"window_end_local":{"type":"string","format":"date-time","title":"Window End Local"}},"type":"object","required":["planning_day","window_start_local","window_end_local"],"title":"ShiftWindow","description":"Fenetre operationnelle d'affichage : planning_day - 1 22h → planning_day + 1 02h (Montreal)."}}}}