Cause: les FK servers.responsable_domaine_contact_id / referent_technique_contact_id
n'etaient pas remplies dans la DB SANEF actuelle, mais les emails sont presents
dans les champs legacy texte servers.responsable_email / referent_email.
Solution: _fetch_pct_cc_emails() cherche maintenant en 2 etapes:
1. Via FK -> contacts.email (si modele moderne renseigne)
2. Via champs legacy texte sur servers (responsable_email/referent_email)
Dedoublonne par email lowercase. Garde le nom (legacy: responsable_nom/referent_nom,
moderne: contacts.name).
- Service mail_service.py: send_html_mail via SMTP standard (host/port/user/pass/from/use_tls
depuis Settings > SMTP). Gere SSL_465 et STARTTLS_587. Mode dry_run pour preview.
- Settings: nouvelle section 'smtp' avec smtp_host/port/user/pass/from/use_tls/pct_recipient
(a configurer pour O365 SMTP submission)
- Router planning_import.py:
* _build_pct_email(): construit subject + HTML pro/colore (header bleu degrade SANEF,
cards avec border-left bleu/orange, tableau serveurs, footer)
* Subject: 'Intervention sur <app>' si app uniforme, sinon liste des serveurs
* Plage horaire = 20 min × N serveurs (formattee Hh MM)
* 'Moyen d'exploitation prevu : Rollback en cas de probleme' ajoute en bas
* _fetch_pct_cc_emails(): query distinct contacts depuis responsable_domaine_contact_id
+ referent_technique_contact_id + server_additional_referents
* Endpoint POST /patching/import/pct-prevenance/preview retourne {subject, html, to, cc,
smtp_configured, row_count} sans envoyer
* Endpoint POST /patching/import/pct-prevenance/send envoie reellement, audit log,
update pct_mail_sent_at sur les rows
- Template patching_import.html:
* Bouton 'Prevenance PCT' (violet) a cote des autres actions
* Modal preview avec iframe sandboxe pour le rendu HTML mail
* Affiche destinataires, CC, objet, count serveurs
* Warning rouge si SMTP non configure (envoi desactive, preview seulement)
* 2 boutons: Annuler / Envoyer (avec confirmation)
Probleme initial: nom snap base sur 'intervenant' (champ libre Excel modifiable) -> peu fiable
pour identifier qui a cree le snap. De plus, sans heure dans le nom, collisions si meme
serveur patche 2x dans la journee.
Solution:
- Format snap PatchCenter v2: <user_jwt>_YYYY-MM-DD_HH-MM_avant_patch
user = login JWT (sub) immutable, traçable cote AD
HH-MM ajoute pour eviter collisions
- Service: nouveau regex SNAP_PATCHCENTER_V2_RE (avec heure), v1 conservee pour
les snapshots existants legacy
- Router iexec_snapshot: utilise user.get('sub') au lieu de row.intervenant
- UI:
* Renomme checkbox 'Format gere' -> 'Snapshots PatchCenter uniquement'
* Filtre origin === 'patchcenter' (exclut SLPM .exe par defaut)
* Combine avec 'Mes snapshots' (author = login user) -> seulement TES snapshots
PatchCenter visibles, parfait pour le cleanup post-patching
- Regex WINDOWS_OS_RE: \bwin(dows)?\b ou 'microsoft' insensible casse
- _is_skipped_os(os) : true si OS Windows
- Skip dans la boucle, comptage skipped_windows
- Append au note de l'import un suffix '[skip-windows: N ligne(s) Windows ignoree(s)]'
pour tracer combien de rows ont ete filtrees
lastval() retourne la derniere valeur de sequence de la session — si un trigger sur
patch_planning_imports bumpe une autre sequence (ex: audit log), lastval() retourne
la mauvaise valeur. Resultat: import_id pointe vers un ID inexistant et les INSERT
sur patch_planning_import_rows echouent en FK violation.
Fix: INSERT ... RETURNING id qui est sans ambiguite, et early-return si NULL.
- Migration migrate_pct_workflow_20260507.sql: ajoute patch_planning_import_rows
pct_required (boolean default false), pct_confirmed_at (timestamptz),
pct_confirmed_by_user_id (FK users). Backfill depuis servers.pct_required.
- Auto-detection a l'import (planning_import.py): scan referent_technique +
mode_operatoire + impacts + commentaire pour pattern \bPCT\b mot entier
(insensible casse) -> pct_required=true sur la row. Propage egalement vers
servers.pct_required si pas deja true.
- UI iexec: badge orange '⚠ Prév PCT à faire' sur la cellule asset_name si
pct_required=true et pas confirme, badge vert '✅ PCT ok' une fois confirme.
- Gate avant Step 3 (PATCH REEL): scan des serveurs cibles, si certains ont
pct_required && !pct_confirmed -> 2 confirmations successives + appel
POST /patching/iexec/confirm-pct qui marque pct_confirmed_at + user_id.
Ne lance pas le patch si l'operateur annule.
- Endpoint POST /patching/iexec/confirm-pct: marque les rows comme PCT confirmes
(pct_confirmed_at = now(), pct_confirmed_by_user_id = current user).
- Notif Teams: send_notification accepte planning_row_id optionnel ; si la row
a pct_required && pct_confirmed, le message debut/fin est suffixe par
' (Prévenance PCT ok)' pour informer le responsable que l'amont a ete gere.