MSIN-4406 Implement duplicate bib check feature in registration management

This commit introduces a new AJAX functionality to check for duplicate bib numbers during the registration process. It adds a case in `ajax_inscr_gestion.php` to handle duplicate checks, returning relevant participant information and policies for handling duplicates. The front-end script in `inc_footer_scripts.php` is updated to incorporate this check before saving a bib, enhancing user interaction with confirmation dialogs for duplicate assignments. Additionally, new functions are added in `inc_fx_inscriptions_gestion.php` to manage duplicate policies and retrieve existing participants with the same bib number, improving data integrity and user experience in the registration management interface.
This commit is contained in:
2026-06-29 11:09:10 -04:00
parent 3b007261d6
commit 811cf02f16
3 changed files with 139 additions and 38 deletions

View File

@ -107,6 +107,49 @@ switch ($strAction) {
$tabRetour = array('state' => 'ok', 'html' => $strForm); $tabRetour = array('state' => 'ok', 'html' => $strForm);
break; 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': case 'transaction_resume':
// Resume de la transaction (meme rendu que facture.php cote promoteur), affichable // Resume de la transaction (meme rendu que facture.php cote promoteur), affichable
// a tout moment depuis la barre Gestion via un panneau repliable independant. // a tout moment depuis la barre Gestion via un panneau repliable independant.

View File

@ -1069,49 +1069,61 @@ if ($strLangue == 'fr') {
str_new_no_bib = $("#txt_no_bib_e" + int_par_id).val(); str_new_no_bib = $("#txt_no_bib_e" + int_par_id).val();
} }
/*bootbox.confirm({ (function (action, parId, pecId, eveId, eprId, newBib) {
title: "<?php afficheTexte('confirm-title', 1, 1, 1); ?>", // Enregistrement reel du dossard (chemin legacy promoteur, inchange).
message: "<?php afficheTexte('promoteur_bib_manual'); ?>", function saveBib() {
buttons: { $preloader_text.html('<?php afficheTexte('preloader_wait', 1, 0, 1); ?>');
confirm: { $preloader.show();
label: '<?php afficheTexte('confirm-btn-confirm', 1, 1, 1); ?>', $.post('<?php echo $vDomaine; ?>/ajax_promoteur.php', 'a=' + action + '&eve_id=' + eveId + '&new_no_bib=' + newBib + '&pec_id=' + pecId + '&epr_id=' + eprId + '&par_id=' + parId + '&lang=<?php echo $strLangue; ?>', function ($result) {
className: 'btn btn-success rounded-0' // Succes : rien a afficher pour l'instant.
}, }, 'json');
cancel: { $preloader.fadeOut('slow');
label: '<?php afficheTexte('confirm-btn-cancel', 1, 1, 1); ?>',
className: 'btn btn-danger rounded-0'
} }
},
callback: function (result) {
if (result === true) {*/
$preloader_text.html('<?php afficheTexte('preloader_wait', 1, 0, 1); ?>');
$preloader.show();
$.post('<?php echo $vDomaine; ?>/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=<?php echo $strLangue; ?>', 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') { // Garde-fou "dossard en double" dans l'evenement. La politique
/*bootbox.alert({ // (warn / block / allow) est decidee cote serveur — par evenement a terme.
title: "<?php afficheTexte('alert-title', 1, 1, 1); ?>", $.post('<?php echo $vDomaine; ?>/ajax_inscr_gestion.php', {
message: "<?php afficheTexte('promoteur_bib_manuel'); ?>", a: 'bib_duplicate_check',
buttons: { par_id: parId,
ok: { no_bib: bibTrim,
label: '<?php afficheTexte('alert-btn-ok', 1, 1, 1); ?>', type: action,
className: 'btn btn-primary rounded-0' lang: '<?php echo $strLangue; ?>'
} }, function (chk) {
}, if (!chk || chk.state !== 'ok' || !chk.duplicate) { saveBib(); return; }
callback: function (result) {
if (result === true) {
} var lignes = (chk.who || []).map(function (w) {
} return '<li><strong>' + w.name + '</strong>' + (w.epreuve ? ' &mdash; ' + w.epreuve : '') + '</li>';
});*/ }).join('');
}
}, 'json');
$preloader.fadeOut('slow'); // Politique bloquante (prevue pour le futur) : interdiction pure.
/*} if (chk.policy === 'block') {
} bootbox.alert({
});*/ title: "<?php if ($strLangue == 'fr') { ?>Dossard déjà utilisé<?php } else { ?>Bib already used<?php } ?>",
message: "<?php if ($strLangue == 'fr') { ?>Ce dossard est déjà assigné à :<?php } else { ?>This bib is already assigned to:<?php } ?><ul style=\"margin:8px 0;\">" + lignes + "</ul><?php if ($strLangue == 'fr') { ?>Les doublons sont interdits pour cet événement.<?php } else { ?>Duplicates are not allowed for this event.<?php } ?>",
buttons: { ok: { label: '<?php afficheTexte('alert-btn-ok', 1, 1, 1); ?>', className: 'btn btn-primary rounded-0' } }
});
return;
}
// Politique d'avertissement (defaut actuel) : confirmation du doublon.
bootbox.confirm({
title: "<?php if ($strLangue == 'fr') { ?>Dossard en double<?php } else { ?>Duplicate bib<?php } ?>",
message: "<?php if ($strLangue == 'fr') { ?>Ce dossard est déjà assigné à :<?php } else { ?>This bib is already assigned to:<?php } ?><ul style=\"margin:8px 0;\">" + lignes + "</ul><?php if ($strLangue == 'fr') { ?>Voulez-vous vraiment assigner ce dossard en double ?<?php } else { ?>Do you really want to assign this bib as a duplicate?<?php } ?>",
buttons: {
confirm: { label: '<?php if ($strLangue == 'fr') { ?>Assigner quand même<?php } else { ?>Assign anyway<?php } ?>', className: 'btn btn-warning rounded-0' },
cancel: { label: '<?php afficheTexte('confirm-btn-cancel', 1, 1, 1); ?>', 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; break;
case 'edit': case 'edit':
$("#row_action_" + int_epr_id).val('defaut'); $("#row_action_" + int_epr_id).val('defaut');

View File

@ -541,6 +541,52 @@ function fxInscrGestionFetchRowByPec($intPecId, $intEveId) {
return $objDatabase->fxGetRow($sql); 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) { function fxInscrGestionEpreuveLabel($intEprId, $strLangue) {
$tab = fxGetEpreuve($intEprId); $tab = fxGetEpreuve($intEprId);
if ($tab === null) { if ($tab === null) {