diff --git a/ajax_inscr_gestion.php b/ajax_inscr_gestion.php index ff3ed59..73e6b26 100644 --- a/ajax_inscr_gestion.php +++ b/ajax_inscr_gestion.php @@ -107,6 +107,49 @@ switch ($strAction) { $tabRetour = array('state' => 'ok', 'html' => $strForm); break; + case 'bib_duplicate_check': + // Controle "dossard en double" dans l'evenement (avant enregistrement legacy). + // Ne modifie rien : renvoie qui porte deja ce dossard + la politique a appliquer. + $intParId = intval($_REQUEST['par_id'] ?? 0); + $strNoBib = trim((string)($_REQUEST['no_bib'] ?? '')); + $blnTeam = (trim((string)($_REQUEST['type'] ?? '')) === 'no_equipe'); + + $intEveId = fxEveAccesResolveEveIdFromParId($intParId); + if ($intEveId <= 0 || !fxInscrGestionCanDo($intComId, $intEveId, 'inscriptions_gestion.bib_edit')) { + $tabRetour = array('state' => 'error', 'message' => 'unauthorized'); + break; + } + + $strPolicy = fxInscrGestionBibDuplicatePolicy($intEveId); + + if ($strNoBib === '') { + $tabRetour = array('state' => 'ok', 'duplicate' => false, 'policy' => $strPolicy, 'who' => array()); + break; + } + + // Commande courante a exclure du controle (un participant ne se "double" pas lui-meme). + $intPecOrig = intval($objDatabase->fxGetVar( + 'SELECT e.pec_id_original FROM resultats_epreuves_commandees e, resultats_participants p' + . ' WHERE p.par_id = ' . $intParId . ' AND p.pec_id = e.pec_id_original LIMIT 1' + )); + + $arrDup = fxInscrGestionFindBibDuplicates($intEveId, $strNoBib, $intPecOrig, $blnTeam); + $arrWho = array(); + foreach ($arrDup as $rowDup) { + $arrWho[] = array( + 'name' => fxInscrGestionEsc(fxUnescape($rowDup['par_nom']) . ', ' . fxUnescape($rowDup['par_prenom'])), + 'epreuve' => fxInscrGestionEsc(fxInscrGestionEpreuveLabel($rowDup['epr_id'], $strLangue)), + ); + } + + $tabRetour = array( + 'state' => 'ok', + 'duplicate' => count($arrWho) > 0, + 'policy' => $strPolicy, + 'who' => $arrWho, + ); + break; + case 'transaction_resume': // Resume de la transaction (meme rendu que facture.php cote promoteur), affichable // a tout moment depuis la barre Gestion via un panneau repliable independant. diff --git a/inc_footer_scripts.php b/inc_footer_scripts.php index 40e1346..cf63d99 100644 --- a/inc_footer_scripts.php +++ b/inc_footer_scripts.php @@ -1069,49 +1069,61 @@ if ($strLangue == 'fr') { str_new_no_bib = $("#txt_no_bib_e" + int_par_id).val(); } - /*bootbox.confirm({ - title: "", - message: "", - buttons: { - confirm: { - label: '', - className: 'btn btn-success rounded-0' - }, - cancel: { - label: '', - className: 'btn btn-danger rounded-0' + (function (action, parId, pecId, eveId, eprId, newBib) { + // Enregistrement reel du dossard (chemin legacy promoteur, inchange). + function saveBib() { + $preloader_text.html(''); + $preloader.show(); + $.post('/ajax_promoteur.php', 'a=' + action + '&eve_id=' + eveId + '&new_no_bib=' + newBib + '&pec_id=' + pecId + '&epr_id=' + eprId + '&par_id=' + parId + '&lang=', function ($result) { + // Succes : rien a afficher pour l'instant. + }, 'json'); + $preloader.fadeOut('slow'); } - }, - callback: function (result) { - if (result === true) {*/ - $preloader_text.html(''); - $preloader.show(); - $.post('/ajax_promoteur.php', 'a=' + str_action + '&eve_id=' + int_eve_id + '&new_no_bib=' + str_new_no_bib + '&pec_id=' + int_pec_id + '&epr_id=' + int_epr_id + '&par_id=' + int_par_id + '&lang=', function ($result) { + var bibTrim = (newBib == null ? '' : ('' + newBib)).trim(); + // Suppression d'un dossard (champ vide) : aucun controle de doublon. + if (bibTrim === '') { saveBib(); return; } - if ($result.state == 'success') { - /*bootbox.alert({ - title: "", - message: "", - buttons: { - ok: { - label: '', - className: 'btn btn-primary rounded-0' - } - }, - callback: function (result) { - if (result === true) { + // Garde-fou "dossard en double" dans l'evenement. La politique + // (warn / block / allow) est decidee cote serveur — par evenement a terme. + $.post('/ajax_inscr_gestion.php', { + a: 'bib_duplicate_check', + par_id: parId, + no_bib: bibTrim, + type: action, + lang: '' + }, function (chk) { + if (!chk || chk.state !== 'ok' || !chk.duplicate) { saveBib(); return; } - } - } - });*/ - } - }, 'json'); + var lignes = (chk.who || []).map(function (w) { + return '
  • ' + w.name + '' + (w.epreuve ? ' — ' + w.epreuve : '') + '
  • '; + }).join(''); - $preloader.fadeOut('slow'); - /*} - } - });*/ + // Politique bloquante (prevue pour le futur) : interdiction pure. + if (chk.policy === 'block') { + bootbox.alert({ + title: "Dossard déjà utiliséBib already used", + message: "Ce dossard est déjà assigné à :This bib is already assigned to:Les doublons sont interdits pour cet événement.Duplicates are not allowed for this event.", + buttons: { ok: { label: '', className: 'btn btn-primary rounded-0' } } + }); + return; + } + + // Politique d'avertissement (defaut actuel) : confirmation du doublon. + bootbox.confirm({ + title: "Dossard en doubleDuplicate bib", + message: "Ce dossard est déjà assigné à :This bib is already assigned to:Voulez-vous vraiment assigner ce dossard en double ?Do you really want to assign this bib as a duplicate?", + buttons: { + confirm: { label: 'Assigner quand mêmeAssign anyway', className: 'btn btn-warning rounded-0' }, + cancel: { label: '', className: 'btn btn-secondary rounded-0' } + }, + callback: function (result) { if (result === true) { saveBib(); } } + }); + }, 'json').fail(function () { + // Echec du controle : on n'empeche pas l'enregistrement (mode non bloquant). + saveBib(); + }); + })(str_action, int_par_id, int_pec_id, int_eve_id, int_epr_id, str_new_no_bib); break; case 'edit': $("#row_action_" + int_epr_id).val('defaut'); diff --git a/php/inc_fx_inscriptions_gestion.php b/php/inc_fx_inscriptions_gestion.php index 0f2711c..f14e643 100644 --- a/php/inc_fx_inscriptions_gestion.php +++ b/php/inc_fx_inscriptions_gestion.php @@ -541,6 +541,52 @@ function fxInscrGestionFetchRowByPec($intPecId, $intEveId) { return $objDatabase->fxGetRow($sql); } +/** + * Politique de gestion des dossards en double pour un evenement. + * Pour l'instant : toujours 'warn' (avertissement non bloquant). + * + * Hook prevu pour le futur : lire un reglage par evenement afin de renvoyer + * 'block' (interdiction totale) ou 'allow' (doublons silencieux). Exemple : + * $rec = fxGetEvenementsId($intEveId); + * if (!empty($rec['eve_bib_doublon_interdit'])) return 'block'; + * if (!empty($rec['eve_bib_doublon_autorise'])) return 'allow'; + * + * @return string 'warn' | 'block' | 'allow' + */ +function fxInscrGestionBibDuplicatePolicy($intEveId) { + return 'warn'; +} + +/** + * Participants d'un evenement deja porteurs du dossard donne (hors la commande + * courante). Alimente le garde-fou "dossard en double". + * + * @param bool $blnTeam true = dossard d'equipe (e.no_equipe), false = individuel (p.no_bib) + * @return array liste de lignes ['par_nom','par_prenom','epr_id'] + */ +function fxInscrGestionFindBibDuplicates($intEveId, $strNoBib, $intExceptPec, $blnTeam) { + global $objDatabase; + + $strNoBib = trim((string)$strNoBib); + if (intval($intEveId) <= 0 || $strNoBib === '') { + return array(); + } + + $strBibCol = $blnTeam ? 'e.no_equipe' : 'p.no_bib'; + $sql = 'SELECT p.par_nom, p.par_prenom, e.epr_id' + . ' FROM resultats_epreuves_commandees e, resultats_participants p' + . ' WHERE e.eve_id = ' . intval($intEveId) + . ' AND p.pec_id = e.pec_id_original' + . ' AND e.is_cancelled = 0 AND e.pec_actif = 1' + . " AND TRIM(" . $strBibCol . ") = '" . $objDatabase->fxEscape($strNoBib) . "'" + . ' AND e.pec_id_original <> ' . intval($intExceptPec) + . ' GROUP BY e.pec_id_original' + . ' ORDER BY p.par_nom, p.par_prenom'; + + $tab = $objDatabase->fxGetResults($sql); + return is_array($tab) ? $tab : array(); +} + function fxInscrGestionEpreuveLabel($intEprId, $strLangue) { $tab = fxGetEpreuve($intEprId); if ($tab === null) {