V1.17.18 Modifications consignes correction bugs

This commit is contained in:
2026-09-08 15:28:26 +00:00
parent 9d537a9b36
commit 67614908c3
7 changed files with 249 additions and 57 deletions
+130 -13
View File
@@ -22,6 +22,14 @@
* retourne alors ce même type de tableau (valeurs mises à jour, mêmes clés),
* un Test retourne toujours un simple bool. Voir
* consigne_completer_code_postal() ci-dessous pour un exemple.
* - Test à UN champ avec accès base (8 septembre 2026) : une fonction de Test
* qui a besoin d'interroger la base (ex. vérifier qu'une valeur n'est pas
* déjà utilisée ailleurs) peut déclarer deux paramètres supplémentaires,
* dans cet ordre : (string $valeur, PDO $pdo, ?int $idExclusion): bool — le
* moteur les fournit systématiquement à tout Test à un champ (voir
* moteur_consignes.php), $idExclusion étant l'identifiant de la fiche en
* cours de modification (null en Création). Voir consigne_mail_unique() et
* consigne_portable_unique() ci-dessous, premier cas réel.
* - Jumeau "_seraitDeclenchee" (7 septembre 2026, soir) : toute fonction de
* Traitement qui interroge un service externe (IA) et porte
* FON_VisuelAttenteIA = 'O' DOIT avoir, en plus, une fonction nommée
@@ -34,15 +42,20 @@
* qu'interroge consignes_apercu.php pour piloter le badge IA générique côté
* écran, voir ce fichier et moteur_consignes.php
* (auMoinsUneConsigneIaSeraitDeclenchee()).
* Fichier volontairement sans dépendance à la session ni à la base de
* données : chaque fonction ne fait que transformer ou tester une ou
* plusieurs valeurs qu'on lui passe. Exception depuis le 7 septembre 2026
* (soir) : une fonction peut interroger un service externe (API d'IA, voir
* consigne_completer_code_postal()) quand la consigne elle-même l'exige
* explicitement — jamais la session ni la base ASPIC, seulement un appel
* réseau sortant, et toujours avec un repli silencieux (valeurs inchangées)
* en cas d'échec : un Traitement ne doit jamais faire échouer un
* enregistrement à cause d'un service tiers indisponible.
* Fichier volontairement sans dépendance à la session : chaque fonction ne
* fait que transformer ou tester une ou plusieurs valeurs qu'on lui passe (et,
* depuis le 8 septembre 2026, éventuellement interroger $pdo — voir ci-dessus
* — mais jamais $_SESSION). Deux exceptions à la dépendance base/réseau :
* - Depuis le 7 septembre 2026 (soir) : une fonction de Traitement peut
* interroger un service externe (API d'IA, voir
* consigne_completer_code_postal()) quand la consigne elle-même l'exige
* explicitement, toujours avec un repli silencieux (valeurs inchangées) en
* cas d'échec : un Traitement ne doit jamais faire échouer un
* enregistrement à cause d'un service tiers indisponible.
* - Depuis le 8 septembre 2026 : une fonction de Test à un champ peut
* interroger $pdo, reçu directement du moteur (voir "Test à UN champ avec
* accès base" ci-dessus) — jamais de connexion ouverte ici, toujours celle
* déjà établie par l'appelant (db.php).
*/
// ASP_Fonctions.FON_Nom = 'MAJUSCULE' (Nature=Traitement)
@@ -104,6 +117,21 @@ function consigne_mail_valide(string $valeur): bool
return filter_var($valeur, FILTER_VALIDATE_EMAIL) !== false;
}
// ASP_Fonctions.FON_Nom = 'RETIRER_POINTS' (Nature=Traitement), 8 septembre
// 2026. Retire tous les points (".") d'une valeur, sans autre transformation
// — demandée pour un numéro de téléphone saisi avec des points comme
// séparateurs (ex. "02.98.12.34.56" -> "0298123456"). Générique et
// réutilisable tel quel sur n'importe quel champ texte, même principe que
// SANS_CHIFFRE ci-dessus : aucune fonction par champ, juste une ligne dans
// ASP_Consignes par champ concerné (CNT_Tel et/ou CNT_Portable). Volontairement
// limitée aux points, sur la demande telle que formulée par Jean-Marc — à
// étendre (espaces, tirets...) le jour où un besoin réel et concret se
// présentera, pas par anticipation.
function consigne_retirer_points(string $valeur): string
{
return str_replace('.', '', $valeur);
}
// ASP_Fonctions.FON_Nom = 'SANS_CHIFFRE' (Nature=Test), 7 septembre 2026.
// Vrai si la valeur ne contient aucun chiffre, faux sinon — champ facultatif
// (une valeur vide est conforme, même principe que consigne_mail_valide() :
@@ -140,10 +168,15 @@ function consigne_sans_chiffre(string $valeur): bool
// (ex. arrondissements de Paris/Lyon/Marseille) doit renvoyer null plutôt que
// de deviner — c'est écrit explicitement dans la consigne envoyée au modèle.
//
// Toute défaillance (clé absente, réseau, quota, réponse inattendue) se
// traduit par un retour silencieux des valeurs inchangées : un Traitement ne
// doit jamais bloquer l'enregistrement d'un contact à cause d'un service
// externe (voir commentaire d'en-tête de moteur_consignes.php) — contrairement
// Toute défaillance (clé absente, réseau, quota, réponse inattendue) laisse
// toujours CNT_AdrCP inchangé — un Traitement ne doit jamais bloquer
// l'enregistrement d'un contact à cause d'un service externe (voir
// commentaire d'en-tête de moteur_consignes.php) — mais n'est PLUS silencieuse
// pour autant depuis le 8 septembre 2026 (correctif demandé par Jean-Marc,
// constat d'un échec resté invisible sur une ville inconnue) : chaque cas
// d'échec pose désormais un "_avertissement" pour l'écran, et un
// "_realisation" quand un appel a réellement été tenté (voir le corps de la
// fonction ci-dessous) — contrairement
// à un Test, dont l'échec est lui affiché à l'utilisateur.
// Jumeau "_seraitDeclenchee" (voir en-tête de fichier) : réplique à elle
// seule la condition de déclenchement de consigne_completer_code_postal()
@@ -169,6 +202,17 @@ function consigne_completer_code_postal(array $valeurs): array
$apiKey = trim((string) getenv('OPENAI_API_KEY'));
if (!$apiKey) {
// Panne de configuration (8 septembre 2026) : jusqu'ici un retour
// totalement silencieux — repéré par Jean-Marc comme le même défaut
// que celui déjà corrigé le 7 septembre pour une ville ambiguë
// ("un constat d'échec doit toujours être visible, jamais un simple
// silence"). Pas de "_realisation" ici : aucun appel réseau n'a
// réellement eu lieu, donc rien à journaliser comme un coût engagé
// (voir en-tête de moteur_consignes.php) — seulement l'avertissement
// écran, pour que l'utilisateur ne reste pas devant un champ vide
// sans explication.
$valeurs['_avertissement'] = "Code postal non déterminé pour « $ville » "
. "(service indisponible). Saisissez le code postal vous-même.";
return $valeurs;
}
@@ -226,6 +270,18 @@ function consigne_completer_code_postal(array $valeurs): array
curl_close($ch);
if ($reponseBrute === false || $codeHttp !== 200) {
// Échec réseau/HTTP (8 septembre 2026) : contrairement au cas
// "clé API absente" ci-dessus, un appel a bien été tenté (et peut
// avoir un coût, selon où l'échec survient côté fournisseur) — donc
// journalisé comme le cas "ville ambiguë" plus bas, sur le même
// principe posé le 7 septembre par Jean-Marc ("un appel fait à l'IA
// [doit laisser] une trace dans les réalisations").
$valeurs['_realisation'] = [
'action' => 'IA_CP',
'commentaire' => "Échec technique de l'appel IA pour la ville « $ville » (HTTP $codeHttp).",
];
$valeurs['_avertissement'] = "Code postal non déterminé pour « $ville » "
. "(service temporairement indisponible). Saisissez le code postal vous-même.";
return $valeurs;
}
@@ -289,3 +345,64 @@ function consigne_completer_code_postal(array $valeurs): array
return $valeurs;
}
// ASP_Fonctions.FON_Nom = 'MAIL_UNIQUE' (Nature=Test), 8 septembre 2026.
// Vrai si l'adresse mail n'est pas déjà utilisée par un AUTRE contact actif,
// faux sinon. Champ facultatif, même principe que consigne_mail_valide()
// ci-dessus : une valeur vide est toujours conforme (deux fiches sans mail ne
// sont pas en conflit) — le caractère obligatoire reste la responsabilité
// d'une consigne NON_VIDE séparée, combinable sur le même champ.
//
// Fonction dédiée à CNT_Mail plutôt qu'un test générique "valeur unique"
// paramétrable par nom de champ (demande explicite de Jean-Marc, 8 septembre
// 2026) : contrairement à SANS_CHIFFRE ci-dessus, volontairement générique
// parce que ça a du sens sur Nom/Prénom/Ville indifféremment, une contrainte
// d'unicité n'a de sens QUE sur certains champs précis — l'ouvrir à un
// paramétrage libre depuis ASP_Consignes exposerait un futur opérateur du
// catalogue à l'appliquer par erreur à un champ comme Prénom, où deux
// contacts homonymes sont parfaitement normaux. Voir consigne_portable_unique()
// ci-dessous, jumelle sur CNT_Portable — toutes deux appuyées sur le même
// utilitaire privé valeurDejaUtilisee() pour ne pas dupliquer la requête SQL.
function consigne_mail_unique(string $valeur, PDO $pdo, ?int $idExclusion = null): bool
{
return !valeurDejaUtilisee($pdo, 'CNT_Mail', $valeur, $idExclusion);
}
// ASP_Fonctions.FON_Nom = 'PORTABLE_UNIQUE' (Nature=Test), 8 septembre 2026.
// Même principe que consigne_mail_unique() ci-dessus, appliqué à
// CNT_Portable — fonction séparée plutôt qu'un simple paramétrage de la même
// fonction sur un autre champ, pour la même raison (voir commentaire de
// consigne_mail_unique()).
function consigne_portable_unique(string $valeur, PDO $pdo, ?int $idExclusion = null): bool
{
return !valeurDejaUtilisee($pdo, 'CNT_Portable', $valeur, $idExclusion);
}
// Utilitaire privé partagé par consigne_mail_unique() et
// consigne_portable_unique() ci-dessus — pas une fonction du catalogue
// (aucun ASP_Fonctions.FON_NomPHP ne pointe dessus, jamais appelée depuis
// ASP_Consignes) : factorise uniquement la requête SQL commune aux deux.
// $champ vient toujours d'une constante écrite en dur dans l'une des deux
// fonctions ci-dessus, jamais d'une donnée utilisateur — pas de risque
// d'injection SQL malgré l'interpolation dans le nom de colonne.
// $idExclusion (identifiant du contact en cours de modification, voir
// moteur_consignes.php) permet à une fiche de conserver sa propre valeur sans
// se bloquer elle-même ; toujours null en Création, où rien n'existe encore à
// exclure.
function valeurDejaUtilisee(PDO $pdo, string $champ, string $valeur, ?int $idExclusion): bool
{
$valeur = trim($valeur);
if ($valeur === '') return false;
$sql = "SELECT 1 FROM ASP_Contacts WHERE $champ = ? AND CNT_Actif = 1";
$params = [$valeur];
if ($idExclusion !== null) {
$sql .= ' AND CNT_Incr != ?';
$params[] = $idExclusion;
}
$sql .= ' LIMIT 1';
$stmt = $pdo->prepare($sql);
$stmt->execute($params);
return (bool)$stmt->fetchColumn();
}