1) { // calculer le début et la fin de la boucle $intStart = ($intPage - 1) * $intNbItemsParPage + 1; $intEnd = $intStart + $intNbItemsParPage - 1; } if ($blnDon) { $strPrenom = 'rech_prenom_don'; $strNom = 'rech_nom_don'; $strEquipe = 'rech_equipe_don'; } else { $strPrenom = 'rech_prenom'; $strNom = 'rech_nom'; $strEquipe = 'rech_equipe'; } $strRechercheForm .= ' '; if ($blnDon) { $strWhere2 = " a.eve_id = " . intval($intEveId) . " AND a.sta_id = 3 AND a.no_panier = d.no_panier AND e.pec_id_original = d.pec_id "; $strWhere = '1 = 1'; if ($strRechercheNom != '') $strWhere .= ' AND com_nom LIKE "%' . $strRechercheNom . '%"'; if ($strRecherchePrenom != '') $strWhere .= ' AND com_prenom LIKE "%' . $strRecherchePrenom . '%"'; if ($strNomParticipant!= '') $strWhere .= ' AND par_nom LIKE "%' . $strNomParticipant. '%"'; if ($strPrenomParticipant != '') $strWhere .= ' AND par_prenom LIKE "%' . $strPrenomParticipant. '%"'; if ($strNoCommande != '') $strWhere .= ' AND no_commande LIKE "%' . $objDatabase->fxEscape($strNoCommande) . '%"'; if ($strNomEquipe != '') $strWhere .= ' AND pec_nom_equipe LIKE "%' . $objDatabase->fxEscape($strNomEquipe) . '%"'; $sqlTableTemp = "CREATE TEMPORARY TABLE dons_temp AS (SELECT a.com_nom, a.com_prenom, a.eve_id, a.no_panier, a.no_commande, d.pd_montant, d.pd_recu, d.pd_recu_pdf, d.pd_recu_numero, d.pec_id, d.par_id, e.pec_nom_equipe, e.pec_equipe, e.epr_id, IF(d.par_id <> 0, (SELECT p.par_nom FROM resultats_participants p WHERE d.par_id = p.par_id_original), '') AS par_nom, IF(d.par_id <> 0, (SELECT p.par_prenom FROM resultats_participants p WHERE d.par_id = p.par_id_original), '') AS par_prenom FROM inscriptions_panier_acheteurs a, resultats_epreuves_commandees e, inscriptions_panier_dons d WHERE " . $strWhere2 . ")"; $qryTableTemp = $objDatabase->fxQuery($sqlTableTemp); $sqlInscriptionsParPage = "SELECT * FROM dons_temp WHERE " . $strWhere; $tabInscriptionsParPage = $objDatabase->fxGetResults($sqlInscriptionsParPage); $sqlDelete = "DROP TABLE IF EXISTS dons_temp"; $qryDelete = $objDatabase->fxQuery($sqlDelete); } else { $strWhere = ' e.eve_id = ' . intval($intEveId); //MSIN-4009 //$strWhere .= ' AND e.pec_actif = 1'; // MSIN-3524 if ($strRechercheNom != '') { $strWhere .= ' AND p.par_nom LIKE "%' . $strRechercheNom . '%"'; } if ($strRecherchePrenom != '') { $strWhere .= ' AND p.par_prenom LIKE "%' . $strRecherchePrenom . '%"'; } if ($strNoCommande != '') { $strWhere .= ' AND p.no_commande LIKE "%' . $objDatabase->fxEscape($strNoCommande) . '%"'; } if ($strNomEquipe != '') { $strWhere .= ' AND p.par_nom_equipe LIKE "%' . $objDatabase->fxEscape($strNomEquipe) . '%"'; } if ($intEpreuve != 0) { $strWhere .= " AND p.epr_id = " . intval($intEpreuve) . " AND p.epr_id = e.epr_id"; } if (intval($chk_bib) == 1) { $strWhere .= " AND TRIM(p.no_bib) = ''"; } if (intval($chk_cancelled) == 1) { $strWhere .= ' AND e.is_cancelled IN(0,1)'; // MSIN-3522 } else { $strWhere .= ' AND e.is_cancelled = 0 AND e.pec_actif = 1'; // MSIN-3522 } $sqlInscriptionsParPage = 'select p.no_bib,p.no_bib_remis,p.no_bib_remis_date,p.no_bib_remis_par, p.par_id, p.par_equipe, p.par_prenom, p.par_nom, e.pec_id_original, e.epr_id, e.eve_id, e.pec_id, e.is_cancelled, e.pec_actif, p.rol_id, e.no_commande, e.no_panier, e.no_equipe, e.pec_equipe from resultats_epreuves_commandees e, resultats_participants p where ' . $strWhere . ' and p.pec_id = e.pec_id_original group by e.pec_id_original'; $tabInscriptionsParPage = $objDatabase->fxGetResults($sqlInscriptionsParPage); } if ($tabInscriptionsParPage != null) { $arrColonnes = fxGetchampsPromoteur($blnDon, $strLangue); $strRechercheResultats .= '

' . afficheTexte('promoteur_liste_inscrits', 0) . '

'; foreach ($arrColonnes as $intIndex => $arrValues) { $strRechercheResultats .= ' '; } $strRechercheResultats .= ' '; $intNbInscriptions = count($tabInscriptionsParPage); if ($intEnd > $intNbInscriptions) { $intEnd = $intNbInscriptions; } for ($j = $intStart; $j <= $intEnd; $j++) { $tabEpreuve = fxGetEpreuve($tabInscriptionsParPage[$j]['epr_id']); $strClasse = ($j & 1) ? '' : 'table-secondary'; if ($tabInscriptionsParPage[$j]['is_cancelled'] == '1') { $strClasse .= ' table-danger'; } $strRechercheResultats .= ' '; $strRechercheResultats .= fxShowColumnPromoteur($arrColonnes, $tabInscriptionsParPage, $j, $strLangue, $tabInscriptionsParPage[$j]['pec_equipe'],$abonnement); $strRechercheResultats .= ' '; } $intNbPagesTotal = ceil($intNbInscriptions / $intNbItemsParPage); // calculer le nb de pages $intNbItemsAffiches = $intEnd - ($intStart - 1); if ($strLangue == 'fr') { $strStatut = $intNbItemsAffiches . ' de ' . $intNbInscriptions . '
page ' . $intPage . ' de ' . $intNbPagesTotal; } else { $strStatut = $intNbItemsAffiches . ' of ' . $intNbInscriptions . '
page ' . $intPage . ' of ' . $intNbPagesTotal; } $strRechercheResultats .= '
' . fxUnescape($arrValues['label_' . $strLangue]) . '
' . $strStatut . '
'; // afficher pagination $tabQuery = $_GET; unset($tabQuery['pg']); unset($tabQuery['code']); unset($tabQuery['lang']); $strQuery = http_build_query($tabQuery, '', '&'); $strRecherchePagination = fxGetPagination($vDomaine . '/' . $str_code . '?' . $strQuery, $intNbInscriptions, $intNbItemsParPage, $intPage); } else { $strRechercheResultats = ' '; } $strDisplay = (!isset($_GET['pg']) && !isset($_GET['btn_recherche'])) ? '' : ' show'; $strIcon = "'" . $vDomaine . "/images/search-icon.png'"; $strRetour .= '

' . afficheTexte('txt_recherche', 0) . '

' . $strRechercheForm . '
' . $strRechercheResultats . ' ' . $strRecherchePagination . '
'; return $strRetour; } function fxShowRechercheinscription($str_code, $int_promoteur_id, $blnDon, $strLangue, $intEveId,$abonnement=0) { global $objDatabase, $vDomaine; $strRechercheForm = $strRechercheResultats = $strRetour = $strSelected = $strRecherchePrenom = $strRechercheNom = $strNoCommande = $strNomParticipant = $strNomEquipe = $strPrenomParticipant = $strRecherchePagination = $chk_bib = ''; $intEpreuve = 0; $intPage = $intStart = 1; $intNbItemsParPage = $intEnd = 100; // si apres recherche if (isset($_GET['rech_prenom'])) { $strRecherchePrenom = $_GET['rech_prenom']; } if (isset($_GET['rech_nom'])) { $strRechercheNom = $_GET['rech_nom']; } if (isset($_GET['rech_no_commande'])) { $strNoCommande = $_GET['rech_no_commande']; } if (isset($_GET['rech_nom_equipe'])) { $strNomEquipe = $_GET['rech_nom_equipe']; } if (isset($_GET['rech_nom_participant'])) { $strNomParticipant = $_GET['rech_nom_participant']; } if (isset($_GET['rech_prenom_participant'])) { $strPrenomParticipant = $_GET['rech_prenom_participant']; } if (isset($_GET['sel_rech_epreuve'])) { $intEpreuve = base64_decode(urldecode($_GET['sel_rech_epreuve'])); } if (isset($_GET['chk_bib'])) { $chk_bib = 1; } if (isset($_GET['chk_cancelled'])) { $chk_cancelled = 1; }else{ $chk_cancelled = 0;} if (isset($_GET['pg'])) { $intPage = $_GET['pg']; } if (intval($intPage) > 1) { // calculer le début et la fin de la boucle $intStart = ($intPage - 1) * $intNbItemsParPage + 1; $intEnd = $intStart + $intNbItemsParPage - 1; } if ($blnDon) { $strPrenom = 'rech_prenom_don'; $strNom = 'rech_nom_don'; $strEquipe = 'rech_equipe_don'; } else { $strPrenom = 'rech_prenom'; $strNom = 'rech_nom'; $strEquipe = 'rech_equipe'; } $strRechercheForm .= ' '; if ($blnDon) { $strWhere2 = " a.eve_id = " . intval($intEveId) . " AND a.sta_id = 3 AND a.no_panier = d.no_panier AND e.pec_id_original = d.pec_id "; $strWhere = '1 = 1'; if ($strRechercheNom != '') $strWhere .= ' AND com_nom LIKE "%' . $strRechercheNom . '%"'; if ($strRecherchePrenom != '') $strWhere .= ' AND com_prenom LIKE "%' . $strRecherchePrenom . '%"'; if ($strNomParticipant!= '') $strWhere .= ' AND par_nom LIKE "%' . $strNomParticipant. '%"'; if ($strPrenomParticipant != '') $strWhere .= ' AND par_prenom LIKE "%' . $strPrenomParticipant. '%"'; if ($strNoCommande != '') $strWhere .= ' AND no_commande LIKE "%' . $objDatabase->fxEscape($strNoCommande) . '%"'; if ($strNomEquipe != '') $strWhere .= ' AND pec_nom_equipe LIKE "%' . $objDatabase->fxEscape($strNomEquipe) . '%"'; $sqlTableTemp = "CREATE TEMPORARY TABLE dons_temp AS (SELECT a.com_nom, a.com_prenom, a.eve_id, a.no_panier, a.no_commande, d.pd_montant, d.pd_recu, d.pd_recu_pdf, d.pd_recu_numero, d.pec_id, d.par_id, e.pec_nom_equipe, e.pec_equipe, e.epr_id, IF(d.par_id <> 0, (SELECT p.par_nom FROM resultats_participants p WHERE d.par_id = p.par_id_original), '') AS par_nom, IF(d.par_id <> 0, (SELECT p.par_prenom FROM resultats_participants p WHERE d.par_id = p.par_id_original), '') AS par_prenom FROM inscriptions_panier_acheteurs a, resultats_epreuves_commandees e, inscriptions_panier_dons d WHERE " . $strWhere2 . ")"; $qryTableTemp = $objDatabase->fxQuery($sqlTableTemp); $sqlInscriptionsParPage = "SELECT * FROM dons_temp WHERE " . $strWhere; $tabInscriptionsParPage = $objDatabase->fxGetResults($sqlInscriptionsParPage); $sqlDelete = "DROP TABLE IF EXISTS dons_temp"; $qryDelete = $objDatabase->fxQuery($sqlDelete); } else { $strWhere = ' e.eve_id = ' . intval($intEveId); //$strWhere .= ' AND e.pec_actif = 1'; // MSIN-3524 if ($strRechercheNom != '') { $strWhere .= ' AND p.par_nom LIKE "%' . $strRechercheNom . '%"'; } if ($strRecherchePrenom != '') { $strWhere .= ' AND p.par_prenom LIKE "%' . $strRecherchePrenom . '%"'; } if ($strNoCommande != '') { $strWhere .= ' AND p.no_commande LIKE "%' . $objDatabase->fxEscape($strNoCommande) . '%"'; } if ($strNomEquipe != '') { $strWhere .= ' AND p.par_nom_equipe LIKE "%' . $objDatabase->fxEscape($strNomEquipe) . '%"'; } if ($intEpreuve != 0) { $strWhere .= " AND p.epr_id = " . intval($intEpreuve) . " AND p.epr_id = e.epr_id"; } if (intval($chk_bib) == 1) { $strWhere .= " AND TRIM(p.no_bib) = ''"; } if (intval($chk_cancelled) == 1) { $strWhere .= ' AND e.is_cancelled = 1'; // MSIN-3522 //MSIN-4010 } else { $strWhere .= ' AND e.is_cancelled = 0 AND e.pec_actif = 1'; // MSIN-3522 } $sqlInscriptionsParPage = 'select p.no_bib,p.no_bib_remis,p.no_bib_remis_date,p.no_bib_remis_par, p.par_id, p.par_equipe, p.par_prenom, p.par_nom, e.pec_id_original, e.epr_id, e.eve_id, e.pec_id, e.is_cancelled, e.pec_actif, p.rol_id, e.no_commande, e.no_panier, e.no_equipe, e.pec_equipe from resultats_epreuves_commandees e, resultats_participants p where ' . $strWhere . ' and p.pec_id = e.pec_id_original group by e.pec_id_original'; $tabInscriptionsParPage = $objDatabase->fxGetResults($sqlInscriptionsParPage); } if ($tabInscriptionsParPage != null) { $arrColonnes = fxGetchampsPromoteur($blnDon, $strLangue); $strRechercheResultats .= '

' . afficheTexte('promoteur_liste_inscrits', 0) . '

'; foreach ($arrColonnes as $intIndex => $arrValues) { $strRechercheResultats .= ' '; } $strRechercheResultats .= ' '; $intNbInscriptions = count($tabInscriptionsParPage); if ($intEnd > $intNbInscriptions) { $intEnd = $intNbInscriptions; } for ($j = $intStart; $j <= $intEnd; $j++) { $tabEpreuve = fxGetEpreuve($tabInscriptionsParPage[$j]['epr_id']); $strClasse = ($j & 1) ? '' : 'table-secondary'; if ($tabInscriptionsParPage[$j]['is_cancelled'] == '1') { $strClasse .= ' table-danger'; } $strRechercheResultats .= ' '; $strRechercheResultats .= fxShowColumnPromoteur($arrColonnes, $tabInscriptionsParPage, $j, $strLangue, $tabInscriptionsParPage[$j]['pec_equipe'],$abonnement); $strRechercheResultats .= ' '; } $intNbPagesTotal = ceil($intNbInscriptions / $intNbItemsParPage); // calculer le nb de pages $intNbItemsAffiches = $intEnd - ($intStart - 1); if ($strLangue == 'fr') { $strStatut = $intNbItemsAffiches . ' de ' . $intNbInscriptions . '
page ' . $intPage . ' de ' . $intNbPagesTotal; } else { $strStatut = $intNbItemsAffiches . ' of ' . $intNbInscriptions . '
page ' . $intPage . ' of ' . $intNbPagesTotal; } $strRechercheResultats .= '
' . fxUnescape($arrValues['label_' . $strLangue]) . '
' . $strStatut . '
'; // afficher pagination $tabQuery = $_GET; unset($tabQuery['pg']); unset($tabQuery['code']); unset($tabQuery['lang']); $strQuery = http_build_query($tabQuery, '', '&'); $strRecherchePagination = fxGetPagination($vDomaine . '/' . $str_code . '?' . $strQuery, $intNbInscriptions, $intNbItemsParPage, $intPage); } else { $strRechercheResultats = ' '; } $strDisplay = (!isset($_GET['pg']) && !isset($_GET['btn_recherche'])) ? '' : ' show'; $strIcon = "'" . $vDomaine . "/images/search-icon.png'"; $strRetour .= '

' . afficheTexte('txt_recherche', 0) . '

' . $strRechercheForm . '
' . $strRechercheResultats . ' ' . $strRecherchePagination . '
'; return $strRetour; } /*/* * param $int_eve_id : le no. de l'evenement de l'epreuve a ajuster. * param $strLangue : langue d'affichage * */ function fxShowQuantites($int_eve_id, $strLangue) { global $objDatabase, $vDomaine; $tabEvenement = fxGetEvenements(fxGetEvenementsUrl($int_eve_id), $strLangue); // MSIN-1328 $sqlEpreuves = "SELECT * FROM inscriptions_epreuves where eve_id = " . intval($int_eve_id) . " AND epr_actif = 1"; $tabEpreuves = $objDatabase->fxGetResults($sqlEpreuves); ?>

"; } echo $tabEpreuve['epr_type_' . $strLangue]; if (trim($tabEpreuve['epr_nom_' . $strLangue]) != '') { echo "
" . $tabEpreuve['epr_nom_' . $strLangue]; } ?>
   
fxGetResults($sqlEpreuves); ?>

"; } echo $tabEpreuve['epr_type_' . $strLangue]; if (trim($tabEpreuve['epr_nom_' . $strLangue]) != '') { echo "
" . $tabEpreuve['epr_nom_' . $strLangue]; } ?>

0 ? $nbSurListe : '';?> 0 ? $nbSurListe : '';?>
   
fxGetResults($sqlEpreuves); ?>

recalculer les catégorie
fxGetResults($sqlRegles); if ($arrRegles){ ?> $value){ ?>
fxGetResults($sqlEpreuves); $sqlBibAssignments = "SELECT * FROM bib_assignements WHERE ba_actif = 1 ORDER BY ba_tri"; $tabBibAssignments = $objDatabase->fxGetResults($sqlBibAssignments); ?>

bib_assignment?a=show_bib_dupes&eve_id=" target="_blank">
fxGetVar($sqlLastBib); } else $intBibFinish = ''; $sqlCommandees = "SELECT COUNT(*) FROM inscriptions_panier_epreuves_commandees e, inscriptions_panier_acheteurs a WHERE e.epr_id = " . intval($tabEpreuves[$i]['epr_id']) . " AND e.no_panier = a.no_panier AND a.sta_id = 3"; $intQteCommandees = $objDatabase->fxGetVar($sqlCommandees); $sqlInscriptions = 'select p.no_bib, p.par_id, p.par_prenom, p.par_nom, e.pec_id_original, e.epr_id, e.eve_id, e.pec_id, e.is_cancelled, p.rol_id, e.no_commande, e.no_panier, e.no_equipe from resultats_epreuves_commandees e, resultats_participants p where p.is_cancelled = 0 AND p.epr_id = ' . intval($tabEpreuves[$i]['epr_id']) . ' AND p.epr_id = e.epr_id and p.pec_id = e.pec_id_original group by e.pec_id_original'; $tabInscriptions = $objDatabase->fxGetResults($sqlInscriptions); $intNbInscriptions = count($tabInscriptions); // nb bibs used $sqlCount = "SELECT count(*) FROM resultats_participants WHERE epr_id = " . intval($tabEpreuves[$i]['epr_id']) . " AND (no_bib <> '' AND no_bib <> 0)"; $intNbBibs = $objDatabase->fxGetVar($sqlCount); // new calcul de num_bibs_left if($tabEpreuves[$i]['epr_bib_finish'] == 0){ $intNbBibsLeft = ($intQteCommandees + $tabEpreuves[$i]['epr_qte']) - $intNbBibs; } else { $intNbBibsLeft = (($intBibStart + $intNbBibs) - $intBibStart) - $intNbBibs; } ?>

"; } echo $tabEpreuves[$i]['epr_type_' . $strLangue]; if (trim($tabEpreuves[$i]['epr_nom_' . $strLangue]) != '') { echo "
" . $tabEpreuves[$i]['epr_nom_' . $strLangue]; } ?>





fxGetResults($sqlEpreuves); $sqlBibAssignments = "SELECT * FROM bib_assignements WHERE ba_actif = 1 ORDER BY ba_tri"; $tabBibAssignments = $objDatabase->fxGetResults($sqlBibAssignments); ?>

fxGetVar($sqlLastBib); } else $intBibFinish = ''; $sqlCommandees = "SELECT COUNT(*) FROM inscriptions_panier_epreuves_commandees e, inscriptions_panier_acheteurs a WHERE e.epr_id = " . intval($tabEpreuves[$i]['epr_id']) . " AND e.no_panier = a.no_panier AND a.sta_id = 3"; $intQteCommandees = $objDatabase->fxGetVar($sqlCommandees); $sqlInscriptions = 'select p.no_bib, p.par_id, p.par_prenom, p.par_nom, e.pec_id_original, e.epr_id, e.eve_id, e.pec_id, e.is_cancelled, p.rol_id, e.no_commande, e.no_panier, e.no_equipe from resultats_epreuves_commandees e, resultats_participants p where p.is_cancelled = 0 AND p.epr_id = ' . intval($tabEpreuves[$i]['epr_id']) . ' AND p.epr_id = e.epr_id and p.pec_id = e.pec_id_original group by e.pec_id_original'; $tabInscriptions = $objDatabase->fxGetResults($sqlInscriptions); $intNbInscriptions = count($tabInscriptions); // nb bibs used $sqlCount = "SELECT count(*) FROM resultats_participants WHERE epr_id = " . intval($tabEpreuves[$i]['epr_id']) . " AND (no_bib <> '' AND no_bib <> 0)"; $intNbBibs = $objDatabase->fxGetVar($sqlCount); // new calcul de num_bibs_left if($tabEpreuves[$i]['epr_bib_finish'] == 0){ $intNbBibsLeft = ($intQteCommandees + $tabEpreuves[$i]['epr_qte']) - $intNbBibs; } else { $intNbBibsLeft = (($intBibStart + $intNbBibs) - $intBibStart) - $intNbBibs; } ?>

"; } echo $tabEpreuves[$i]['epr_type_' . $strLangue]; if (trim($tabEpreuves[$i]['epr_nom_' . $strLangue]) != '') { echo "
" . $tabEpreuves[$i]['epr_nom_' . $strLangue]; } ?>





fxGetVar($sql); } function fxShowBibNumber($str_bib, $int_epr_id, $type){ // enlever no. epr du no bib $str_return = str_replace($int_epr_id . "-", "", $str_bib); switch($type){ case 'equipe': break; case 'participant': $str_return = str_replace("-", "", $str_return); break; } return $str_return; } function fxShowRapports($tabRapports, $strLangue, $intEvenement) { global $vDomaine; $arrEvenement = fxGetEvenementsId($intEvenement); ?>

'' AND is_cancelled = 0 GROUP BY no_bib, epr_id HAVING c > 1"; $tabIntraDupes = $objDatabase->fxGetResults($sqlNoBib); $tabInterDupes = []; if (!fxBibEventAllowsInterEprDuplicates((int)$int_eve_id)) { $sqlInter = "SELECT no_bib FROM resultats_participants WHERE eve_id = " . intval($int_eve_id) . " AND no_bib <> '' AND is_cancelled = 0 GROUP BY no_bib HAVING COUNT(DISTINCT epr_id) > 1"; $tabInterDupes = $objDatabase->fxGetResults($sqlInter); } $tabBibKeys = []; if (is_array($tabIntraDupes)) { foreach ($tabIntraDupes as $row) { $tabBibKeys[(int)$row['no_bib']] = true; } } if (is_array($tabInterDupes)) { foreach ($tabInterDupes as $row) { $tabBibKeys[(int)$row['no_bib']] = true; } } ?>

fxGetResults($sqlDupes); if ($tabDupes != null) { foreach ($tabDupes as $dupe) { $tabEpreuve = fxGetEpreuve($dupe['epr_id']); ?>
"; } echo $tabEpreuve['epr_type_' . $strLangue]; if (trim($tabEpreuve['epr_nom_' . $strLangue]) != '') { echo "
" . $tabEpreuve['epr_nom_' . $strLangue]; } echo " (" . $tabEpreuve['epr_id'] . ")"; ?>
fxQuery($sqlTri); $strOrderBy = ''; // set ORDER By switch($tab_epreuve['ba_id']){ case 1: $strOrderBy = " ORDER BY TRIM(p.par_prenom)"; break; case 2: $strOrderBy = " ORDER BY TRIM(p.par_nom), TRIM(p.par_prenom)"; break; case 3: $strOrderBy = " ORDER BY p.par_maj"; break; case 4: $strOrderBy = " ORDER BY p.par_naissance DESC"; break; } //get inscriptions $sqlInscriptions = "SELECT * FROM resultats_participants p WHERE p.is_cancelled = 0 AND p.epr_id = " . intval($tab_epreuve['epr_id']) . $strOrderBy; $tabInscriptions = $objDatabase->fxGetResults($sqlInscriptions); // get epreuves_commandees (pour les equipes) $sqlEpreuvesCommandees = "select * from resultats_epreuves_commandees c, resultats_participants p where c.epr_id = " . intval($tab_epreuve['epr_id']) . " and c.pec_actif = 1 and c.pec_id_original = p.pec_id GROUP BY pec_id_original" . $strOrderBy; $tabEpreuvesCommandees = $objDatabase->fxGetResults($sqlEpreuvesCommandees); ?>


"> ">
"; if ($tab_epreuve['epr_type_' . $strLangue] != '') echo $tab_epreuve['epr_type_' . $strLangue]; echo " (" . $tab_epreuve['epr_id'] . ")"; ?>
"; if ($tab_epreuve['epr_type_' . $strLangue] != '') echo $tab_epreuve['epr_type_' . $strLangue]; echo " (" . $tab_epreuve['epr_id'] . ")"; ?>
    fxGetResults($sqlEquipe); for($j = 1; $j <= count($tabEquipe); $j++){ if($tabEquipe[$j]['rol_id'] != 3 && $tabEquipe[$j]['rol_id'] != 4) { ?>
fxQuery($sqlUpdate); if (!$qryUpdate) { $blnError = true; fxcreer_log('Erreur lors de la reinitialisation des infos de dossards de l\'epreuve.'); } // resultats_epreuves_commandees $sqlUpdate = "UPDATE resultats_epreuves_commandees SET no_equipe = null WHERE epr_id = " . intval($int_epr_id); $qryUpdate = $objDatabase->fxQuery($sqlUpdate); if (!$qryUpdate) { $blnError = true; fxcreer_log('Erreur lors de la reinitialisation des infos de dossards de la commande.'); } // resultats_participants $sqlUpdate = "UPDATE resultats_participants SET no_bib = null, par_date_bib = null WHERE epr_id = " . intval($int_epr_id); $qryUpdate = $objDatabase->fxQuery($sqlUpdate); if (!$qryUpdate) { $blnError = true; fxcreer_log('Erreur lors de la reinitialisation des numeros de dossards des participants.'); } if ($blnError) { ?>
Reset Problems - Try again


fxGetResults($sqlInscriptions); $sqlEpreuves = "SELECT * FROM inscriptions_epreuves WHERE epr_id = " . intval($int_epr_id); $tabEpreuves = $objDatabase->fxGetRow($sqlEpreuves); if ($int_par_equipe == 0) { if ($int_finish == 0) $int_finish = $int_start + count($tabInscriptions); for ($i = 1; $i <= count($tabInscriptions); $i++) { if ($tabInscriptions[$i]['rol_id'] != 3 && $tabInscriptions[$i]['rol_id'] != 4) { $sqlUpdate = "UPDATE resultats_participants "; if ($str_action == 'reset') { $sqlUpdate .= "SET no_bib = '', par_date_bib = null"; // $sqlUpdate .= "SET no_bib = ''"; } else { if($tabInscriptions[$i]['par_date_bib'] == null){ $sqlUpdate .= "SET par_date_bib = '" . fxGetDateTime() . "', no_bib = '" . $objDatabase->fxEscape($intNoBib + $intCtr) . "'"; } else{ $sqlUpdate .= "SET no_bib = '" . $objDatabase->fxEscape($intNoBib + $intCtr) . "'"; // $sqlUpdate .= "SET no_bib = '" . $objDatabase->fxEscape($intNoBib + $intCtr) . "', par_maj = '" . fxGetDateTime() . "'"; // $sqlUpdate .= "SET no_bib = '" . $objDatabase->fxEscape($intNoBib + $intCtr) . "', par_modification_promoteur = 1, par_maj = '" . fxGetDateTime() . "'"; } } $sqlUpdate .= " WHERE par_id = " . $tabInscriptions[$i]['par_id'] . " AND is_cancelled = 0"; $qryUpdate = $objDatabase->fxQuery($sqlUpdate); if (!$qryUpdate) fxcreer_log('Erreur lors de mise a jour du numero de dossard du participant ' . $tabInscriptions[$i]['par_id']); $sqlUpdate = "UPDATE resultats_epreuves_commandees SET no_equipe = '" . $objDatabase->fxEscape($intNoBib + $intCtr) . "' WHERE epr_id = " . intval($tabInscriptions[$i]['epr_id']) . " AND pec_id_original = " . $tabInscriptions[$i]['pec_id']; $qryUpdate = $objDatabase->fxQuery($sqlUpdate); if (!$qryUpdate) fxcreer_log('Erreur lors de mise a jour du numero de dossard de l\'equipe." ' . $int_epr_id); $intCtr++; } if( $intNoBib + $intCtr>$int_finish_dispo){ break; } } } // assigner no_equipe (+ no_bib) if ($int_par_equipe == 1) { if ($int_finish == 0) { $int_finish = count($tab_data); } $intNumeroEquipe= $int_start; for ($i = 1; $i <= count($tab_data); $i++) { $strNumeroEquipe = $objDatabase->fxEscape($intNumeroEquipe+ $intCtr); $sqlUpdate = "UPDATE resultats_epreuves_commandees SET no_equipe = '" . $strNumeroEquipe. "' WHERE epr_id = " . intval($tab_data[$i]['epr_id']) . " AND pec_id_original = " . $tab_data[$i]['pec_id']; $qryUpdate = $objDatabase->fxQuery($sqlUpdate); if (!$qryUpdate) fxcreer_log('Erreur lors de mise a jour du numero de dossard de l\'equipe." ' . $int_epr_id); $intCtrBib = 1; // assigner no_bib aux membres de lequipe // get membres de lequipe $sqlEquipe = "SELECT * FROM resultats_participants p WHERE pec_id = " . $tab_data[$i]['pec_id_original'] . $strOrderBy; $tabEquipe = $objDatabase->fxGetResults($sqlEquipe); for ($j = 1; $j <= count($tabEquipe); $j++) { if ($tabEquipe[$j]['rol_id'] != 3 && $tabEquipe[$j]['rol_id'] != 4) { if($int_mode == 0) { if ($tabEquipe[$j]['rol_id'] == 1) { $intNoBib = $strNumeroEquipe . "0"; } else { $intNoBib = $objDatabase->fxEscape($strNumeroEquipe . $intCtrBib);// "1234"; $intCtrBib++; } } else{ $intNoBib = $strNumeroEquipe; } if($tabInscriptions[$i]['par_date_bib'] == null){ $sqlUpdatePart = "UPDATE resultats_participants SET par_date_bib = '" . fxGetDateTime() . "', no_bib = '" . $objDatabase->fxEscape($intNoBib). "' WHERE par_id = " . intval($tabEquipe[$j]['par_id']); } else{ $sqlUpdatePart = "UPDATE resultats_participants SET no_bib = '" . $objDatabase->fxEscape($intNoBib) . "' WHERE par_id = " . intval($tabEquipe[$j]['par_id']); // $sqlUpdatePart = "UPDATE resultats_participants SET par_maj = '" . fxGetDateTime() . "', no_bib = '" . $objDatabase->fxEscape($intNoBib) . "' WHERE par_id = " . intval($tabEquipe[$j]['par_id']); // $sqlUpdatePart = "UPDATE resultats_participants SET par_maj = '" . fxGetDateTime() . "', par_modification_promoteur = 1, no_bib = '" . $objDatabase->fxEscape($intNoBib) . "' WHERE par_id = " . intval($tabEquipe[$j]['par_id']); } $qryUpdatePart = $objDatabase->fxQuery($sqlUpdatePart); if (!$qryUpdatePart) fxcreer_log('Erreur lors de mise a jour du numero de dossard du participant ' . $tabInscriptions[$i]['par_id']); } } $intCtr++; } } // update epreuve $sqlUpdate = "UPDATE inscriptions_epreuves SET epr_bib_finish_dispo=".$int_finish_dispo.", epr_bib_start = " . intval($int_start) . ", epr_bib_finish = " . intval($int_finish) . ", epr_bib_mode = " . intval($int_mode) . ", ba_id = " . intval($int_type) . " WHERE epr_id = " . intval($int_epr_id); $qryUpdate = $objDatabase->fxQuery($sqlUpdate); if (!$qryUpdate) fxcreer_log('Erreur lors de mise a jour information dossard" ' . $int_epr_id); // fxShowBibNumbersTable($tab_data, $strLangue); } } function fxShowColumnPromoteur($arrColonnes, $arrListeParticipants, $x, $strLangue, $intEquipe = 0,$abonnement=0) { global $vDomaine ; $strResults = ''; foreach ($arrColonnes as $arrCol) { $arrFields = explode(',', $arrCol['field']); $strAffichage = $arrCol['mask']; switch ($arrCol['format']) { // vérifier le format d'affichage case 'bib': if ($abonnement){ $membre=fxGetAbonnement(0,0,0,$arrListeParticipants[$x]['pec_id_original']); isset($membre['mem_numero'])? $m_membre=$membre['mem_numero'] :$m_membre=""; $strAffichage = '
' . $m_membre . '
'; } else { $intValue = (intval($arrListeParticipants[$x]['pec_equipe']) == 1) ? fxShowBibNumber($arrListeParticipants[$x]['no_equipe'], $arrListeParticipants[$x]['epr_id'], 'equipe') : fxShowBibNumber($arrListeParticipants[$x]['no_bib'], $arrListeParticipants[$x]['epr_id'], 'participant'); $strAffichage = '
'; if (intval($intEquipe) == 1) { $strAffichage .= afficheTexte('promoteur_bib_no_equipe', 0); $strAction = 'no_equipe'; $strId = 'txt_no_bib_e'; } else { $strAffichage .= afficheTexte('promoteur_bib_no_participants', 0); $strAction = 'no_bib'; $strId = 'txt_no_bib_p'; } $strAffichage .= '
'; } break; case 'management': $strCode = fxGetEvenementsUrl($arrListeParticipants[$x]['eve_id']); $strCancelBadge = ''; if ($arrListeParticipants[$x]['is_cancelled'] == '' || $arrListeParticipants[$x]['is_cancelled'] == '0') { $strStyleEdit = $strStyleCancel = ''; $strStyleRetablir = ' d-none'; } else { $strStyleEdit = $strStyleCancel = ' d-none'; $strStyleRetablir = ''; // MSIN-4405 — Cause de l'annulation derivee en lecture seule (transferee / annulee). $strCause = fxGetAnnulationCause($arrListeParticipants[$x]['pec_id_original'], isset($arrListeParticipants[$x]['pec_actif']) ? $arrListeParticipants[$x]['pec_actif'] : null); if ($strCause == 'transfert') { $strBadgeLbl = ($strLangue == 'fr') ? 'Transférée' : 'Transferred'; } else { $strBadgeLbl = ($strLangue == 'fr') ? 'Annulée' : 'Cancelled'; } $strCancelBadge = '
' . htmlspecialchars($strBadgeLbl, ENT_QUOTES, 'UTF-8') . '
'; } $strAffichage = $strCancelBadge . ' '; break; case 'details': // bib reception sl affichage check box // print_r($arrListeParticipants[$x]); $intValeur = 0; if ($arrListeParticipants[$x]['no_bib_remis'] == 1) { $strChecked = ' checked'; $intValeur = 0; $strClass = 'btn-success'; $strImage = ''; } else { $strChecked = ""; $intValeur = 1; $strClass = 'btn-primary'; $strImage = ''; } if ($intEquipe == 1 && fxIsParEquipe($arrListeParticipants[$x]['pec_id_original'], $arrListeParticipants[$x]['epr_id'])) { $strAffichage .= ' '; } else { $strAffichage .= afficheTexte('epreuve_individuelle', 0); } break; case 'edit': $strAffichage = fxCheckMAJ($arrListeParticipants[$x]['pec_id_original'], $arrListeParticipants[$x]['par_id'], $strLangue); // a faire sl mettre les deux langues $strAffichage .='

'.afficheTexte('promoteur_bib_enregistre', 0).'
'. $strImage.' '; $strAffichage .='
'; $strAffichage .='
'; $strAffichage .='
'; $strAffichage .=$arrListeParticipants[$x]['no_bib_remis_date']; $strAffichage .='
'; $strAffichage .='
'; $strAffichage .=$arrListeParticipants[$x]['no_bib_remis_par']; break; case 'factures': case 'confirmm': $arrFactures = fxGetFactures($arrListeParticipants[$x]['pec_id_original']); // trouver toutes les factures pour cette épreuve if ($arrFactures != null) { $strAffichageTemp = ''; for ($intCtr2 = 1; $intCtr2 <= count($arrFactures); $intCtr2++) { $strAffichageTemp .= $strAffichage; if ($intCtr2 > 1) { $strAffichageTemp .= '
'; } foreach ($arrFields as $intIndex => $strField) { // remplacer {x} dans le mask de la colonne $strAffichageTemp = str_replace('{' . $intIndex . '}', $arrListeParticipants[$x][fxchecklanguePromoteur($strField, $arrCol['multilingue'], $strLangue)], $strAffichageTemp); } } $strAffichage = $strAffichageTemp; } break; case 'race': foreach ($arrFields as $intIndex => $strField) { // remplacer {x} dans le mask de la colonne $strReplace = fxGetTabInscription(fxchecklanguePromoteur($strField, $arrCol['multilingue'], $strLangue), $arrListeParticipants[$x]['epr_id'], 'epreuves', 'epr'); if (trim($strReplace) != '') { $strReplace .= '
'; } $strAffichage = str_replace('{' . $intIndex . '}', $strReplace, $strAffichage); } break; case 'money': //format dollar foreach ($arrFields as $intIndex => $strField) { // remplacer {x} dans le mask de la colonne $strAffichage = str_replace('{' . $intIndex . '}', fxShowPrix($arrListeParticipants[$x][fxchecklanguePromoteur($strField, $arrCol['multilingue'], $strLangue)], $strLangue), $strAffichage); } break; default: if ($arrCol['format'] == 'participant') { if (fxIsParEquipe($arrListeParticipants[$x]['pec_id_original'], $arrListeParticipants[$x]['epr_id'])) { $tabCapitaine = fxGetCapitaine($arrListeParticipants[$x]['pec_id'], $arrListeParticipants[$x]['epr_id']); if ($tabCapitaine != null) { $arrListeParticipants[$x]['par_prenom'] = $tabCapitaine['par_prenom']; $arrListeParticipants[$x]['par_nom'] = $tabCapitaine['par_nom']; } /* else afficheTexte('promoteur_no_captain'); */ } } foreach ($arrFields as $intIndex => $strField) { // remplacer {x} dans le mask de la colonne // $strField ici {}on change par la valeur du champ le champ $strAffichage = str_replace('{' . $intIndex . '}', $arrListeParticipants[$x][fxchecklanguePromoteur($strField, $arrCol['multilingue'], $strLangue)], $strAffichage); } // MSIN-3679 patch eve_id $eve_idcode=urlencode(base64_encode($arrListeParticipants[$x]['eve_id'])) ; $strAffichage = str_replace('{99}',$eve_idcode, $strAffichage); } if ($arrCol['format'] == 'team') { //si épreuve solo, ne pas afficher lev nom d'équipe if ($intEquipe != 1) { $strAffichage = ''; } } /* if (count($arrFields) == 1 && trim($arrListeParticipants[$x][fxchecklanguePromoteur($arrFields[0], $arrCol['multilingue'], $strLangue)]) == '') { // si la valeur est vide, ne rien afficher $strAffichage = ''; } */ $strResults .= '' . $strAffichage . ''; } return $strResults; } function fxchecklanguePromoteur($strField, $intMultilingue, $langues) { if (intval($intMultilingue) == 1) return $strField . "_" . $langues; else return $strField; } function fxGetchampsPromoteur($blnDon, $strLangue) { global $vDomaine, $vRepertoireFichiers; if ($blnDon) { $arrColonnes = array( 1 => array( 'field' => 'com_nom,com_prenom', 'align' => 'left', 'format' => 'string', 'mask' => '{0}, {1}', 'multilingue' => 0, 'label_fr' => 'Donateur', 'label_en' => 'Donator' ), 2 => array( 'field' => 'epr_categorie,epr_type,epr_nom', 'align' => 'left', 'format' => 'race', 'mask' => '{0}{1}{2}', 'multilingue' => 1, 'label_fr' => 'Épreuve', 'label_en' => 'Race' ), 3 => array( 'field' => 'pec_nom_equipe', 'align' => 'left', 'format' => 'team', 'mask' => '{0}', 'multilingue' => 0, 'label_fr' => ' Équipe', 'label_en' => 'Team' ), 4 => array( 'field' => 'par_nom,par_prenom', 'align' => 'left', 'format' => 'string', 'mask' => '{0}, {1}', 'multilingue' => 0, 'label_fr' => 'Participant', 'label_en' => 'Participant' ), 5 => array( 'field' => 'pd_montant', 'align' => 'right', 'format' => 'money', 'mask' => '{0}', 'multilingue' => 0, 'label_fr' => 'Montant', 'label_en' => 'Amount' ), 6 => array( 'field' => 'no_panier,no_commande', 'align' => 'left', 'format' => ' factures', 'mask' => '{1}', 'multilingue' => 0, 'label_fr' => '# commande', 'label_en' => 'Order #' ), 7 => array( 'field' => 'pd_recu_pdf', 'align' => 'left', 'format' => 'string', 'mask' => 'PDF', 'multilingue' => 0, 'label_fr' => 'Reçu', 'label_en' => 'Receipt' ) ); } else { $strRenvoi = ($strLangue == 'fr') ? "RENVOI" : "RESEND"; $arrColonnes = array( 1 => array( 'field' => 'epr_categorie,epr_type,epr_nom', 'align' => 'left', 'format' => 'race', 'mask' => '{0}{1}{2}', 'multilingue' => 1, 'label_fr' => 'Épreuve', 'label_en' => 'Race' ), 2 => array( 'field' => 'par_nom,par_prenom', 'align' => 'left', 'format' => '^particpant', 'mask' => '{0}, {1}', 'multilingue' => 0, 'label_fr' => 'Nom du participant', 'label_en' => 'Participant\'s name' ), 3 => array( 'field' => '', 'align' => 'left', 'format' => 'bib', 'mask' => '', 'multilingue' => 0, 'label_fr' => afficheTexte('promoteur_bib_number', 0), 'label_en' => afficheTexte('promoteur_bib_number', 0) ), 4 => array( 'field' => '', 'align' => 'left', 'format' => 'details', 'mask' => '', 'multilingue' => 0, 'label_fr' => 'Détails', 'label_en' => 'Details' ),5 => array( 'field' => '', 'align' => 'left', 'format' => 'management', 'mask' => '', 'multilingue' => 0, 'label_fr' => 'Gestion', 'label_en' => 'Management' ), 6 => array( 'field' => '', 'align' => 'left', 'format' => 'edit', 'mask' => '', 'multilingue' => 0, 'label_fr' => 'Modifiée', 'label_en' => 'Modified' ), 7 => array( 'field' => 'no_panier,no_commande', 'align' => 'left', 'format' => 'string',// MSIN-3679 'mask' => '{1}
', 'multilingue' => 0, 'label_fr' => '# commande', 'label_en' => 'Order #' ), 8 => array( 'field' => 'no_panier,no_commande', 'align' => 'left', 'format' => 'confirm', 'mask' => '', 'multilingue' => 0, 'label_fr' => 'Confirmation', 'label_en' => 'Confirmation' ) ); } return $arrColonnes; } function fxSetBibManuel($strAction, $intEvenement, $intNewNoBib, $intEpreuveCommandee, $intEpreuve, $intPartipcipant, $strLangue = 'fr') { global $objDatabase; $arrRetour = array('state' => 'error'); $intEveId = base64_decode(urldecode($intEvenement)); $_SESSION['promoteur_eve_id'] = base64_decode(urldecode($intEvenement)); $sqlEpreuve = "SELECT * FROM inscriptions_epreuves WHERE epr_id = " . intval($intEpreuve); $tabEpreuve = $objDatabase->fxGetRow($sqlEpreuve); $strBib = $intNewNoBib; $sqlTeam = "SELECT * FROM resultats_participants WHERE pec_id = " . intval($intEpreuveCommandee) . " ORDER BY no_bib"; $tabTeam = $objDatabase->fxGetResults($sqlTeam); switch($strAction) { case 'no_bib': if ($tabEpreuve['epr_equipe'] == 0 || ($tabEpreuve['epr_equipe'] == 1 && $tabEpreuve['epr_bib_mode'] == 1)) { // verifier si new_no_bib existe $sqlNoBib = "SELECT no_bib FROM resultats_participants WHERE no_bib LIKE '%" . "-" . $intNewNoBib . "' AND eve_id = " . intval($intEveId); $qryNoBib = $objDatabase->fxGetResults($sqlNoBib); if ($qryNoBib != null) { if ($strLangue == 'fr') $_SESSION['msg'] = 'p155'; else $_SESSION['msg'] = 'p555'; } $sqlUpdate = "UPDATE resultats_participants SET par_modification_promoteur = 1, no_bib = '" . fxUnescape($strBib) . "', par_maj = '" . fxGetDateTime() . "' WHERE epr_id = " . intval($intEpreuve) . " AND par_id = " . intval($intPartipcipant); $qryUpdate = $objDatabase->fxQuery($sqlUpdate); if (!$qryUpdate) { fxcreer_log("Erreur durant assignation manuelle du numero de dossard (" . $intPartipcipant . ")"); } else { if (function_exists('fxEvtErreurResoudre')) { fxEvtErreurResoudre('bib_auto_miss', (int)$intEpreuve, (int)$intPartipcipant); } $arrRetour['state'] = 'success'; } } elseif($tabEpreuve['epr_equipe'] == 1 && $tabEpreuve['epr_bib_mode'] == 0) { $sqlUpdateTeamPart = "UPDATE resultats_participants SET par_modification_promoteur = 1, no_bib = '" . $intNewNoBib . "', par_maj = '" . fxGetDateTime() . "' WHERE par_id = " . intval($intPartipcipant); $qryUpdateTeam = $objDatabase->fxQuery($sqlUpdateTeamPart); if (!$qryUpdateTeam) { fxcreer_log("Erreur durant assignation manuelle du numero de dossard (" . $intPartipcipant . ")"); } else { if (function_exists('fxEvtErreurResoudre')) { fxEvtErreurResoudre('bib_auto_miss', (int)$intEpreuve, (int)$intPartipcipant); } $arrRetour['state'] = 'success'; } } break; case 'no_equipe': if ($tabEpreuve['epr_equipe'] == 1 && $strAction == 'no_equipe') { // verifier si new_no_bib existe $sqlNoBib = "SELECT no_equipe FROM resultats_epreuves_commandees WHERE no_equipe LIKE '%" . $intNewNoBib . "' AND eve_id = " . intval($intEveId); $qryNoBib = $objDatabase->fxGetResults($sqlNoBib); if ($qryNoBib != null) { if ($strLangue == 'fr') $_SESSION['msg'] = 'p156'; else $_SESSION['msg'] = 'p556'; } if ($strAction == 'no_equipe') { // update no_equipe $sqlUpdate = "UPDATE resultats_epreuves_commandees SET no_equipe = '" . fxUnescape($strBib) . "' WHERE pec_id_original = " . intval($intEpreuveCommandee); $qryUpdate = $objDatabase->fxQuery($sqlUpdate); } $intCtr = 1; for ($i = 1; $i <= count($tabTeam); $i++) { if ($strAction == 'no_equipe' && $tabEpreuve['epr_bib_mode'] == 0) { if (intval($tabTeam[$i]['rol_id']) == 1) { $sqlUpdatePart = "UPDATE resultats_participants SET par_modification_promoteur = 1, no_bib = " . intval($strBib) * 10 . ", par_maj = '" . fxGetDateTime() . "' WHERE par_id = " . intval($tabTeam[$i]['par_id']); } else { $sqlUpdatePart = "UPDATE resultats_participants SET par_modification_promoteur = 1, no_bib = '" . $strBib . $intCtr . "', par_maj = '" . fxGetDateTime() . "' WHERE par_id = " . intval($tabTeam[$i]['par_id']); $intCtr++; } $qryUpdatePart = $objDatabase->fxQuery($sqlUpdatePart); if (!$qryUpdate) { fxcreer_log("Erreur durant assignation manuelle du numero d'équipe (" . $intEpreuveCommandee . ")"); } } else { $sqlUpdatePart = "UPDATE resultats_participants SET par_modification_promoteur = 1, no_bib = '" . $strBib . "', par_maj = '" . fxGetDateTime() . "' WHERE par_id = " . intval($tabTeam[$i]['par_id']); $qryUpdatePart = $objDatabase->fxQuery($sqlUpdatePart); if (!$qryUpdatePart) { fxcreer_log("Erreur durant assignation manuelle du numero de dossard (" . $intPartipcipant . ")"); } } } if (!$qryUpdate) { fxcreer_log("Erreur durant assignation manuelle du numero d'équipe (" . $intEpreuveCommandee . ")"); } else { $arrRetour['state'] = 'success'; } } break; } return $arrRetour; } function fxSetParStatutCourse($intPartipcipant, $strStatutCode, $strLangue = 'fr') { global $objDatabase; $arrRetour = array('state' => 'error'); $intParId = intval($intPartipcipant); $strCode = trim((string)$strStatutCode); if ($intParId <= 0 || $strCode === '') { return $arrRetour; } $sqlCheck = "SELECT 1 FROM info WHERE info_actif = 1 AND info_clef = 'inscr_statut_course'" . " AND info_description = 'opt' AND info_option2 = '" . $objDatabase->fxEscape($strCode) . "' LIMIT 1"; if ($objDatabase->fxGetRow($sqlCheck) === null) { return $arrRetour; } $sqlUpdate = "UPDATE resultats_participants SET par_modification_promoteur = 1," . " par_statut_course = '" . $objDatabase->fxEscape($strCode) . "'," . " par_maj = '" . fxGetDateTime() . "'" . " WHERE par_id = " . $intParId; if ($objDatabase->fxQuery($sqlUpdate)) { $arrRetour['state'] = 'success'; } else { fxcreer_log('Erreur durant mise a jour statut course (' . $intParId . ')'); } return $arrRetour; } function fxShowTeammates($intEvenement, $intEpreuve, $intEpreuveCommandee) { $arrRetour = array('state' => 'error'); $tabEquipe = fxGetMembresEquipe($intEvenement, $intEpreuveCommandee, $intEpreuve, 0, 1); if ($tabEquipe != null) { $arrRetour['state'] = 'success'; $arrRetour['team'] = fxUnescape($tabEquipe[1]['pec_nom_equipe']); $strMembers = ''; for ($i = 1; $i <= count($tabEquipe); $i++) { $strMembers .= '
'; } $arrRetour['members'] = $strMembers; } return $arrRetour; } function fxGetQuantitesEvenement($intEpreuve) { global $objDatabase; $arrRetour = array(); $intTotalOriginal = $intTotalAvailable = $intTotalBooked = $intTotalCurrent = $intTotalAjustement = 0; // récupérer toutes les épreuves de l'événement de $intEpreuve $sqlEpreuves = "SELECT epr_id FROM inscriptions_epreuves WHERE eve_id = (SELECT DISTINCT eve_id FROM inscriptions_epreuves WHERE epr_id = " . intval($intEpreuve) . ") AND epr_actif = 1"; $arrEpreuves = $objDatabase->fxGetResults($sqlEpreuves); if ($arrEpreuves != null) { for ($intCtr = 1; $intCtr <= count($arrEpreuves); $intCtr++) { $tabEpreuve = fxGetEpreuve($arrEpreuves[$intCtr]['epr_id']); $sqlInscriptions = 'select p.no_bib, p.par_id, p.par_prenom, p.par_nom, e.pec_id_original, e.epr_id, e.eve_id, e.pec_id, e.is_cancelled, p.rol_id, e.no_commande, e.no_panier, e.no_equipe from resultats_epreuves_commandees e, resultats_participants p where p.is_cancelled = 0 AND p.epr_id = ' . intval($tabEpreuve['epr_id']) . ' AND p.epr_id = e.epr_id and p.pec_id = e.pec_id_original group by e.pec_id_original'; $tabInscriptions = $objDatabase->fxGetResults($sqlInscriptions); $intNbInscriptions = count($tabInscriptions); $intTotalOriginal += $tabEpreuve['epr_qte_origine']; $intTotalAvailable += $tabEpreuve['epr_qte']; $intTotalBooked += $intNbInscriptions; $intTotalCurrent += intval($intNbInscriptions) + intval($tabEpreuve['epr_qte']); $intTotalAjustement += intval($intNbInscriptions) + intval($tabEpreuve['epr_qte']) - intval($tabEpreuve['epr_qte_origine']); $checkin=fxGetCheckinqte($arrEpreuves[$intCtr]['epr_id']); $arrRetour[$arrEpreuves[$intCtr]['epr_id']] = array( 'qte_origine' => intval($tabEpreuve['epr_qte_origine']), 'qte_restante' => intval($tabEpreuve['epr_qte']), 'qte_inscrits' => intval($intNbInscriptions), 'qte_courante' => intval($intNbInscriptions) + intval($tabEpreuve['epr_qte']), 'qte_ajustement' => intval($intNbInscriptions) + intval($tabEpreuve['epr_qte']) - intval($tabEpreuve['epr_qte_origine']), 'qte_checkin' => intval($checkin) ); } $arrRetour['total'] = array( 'qte_origine_total' => $intTotalOriginal, 'qte_restante_total' => $intTotalAvailable, 'qte_inscrits_total' => $intTotalBooked, 'qte_courante_total' => $intTotalCurrent, 'qte_ajustement_total' => $intTotalAjustement ); } return $arrRetour; } function fxGetQuantites($intEpreuve) { global $objDatabase; $tabEpreuve = fxGetEpreuve($intEpreuve); $sqlCommandees = "SELECT COUNT(*) FROM inscriptions_panier_epreuves_commandees e, inscriptions_panier_acheteurs a WHERE e.epr_id = " . intval($tabEpreuve['epr_id']) . " AND e.no_panier = a.no_panier AND a.sta_id = 3"; $intQteCommandees = $objDatabase->fxGetVar($sqlCommandees); $sqlInscriptions = 'select p.no_bib, p.par_id, p.par_prenom, p.par_nom, e.pec_id_original, e.epr_id, e.eve_id, e.pec_id, e.is_cancelled, p.rol_id, e.no_commande, e.no_panier, e.no_equipe from resultats_epreuves_commandees e, resultats_participants p where p.is_cancelled = 0 AND p.epr_id = ' . intval($intEpreuve) . ' AND p.epr_id = e.epr_id and p.pec_id = e.pec_id_original group by e.pec_id_original'; $tabInscriptions = $objDatabase->fxGetResults($sqlInscriptions); $intNbInscriptions = count($tabInscriptions); $arrRetour = array( 'qte_origine' => intval($tabEpreuve['epr_qte_origine']), 'qte_restante' => intval($tabEpreuve['epr_qte']), 'qte_inscrits' => intval($intNbInscriptions), 'qte_courante' => intval($intNbInscriptions) + intval($tabEpreuve['epr_qte']), 'qte_ajustement' => intval($intNbInscriptions) + intval($tabEpreuve['epr_qte']) - intval($tabEpreuve['epr_qte_origine']) ); return $arrRetour; } function fxUpdateQuantites($intEpreuve, $intQuantite, $strLangue) { global $objDatabase; $arrRetour = array('state' => 'error'); if ($intQuantite >= 0) { $tabEpreuve = fxGetEpreuve($intEpreuve); $intDifference = $intQuantite - $tabEpreuve['epr_qte']; $sqlUpdate = "UPDATE inscriptions_epreuves SET epr_qte_ajoutee = epr_qte_ajoutee + " . intval($intDifference) . ", epr_qte = epr_qte + " . intval($intDifference) . " WHERE epr_id = " . intval($intEpreuve); $qryUpdate = $objDatabase->fxQuery($sqlUpdate); if ($qryUpdate) { if($strLangue == 'fr') $_SESSION['msg'] = 'p154'; else $_SESSION['msg'] = 'p554'; } $arrRetour['data'] = fxGetQuantitesEvenement($intEpreuve); $arrRetour['state'] = 'success'; } return $arrRetour; } // v2 — encart « choisissez vos séquences » (batch + auto) function renderBibBatchPickPrompt() { ob_start(); ?>

0) { return 'ba:' . (int)$mix; } $str = trim((string)$mix); if (preg_match('/^(ba|que):\d+$/', $str)) { return $str; } return ''; } /** MSIN-4436 — Clé de tri depuis ba_id legacy. */ function fxBibSortKeyFromBaId($int_ba_id) { $int_ba_id = (int)$int_ba_id; return $int_ba_id > 0 ? 'ba:' . $int_ba_id : ''; } /** MSIN-4436 — Tris mémorisés sur l'épreuve (repli sur ba_id). */ function fxBibGetEpreuveDefaultSortKeys(array $tabEpreuve) { $strSort1 = fxBibNormalizeSortKey($tabEpreuve['ba_bib_sort_1'] ?? ''); $strSort2 = fxBibNormalizeSortKey($tabEpreuve['ba_bib_sort_2'] ?? ''); if ($strSort1 === '') { $strSort1 = fxBibSortKeyFromBaId((int)($tabEpreuve['ba_id'] ?? 0)); } if ($strSort1 === '') { $strSort1 = 'ba:2'; } return [$strSort1, $strSort2]; } /** MSIN-4436 — Persiste le tri batch sur l'épreuve (+ ba_id si tri fixe). */ function fxBibSaveEpreuveBatchSort($epr_id, $sort1, $sort2 = '') { global $objDatabase; $epr_id = (int)$epr_id; $strSort1 = fxBibNormalizeSortKey($sort1); $strSort2 = fxBibNormalizeSortKey($sort2); if ($epr_id <= 0 || $strSort1 === '') { return false; } if ($strSort1 === $strSort2) { $strSort2 = ''; } $intBaId = 0; if (preg_match('/^ba:(\d+)$/', $strSort1, $m)) { $intBaId = (int)$m[1]; } $sql = " UPDATE inscriptions_epreuves SET ba_bib_sort_1 = '" . $objDatabase->fxEscape($strSort1) . "', ba_bib_sort_2 = '" . $objDatabase->fxEscape($strSort2) . "' " . ($intBaId > 0 ? ", ba_id = $intBaId" : '') . " WHERE epr_id = $epr_id LIMIT 1 "; return (bool)$objDatabase->fxQuery($sql); } /** MSIN-4436 — Lit sort_1 / sort_2 depuis POST (repli ba_id). */ function fxBibParseBatchSortFromPost() { $strSort1 = fxBibNormalizeSortKey($_POST['sort_1'] ?? ''); $strSort2 = fxBibNormalizeSortKey($_POST['sort_2'] ?? ''); if ($strSort1 === '') { $strSort1 = fxBibNormalizeSortKey($_POST['ba_id'] ?? ''); } if ($strSort1 === '') { $strSort1 = 'ba:2'; } if ($strSort1 === $strSort2) { $strSort2 = ''; } return [$strSort1, $strSort2]; } /** MSIN-4436 — Questions éligibles au tri pour une épreuve. */ function fxBibGetQuestionSortOptions($epr_id, $eve_id, $strLangue = 'fr') { global $objDatabase; $epr_id = (int)$epr_id; $eve_id = (int)$eve_id; if ($epr_id <= 0 || $eve_id <= 0) { return []; } $strCol = ($strLangue === 'en') ? 'que_question_en' : 'que_question_fr'; $sql = " SELECT que_id, $strCol AS que_label, que_validation FROM inscriptions_questions WHERE que_actif = 1 AND eve_id = $eve_id AND (FIND_IN_SET($epr_id, que_epreuves_incluses) OR que_epreuves_incluses = '' OR que_epreuves_incluses IS NULL) AND que_validation NOT IN ('image', 'pdf') ORDER BY que_tri, que_id "; $rows = $objDatabase->fxGetResults($sql); if (!is_array($rows)) { return []; } $out = []; foreach ($rows as $row) { $intQueId = (int)($row['que_id'] ?? 0); if ($intQueId <= 0) { continue; } $strLabel = trim((string)($row['que_label'] ?? '')); if ($strLabel === '') { $strLabel = 'Question #' . $intQueId; } if (mb_strlen($strLabel) > 72) { $strLabel = mb_substr($strLabel, 0, 69) . '…'; } $out[] = [ 'key' => 'que:' . $intQueId, 'label' => $strLabel, 'group' => 'question', ]; } return $out; } /** MSIN-4436 — Options de tri (fixes + questions) pour une épreuve. */ function fxBibGetSortOptionsForEpreuve($epr_id, $eve_id, $strLangue = 'fr', $blnTeamEpreuve = false, $blnIncludeQuestions = true) { global $objDatabase; $out = []; $rows = $objDatabase->fxGetResults( "SELECT * FROM bib_assignements WHERE ba_actif = 1 ORDER BY ba_tri" ); if (is_array($rows)) { foreach ($rows as $row) { if ((int)($row['ba_equipe'] ?? 0) === 1 && !$blnTeamEpreuve) { continue; } $intBaId = (int)($row['ba_id'] ?? 0); if ($intBaId <= 0) { continue; } $strLabel = trim((string)($row['ba_nom_' . $strLangue] ?? '')); if ($strLabel === '') { $strLabel = trim((string)($row['ba_nom_fr'] ?? '')); } $out[] = [ 'key' => 'ba:' . $intBaId, 'label' => $strLabel !== '' ? $strLabel : ('ba:' . $intBaId), 'group' => 'fixed', ]; } } if ($blnIncludeQuestions && (int)$epr_id > 0 && (int)$eve_id > 0) { $out = array_merge($out, fxBibGetQuestionSortOptions($epr_id, $eve_id, $strLangue)); } return $out; } /** MSIN-4436 — Vérifie qu'une question est liée à l'épreuve. */ function fxBibQuestionSortAllowedForEpreuve($epr_id, $que_id, $eve_id) { global $objDatabase; $epr_id = (int)$epr_id; $que_id = (int)$que_id; $eve_id = (int)$eve_id; if ($epr_id <= 0 || $que_id <= 0 || $eve_id <= 0) { return false; } $sql = " SELECT COUNT(*) FROM inscriptions_questions WHERE que_id = $que_id AND eve_id = $eve_id AND que_actif = 1 AND que_validation NOT IN ('image', 'pdf') AND (FIND_IN_SET($epr_id, que_epreuves_incluses) OR que_epreuves_incluses = '' OR que_epreuves_incluses IS NULL) "; return (int)$objDatabase->fxGetVar($sql) > 0; } /** MSIN-4436 — Fragment ORDER BY pour une clé de tri. */ function fxBibResolveSortClause($epr_id, $sortKey, $strLangue = 'fr', $eve_id = 0) { global $objDatabase; $sortKey = fxBibNormalizeSortKey($sortKey); if ($sortKey === '') { return ''; } if (preg_match('/^ba:(\d+)$/', $sortKey, $m)) { $intBaId = (int)$m[1]; $orderBy = trim((string)$objDatabase->fxGetVar( "SELECT ba_sql_order FROM bib_assignements WHERE ba_id = $intBaId AND ba_actif = 1 LIMIT 1" )); return $orderBy !== '' ? $orderBy : ''; } if (preg_match('/^que:(\d+)$/', $sortKey, $m)) { $que_id = (int)$m[1]; $epr_id = (int)$epr_id; if ($epr_id <= 0) { return ''; } if ($eve_id <= 0) { $eve_id = (int)$objDatabase->fxGetVar( "SELECT eve_id FROM inscriptions_epreuves WHERE epr_id = $epr_id LIMIT 1" ); } if (!fxBibQuestionSortAllowedForEpreuve($epr_id, $que_id, $eve_id)) { return ''; } $strCol = ($strLangue === 'en') ? 'que_choix_en' : 'que_choix_fr'; return "( SELECT TRIM(rq.$strCol) FROM resultats_questions rq WHERE rq.que_actif = 1 AND rq.que_id = $que_id AND rq.par_id = p.par_id AND rq.pec_id = p.pec_id LIMIT 1 )"; } return ''; } /** MSIN-4436 — ORDER BY complet (2 niveaux + tie-breaker). */ function fxBibBuildBatchOrderBy($epr_id, $sort1, $sort2 = '', $strLangue = 'fr', $eve_id = 0) { $epr_id = (int)$epr_id; if ($eve_id <= 0 && $epr_id > 0) { global $objDatabase; $eve_id = (int)$objDatabase->fxGetVar( "SELECT eve_id FROM inscriptions_epreuves WHERE epr_id = $epr_id LIMIT 1" ); } $tabParts = []; foreach ([$sort1, $sort2] as $mixKey) { $strClause = fxBibResolveSortClause($epr_id, $mixKey, $strLangue, $eve_id); if ($strClause !== '' && !in_array($strClause, $tabParts, true)) { $tabParts[] = $strClause; } } if (empty($tabParts)) { $tabParts[] = 'p.par_nom, p.par_prenom'; } $tabParts[] = 'p.par_id ASC'; return implode(', ', $tabParts); } /** MSIN-4436 — Rendu des '; } $tabFixed = []; $tabQuestions = []; foreach ($tabOptions as $opt) { if (($opt['group'] ?? '') === 'question') { $tabQuestions[] = $opt; } else { $tabFixed[] = $opt; } } if (!empty($tabFixed)) { $html .= ''; foreach ($tabFixed as $opt) { $strKey = fxBibEsc($opt['key'] ?? ''); $html .= ''; } $html .= ''; } if (!empty($tabQuestions)) { $html .= ''; foreach ($tabQuestions as $opt) { $strKey = fxBibEsc($opt['key'] ?? ''); $html .= ''; } $html .= ''; } return $html; } // v2 — options d'ordre batch (table bib_assignements) function renderBatchConfig($epr_id, $eve_id, $strLangue = 'fr', $isTeamEpreuve = false, $strSort1 = '', $strSort2 = '') { $epr_id = (int)$epr_id; $eve_id = (int)$eve_id; $strSort1 = fxBibNormalizeSortKey($strSort1); $strSort2 = fxBibNormalizeSortKey($strSort2); if ($strSort1 === '') { $strSort1 = 'ba:2'; } $tabOptions = fxBibGetSortOptionsForEpreuve($epr_id, $eve_id, $strLangue, $isTeamEpreuve, true); ob_start(); ?>
Commencer à partir de la dernière assignation */ ?>

'inc_tableau_gestion_epreuves', 'epreuves' => 'inc_tableau_epreuves', 'dossards' => 'inc_tableau_dossards', 'promoteur' => 'inc_tableau_promoteur', ); $strFrom = (isset($_GET['from']) && trim($_GET['from']) !== '') ? trim($_GET['from']) : $strDefault; if (!isset($arrAllowed[$strFrom])) { $strFrom = $strDefault; } $strEveParam = 'promoteur_eve_id=' . urlencode(base64_encode($intEveId)); return $vDomaine . $strPage . '/' . $arrAllowed[$strFrom] . '?' . $strEveParam; } /** Chemin relatif (sans domaine) pour strhomeclientannuler et liens internes. */ function fxGetPromoteurBackPath($strPage = '/compte', $strDefault = 'promoteur') { $arrAllowed = array( 'gestion' => 'inc_tableau_gestion_epreuves', 'epreuves' => 'inc_tableau_epreuves', 'dossards' => 'inc_tableau_dossards', 'promoteur' => 'inc_tableau_promoteur', ); $strFrom = (isset($_GET['from']) && trim($_GET['from']) !== '') ? trim($_GET['from']) : $strDefault; if (!isset($arrAllowed[$strFrom])) { $strFrom = $strDefault; } return $strPage . '/' . $arrAllowed[$strFrom]; } /** Jeton CSRF du module dossards v4 (session). */ function fxBibCsrfToken($mem_regenerate = false) { if (empty($_SESSION['bib_csrf_token']) || $mem_regenerate) { $_SESSION['bib_csrf_token'] = bin2hex(random_bytes(32)); } return $_SESSION['bib_csrf_token']; } /** Valide le jeton CSRF reçu en POST (ajax_bib_range save_auto_config, etc.). */ function fxBibValidateCsrf($str_token) { if ($str_token === '' || empty($_SESSION['bib_csrf_token'])) { return false; } return hash_equals($_SESSION['bib_csrf_token'], $str_token); } /** Session promoteur ou admin requise pour les actions AJAX sensibles du module bib. */ function fxBibRequirePromoteurSession() { return !empty($_SESSION['com_id']) || !empty($_SESSION['usa_id']); } /** MSIN-4436 — Au moins une séquence verrouillée sur l'épreuve. */ function fxBibEpreuveHasLockedRanges($epr_id) { global $objDatabase; $epr_id = (int)$epr_id; if ($epr_id <= 0) { return false; } $sql = " SELECT COUNT(*) FROM inscriptions_epreuves_bib WHERE epr_id = $epr_id AND epr_bib_locked = 1 "; return (int)$objDatabase->fxGetVar($sql) > 0; } /** MSIN-4436 — Réinitialisation totale : aucun cadenas sur l'épreuve. */ function fxBibAssertEpreuveNoLockedRanges($epr_id) { if (fxBibEpreuveHasLockedRanges($epr_id)) { return ['success' => false, 'message' => fxBibMsg('bib_v4_js_reset_all_locked')]; } return ['success' => true]; } /** MSIN-4424 — Mêmes numéros de dossard autorisés entre épreuves (réglage événement). */ function fxBibEventAllowsInterEprDuplicates($int_eve_id) { global $objDatabase; static $memCache = []; $int_eve_id = (int)$int_eve_id; if ($int_eve_id <= 0) { return false; } if (array_key_exists($int_eve_id, $memCache)) { return $memCache[$int_eve_id]; } $intVal = (int)$objDatabase->fxGetVar( 'SELECT eve_bib_allow_inter_epr FROM inscriptions_evenements WHERE eve_id = ' . $int_eve_id . ' LIMIT 1' ); $memCache[$int_eve_id] = ($intVal === 1); return $memCache[$int_eve_id]; } /** MSIN-4424 — Persiste le réglage doublons inter-épreuves. */ function fxBibSetEventAllowsInterEprDuplicates($int_eve_id, $blnAllow) { global $objDatabase; $int_eve_id = (int)$int_eve_id; $intAllow = $blnAllow ? 1 : 0; if ($int_eve_id <= 0) { return ['success' => false, 'message' => fxBibMsg('bib_v4_ajax_epr_invalid')]; } $objDatabase->fxQuery( 'UPDATE inscriptions_evenements SET eve_bib_allow_inter_epr = ' . $intAllow . ' WHERE eve_id = ' . $int_eve_id . ' LIMIT 1' ); return ['success' => true, 'allow_inter_epr' => ($intAllow === 1)]; } /** Resout eve_id depuis la requete AJAX bib (eve_id, epr_id ou epr_bib_id / id). */ function fxBibResolveEveIdFromRequest() { global $objDatabase; if (!empty($_REQUEST['eve_id'])) { return intval($_REQUEST['eve_id']); } $intEprId = intval($_REQUEST['epr_id'] ?? 0); if ($intEprId > 0) { $intEveId = intval($objDatabase->fxGetVar( 'SELECT eve_id FROM inscriptions_epreuves WHERE epr_id = ' . $intEprId . ' LIMIT 1' )); return ($intEveId > 0) ? $intEveId : 0; } // delete n'envoyait que id (epr_bib_id) — seule action sans epr_id ni eve_id $intBibId = intval($_REQUEST['epr_bib_id'] ?? $_REQUEST['id'] ?? 0); if ($intBibId <= 0) { return 0; } $intEveId = intval($objDatabase->fxGetVar( 'SELECT e.eve_id FROM inscriptions_epreuves_bib b ' . 'INNER JOIN inscriptions_epreuves e ON e.epr_id = b.epr_id ' . 'WHERE b.epr_bib_id = ' . $intBibId . ' LIMIT 1' )); return ($intEveId > 0) ? $intEveId : 0; } /** Gate AJAX bib v4 — session promoteur + CSRF (pas de granularite v2 pour l'instant). */ function fxBibRequireAjaxAccess() { if (!fxBibRequirePromoteurSession()) { echo json_encode([ 'success' => false, 'message' => fxBibMsg('bib_v4_ajax_session_invalid'), ]); exit; } $strToken = $_POST['csrf_token'] ?? $_REQUEST['csrf_token'] ?? ''; if (!fxBibValidateCsrf($strToken)) { echo json_encode([ 'success' => false, 'message' => fxBibMsg('bib_v4_ajax_unauthorized'), ]); exit; } } /** Échappement HTML pour attributs Tippy / boutons bib v4. */ function fxBibEsc($str) { return htmlspecialchars($str, ENT_QUOTES, 'UTF-8'); } /** Page info pour le module bib v4 — toujours compte.php, jamais le script AJAX. */ function fxBibInfoPage() { return 'compte.php'; } /** * Lit info_texte / info_aide depuis $vtexte_page ou la BD (compte.php / global uniquement). * N'écrit jamais dans info — évite les doublons info_prg=ajax_bib_range.php en AJAX. */ function fxBibGetInfoRow($clef) { global $vtexte_page, $objDatabase, $strLangue; if (!empty($vtexte_page[$clef])) { return $vtexte_page[$clef]; } static $bibInfoCache = array(); if (array_key_exists($clef, $bibInfoCache)) { return $bibInfoCache[$clef]; } if (!isset($objDatabase)) { $bibInfoCache[$clef] = array(); return $bibInfoCache[$clef]; } $strLang = !empty($strLangue) ? $strLangue : 'fr'; $strPage = fxBibInfoPage(); $sql = "SELECT info_texte, info_aide FROM info WHERE info_actif = 1 AND info_clef = '" . $objDatabase->fxEscape($clef) . "' AND info_langue = '" . $objDatabase->fxEscape($strLang) . "' AND info_prg IN ('" . $strPage . "', 'global') ORDER BY FIELD(info_prg, '" . $strPage . "', 'global') LIMIT 1"; $row = $objDatabase->fxGetRow($sql); if (!is_array($row) || empty($row)) { // Entrées legacy créées par erreur sous ajax_bib_range.php — lecture seule. $sqlLegacy = "SELECT info_texte, info_aide FROM info WHERE info_actif = 1 AND info_clef = '" . $objDatabase->fxEscape($clef) . "' AND info_langue = '" . $objDatabase->fxEscape($strLang) . "' AND info_prg = 'ajax_bib_range.php' LIMIT 1"; $row = $objDatabase->fxGetRow($sqlLegacy); } $bibInfoCache[$clef] = is_array($row) ? $row : array(); return $bibInfoCache[$clef]; } /** Libellé bib v4 — sur compte.php auto-crée si absent ; en AJAX, lecture seule. * $blnAllowEmptyLabel : si true, info_texte vide en BD reste vide (en-têtes colonnes éditables). */ function fxBibTexte($clef, $mem_echo = 0, $blnAllowEmptyLabel = false) { $row = fxBibGetInfoRow($clef); $blnHasDbRow = !empty($row) && array_key_exists('info_texte', $row); $str = trim($row['info_texte'] ?? ''); if ($blnAllowEmptyLabel && $blnHasDbRow && $str === '') { if ($mem_echo) { echo ''; } return ''; } if ($str === '' || $str === $clef || $str === '*' . $clef . '*') { if (!defined('MS1_BIB_AJAX') || !MS1_BIB_AJAX) { if (!$blnHasDbRow && !$blnAllowEmptyLabel) { $str = trim(afficheTexte($clef, 0)); } } if ($str === '' || $str === $clef || $str === '*' . $clef . '*') { $str = fxBibStaticFallback($clef); if ($str === '') { $str = $blnAllowEmptyLabel ? '' : $clef; } } } if ($mem_echo) { echo fxBibEsc($str); } return $str; } /** Repli FR/EN si clé bib_v4 absente ou mal saisie en BD. */ function fxBibStaticFallback($clef) { global $strLangue; $lng = ($strLangue === 'en') ? 'en' : 'fr'; $tab = [ 'bib_v4_view_cancelled' => ['fr' => 'Annulé', 'en' => 'Cancelled'], 'bib_v4_err_gaps' => ['fr' => 'Dossards non assignés :', 'en' => 'Unassigned bibs:'], 'bib_v4_err_dupes' => ['fr' => 'Dossards en double :', 'en' => 'Duplicate bibs:'], 'bib_v4_anomalies_auto_miss_title' => ['fr' => 'Inscriptions sans dossard (auto)', 'en' => 'Registrations without a bib (auto)'], 'bib_v4_anomalies_out_of_range_title' => ['fr' => 'Dossards hors séquence', 'en' => 'Bibs outside sequences'], 'bib_v4_anomalies_no_range' => ['fr' => 'Aucune séquence configurée', 'en' => 'No sequence configured'], 'bib_v4_anomalies_order_fmt' => ['fr' => 'Commande %s', 'en' => 'Order %s'], 'bib_v4_anomalies_assign_link' => ['fr' => 'Assigner un dossard', 'en' => 'Assign a bib'], 'bib_v4_anomalies_orphan_annule' => ['fr' => 'Annulée — dossard encore en séquence', 'en' => 'Cancelled — bib still in sequence'], 'bib_v4_anomalies_orphan_transfert'=> ['fr' => 'Transférée — dossard conservé', 'en' => 'Transferred — bib kept'], 'bib_v4_anomalies_view_inscription'=> ['fr' => "Voir l'inscription", 'en' => 'View registration'], 'bib_v4_js_loader_wait' => ['fr' => 'Chargement...', 'en' => 'Loading...'], 'bib_v4_js_global_no_epr' => ['fr' => 'Sélectionnez au moins une épreuve solo.', 'en' => 'Select at least one solo race.'], 'bib_v4_js_global_no_seq' => ['fr' => 'Cochez au moins une séquence pour les épreuves sélectionnées.', 'en' => 'Check at least one sequence for the selected races.'], 'bib_v4_global_batch_plan_invalid' => ['fr' => 'Sélection invalide : choisissez au moins une épreuve solo et des séquences.', 'en' => 'Invalid selection: pick at least one solo race and sequences.'], 'bib_v4_global_batch_need_two' => ['fr' => "L'assignation globale nécessite au moins deux épreuves solo avec des séquences disponibles.", 'en' => 'Global assignment requires at least two solo races with available sequences.'], 'bib_v4_batch_order_2' => ['fr' => 'Tri secondaire', 'en' => 'Secondary sort'], 'bib_v4_batch_order_none' => ['fr' => '— Aucun —', 'en' => '— None —'], 'bib_v4_batch_sort_group_fixed' => ['fr' => 'Critères fixes', 'en' => 'Fixed criteria'], 'bib_v4_summary_teams_unit' => ['fr' => 'équipes', 'en' => 'teams'], 'bib_v4_event_config_title' => ['fr' => "Configuration globale pour l'assignation de dossard", 'en' => 'Global bib assignment settings'], 'bib_v4_event_config_inter_epr_on' => ['fr' => 'Inter épreuve', 'en' => 'Inter-race'], 'bib_v4_anomalies_nav_todo' => ['fr' => 'Élément à valider', 'en' => 'Item to review'], 'bib_v4_anomalies_nav_todo_fmt' => ['fr' => 'Élément à valider : %d', 'en' => 'Item to review: %d'], 'bib_v4_qty_summary_title' => ['fr' => 'Sommaire des quantités', 'en' => 'Quantity summary'], 'bib_v4_qty_summary_a_assigner' => ['fr' => 'À assigner', 'en' => 'To assign'], 'bib_v4_qty_summary_bib_restant' => ['fr' => 'Dossards restants', 'en' => 'Bibs remaining'], 'bib_v4_qty_summary_orphelins' => ['fr' => 'Personnes orphelines', 'en' => 'Orphan participants'], 'bib_v4_qty_summary_total' => ['fr' => 'Inscrits', 'en' => 'Registered'], 'bib_v4_qty_summary_avec_bib' => ['fr' => 'Avec dossard', 'en' => 'With bib'], 'bib_v4_qty_summary_sans_bib' => ['fr' => 'Sans dossard', 'en' => 'Without bib'], 'bib_v4_qty_summary_places_dispo' => ['fr' => 'Places disponibles', 'en' => 'Places available'], 'bib_v4_qty_summary_global' => ['fr' => 'Total événement', 'en' => 'Event total'], 'bib_v4_qty_summary_by_epr' => ['fr' => 'Détail par épreuve', 'en' => 'Breakdown by race'], 'bib_v4_qty_summary_col_epr' => ['fr' => 'Épreuve', 'en' => 'Race'], 'bib_v4_ajax_global_sim_summary' => ['fr' => '%d épreuve(s) · %d dispo / %d à assigner → %d assignables (%d manque)', 'en' => '%d race(s) · %d avail. / %d to assign → %d assignable (%d short)'], 'bib_v4_dist_print_title' => ['fr' => 'Impression PDF — distribution des dossards', 'en' => 'PDF print — bib distribution'], 'bib_v4_dist_print_questions' => ['fr' => 'Colonnes optionnelles (questions)', 'en' => 'Optional columns (questions)'], 'bib_v4_dist_print_generate' => ['fr' => 'Générer le PDF', 'en' => 'Generate PDF'], 'bib_v4_dist_print_last_none' => ['fr' => 'Aucune impression enregistrée pour cet événement.', 'en' => 'No print recorded for this event yet.'], 'bib_v4_dist_print_max_questions' => ['fr' => 'Maximum %d questions par épreuve pour le format lettre.', 'en' => 'Maximum %d questions per race for letter format.'], 'bib_v4_dist_print_no_questions' => ['fr' => 'Aucune question éligible pour cette épreuve.', 'en' => 'No eligible question for this race.'], 'bib_v4_dist_print_col_bib' => ['fr' => 'Dossard', 'en' => 'Bib'], 'bib_v4_dist_print_col_nom' => ['fr' => 'Nom', 'en' => 'Last name'], 'bib_v4_dist_print_col_prenom' => ['fr' => 'Prénom', 'en' => 'First name'], 'bib_v4_dist_print_col_sexe' => ['fr' => 'Sexe', 'en' => 'Gender'], 'bib_v4_dist_print_empty_epr' => ['fr' => 'Aucun dossard assigné pour cette épreuve.', 'en' => 'No bib assigned for this race.'], 'bib_v4_dist_print_last' => ['fr' => 'Dernière impression : %s — %s', 'en' => 'Last print: %s — %s'], 'bib_v4_dist_print_footer' => ['fr' => 'Imprimé le %s — %s', 'en' => 'Printed on %s — %s'], ]; return $tab[$clef][$lng] ?? ''; } /** Libellé « Annulé » dans la vue dossards — repli si clé BD absente ou mal saisie. */ function fxBibViewCancelledLabel($strLangue = 'fr') { $str = fxBibTexte('bib_v4_view_cancelled', 0); if ($str === '' || $str === 'bib_v4_view_cancelled' || $str === '*bib_v4_view_cancelled*') { return ($strLangue === 'en') ? 'Cancelled' : 'Annulé'; } return $str; } /** Message bib v4 avec placeholders sprintf (%d, %s…) — AJAX et vues dynamiques. */ function fxBibMsg($clef, ...$args) { $str = fxBibTexte($clef, 0); if (!empty($args)) { return vsprintf($str, $args); } return $str; } /** Crayon traduction bib v4 (page info bib). */ function fxBibTradButton($clef) { return fxAdminTradButton($clef, fxBibInfoPage()); } /** Texte bib + crayon si mode Textes actif. */ function fxBibTexteTrad($clef, $mem_echo = 0, $blnAllowEmptyLabel = false) { $html = fxBibEsc(fxBibTexte($clef, 0, $blnAllowEmptyLabel)) . fxBibTradButton($clef); if ($mem_echo) { echo $html; } return $html; } /** Message sprintf + crayon. */ function fxBibMsgTrad($clef, ...$args) { return fxBibEsc(fxBibMsg($clef, ...$args)) . fxBibTradButton($clef); } /** Clé site promoteur_* puis repli bib_v4_* + crayon sur la clé bib. */ function fxBibSiteOrBibTexteTrad($strSiteClef, $strBibClef, $mem_echo = 0) { $html = fxBibEsc(fxBibSiteOrBibTexte($strSiteClef, $strBibClef)) . fxBibTradButton($strBibClef); if ($mem_echo) { echo $html; } return $html; } /** Description d'une épreuve en conflit : type + catégorie (langue courante), (inactive) si epr_actif = 0. */ function fxBibEpreuveDisplayName(array $row, $strLangue = 'fr') { $strLangue = in_array($strLangue, ['fr', 'en'], true) ? $strLangue : 'fr'; $strAlt = $strLangue === 'fr' ? 'en' : 'fr'; $strType = trim($row['epr_type_' . $strLangue] ?? ''); if ($strType === '') { $strType = trim($row['epr_type_' . $strAlt] ?? ''); } $strCat = trim($row['epr_categorie_' . $strLangue] ?? ''); if ($strCat === '') { $strCat = trim($row['epr_categorie_' . $strAlt] ?? ''); } $parts = []; if ($strType !== '') { $parts[] = $strType; } if ($strCat !== '') { $parts[] = $strCat; } $str = implode(' ', $parts); if ($str === '') { $strNom = trim($row['epr_nom_' . $strLangue] ?? ''); if ($strNom === '') { $strNom = trim($row['epr_nom_' . $strAlt] ?? ''); } $str = $strNom; } if ($str === '' && !empty($row['epr_id'])) { $str = '#' . (int)$row['epr_id']; } if ((int)($row['epr_actif'] ?? 1) === 0) { $strInactive = trim(fxBibTexte('bib_v4_ajax_overlap_inactive', 0)); if ($strInactive !== '' && $strInactive !== 'bib_v4_ajax_overlap_inactive') { $str .= ' (' . $strInactive . ')'; } } return $str; } /** * MSIN-4424 — Séquences qui chevauchent [start, end]. * Par défaut : tout l'événement. Si doublons inter-épreuves autorisés : même epr_id seulement. * * @return array|null Tableau 1-based fxGetResults, ou null si paramètres invalides. */ function fxBibGetOverlappingRanges($eve_id, $start, $end, $exclude_bib_id = 0, $epr_id = 0) { global $objDatabase; $eve_id = (int)$eve_id; $start = (int)$start; $end = (int)$end; $exclude_bib_id = (int)$exclude_bib_id; $epr_id = (int)$epr_id; if ($eve_id <= 0 || $start <= 0 || $end <= 0) { return null; } $sqlExclude = ($exclude_bib_id > 0) ? "AND b.epr_bib_id <> $exclude_bib_id" : ''; $sqlEprOnly = ''; if ($epr_id > 0 && fxBibEventAllowsInterEprDuplicates($eve_id)) { $sqlEprOnly = "AND b.epr_id = $epr_id"; } $sql = " SELECT b.epr_bib_id, b.epr_id, b.epr_bib_start, b.epr_bib_finish, e.epr_nom_fr, e.epr_nom_en, e.epr_type_fr, e.epr_type_en, e.epr_categorie_fr, e.epr_categorie_en, e.epr_actif FROM inscriptions_epreuves_bib b INNER JOIN inscriptions_epreuves e ON e.epr_id = b.epr_id WHERE b.eve_id = $eve_id AND b.epr_bib_start IS NOT NULL AND b.epr_bib_finish IS NOT NULL $sqlExclude $sqlEprOnly AND ($start <= b.epr_bib_finish AND $end >= b.epr_bib_start) ORDER BY b.epr_bib_start, b.epr_bib_id "; return $objDatabase->fxGetResults($sql); } /** Message d'erreur détaillé pour un chevauchement de séquences de dossards. */ function fxBibFormatOverlapConflictMessage($tabOverlaps, $strLangue = 'fr') { if (empty($tabOverlaps) || !is_array($tabOverlaps)) { return fxBibTexte('bib_v4_ajax_overlap', 0); } $entries = []; $firstRow = null; foreach ($tabOverlaps as $row) { if (!is_array($row)) { continue; } if ($firstRow === null) { $firstRow = $row; } $entries[] = fxBibMsg( 'bib_v4_ajax_overlap_entry', fxBibEpreuveDisplayName($row, $strLangue), (int)$row['epr_bib_start'], (int)$row['epr_bib_finish'] ); } if (empty($entries) || $firstRow === null) { return fxBibTexte('bib_v4_ajax_overlap', 0); } if (count($entries) === 1) { return fxBibMsg( 'bib_v4_ajax_overlap', (int)$firstRow['epr_bib_start'], (int)$firstRow['epr_bib_finish'], fxBibEpreuveDisplayName($firstRow, $strLangue) ); } return fxBibMsg('bib_v4_ajax_overlap_many', implode('; ', $entries)); } /** MSIN-4379 — Attributs data-js-* injectés dans #module-bib pour le JS client. */ function fxBibModuleJsDataAttrs() { $map = [ 'loader-wait' => 'bib_v4_js_loader_wait', 'server-error' => 'bib_v4_js_server_error', 'fields-required' => 'bib_v4_js_fields_required', 'auto-select-count' => 'bib_v4_js_auto_select_count', 'batch-none' => 'bib_v4_js_batch_none_selected', 'batch-summary-ok' => 'bib_v4_js_batch_summary_ok', 'reset-warning' => 'bib_v4_js_reset_warning', 'auto-disable-confirm'=> 'bib_v4_js_auto_disable_confirm', 'no-seq-selected' => 'bib_v4_ajax_no_seq_selected', 'err-generic' => 'bib_v4_ajax_error', 'batch-summary-short' => 'bib_v4_ajax_sim_summary', 'batch-reset-count' => 'bib_v4_ajax_reset_preview_count', 'auto-select-hint' => 'bib_v4_auto_select_hint', 'lock-confirm' => 'bib_v4_js_lock_confirm', 'unlock-confirm' => 'bib_v4_js_unlock_confirm', 'auto-pending-bibs' => 'bib_v4_js_auto_pending_bibs', 'range-incomplete' => 'bib_v4_js_range_incomplete', 'range-invalid' => 'bib_v4_js_range_invalid', 'range-invalid-end' => 'bib_v4_js_range_invalid_end', 'range-invalid-qty' => 'bib_v4_js_range_invalid_qty', 'batch-progress' => 'bib_v4_js_batch_progress', 'global-no-epr' => 'bib_v4_js_global_no_epr', 'global-no-seq' => 'bib_v4_js_global_no_seq', 'reset-all-confirm' => 'bib_v4_js_reset_all_confirm', 'reset-all-locked' => 'bib_v4_js_reset_all_locked', 'assign-full' => 'bib_v4_ajax_assign_full', 'assign-partial' => 'bib_v4_ajax_assign_partial', 'assign-none' => 'bib_v4_ajax_assign_none', 'anomalies-nav-todo' => 'bib_v4_anomalies_nav_todo', 'anomalies-nav-todo-fmt' => 'bib_v4_anomalies_nav_todo_fmt', ]; $html = ''; foreach ($map as $attr => $clef) { $html .= ' data-js-' . $attr . '="' . fxBibEsc(fxBibTexte($clef, 0)) . '"'; } return $html; } /** Texte d'aide long bib v4 — même règle que fxBibTexte pour l'auto-création. */ function fxBibAide($clef, $mem_echo = 0) { $row = fxBibGetInfoRow($clef); $str = trim($row['info_aide'] ?? ''); if ($str === '' && (!defined('MS1_BIB_AJAX') || !MS1_BIB_AJAX)) { $str = trim(afficheAide($clef, 0)); } if ($mem_echo) { echo fxBibEsc($str); } return $str; } /** Attribut data-tippy-content — info_aide (tips sur icônes œil, colonnes sans libellé, etc.). */ function fxBibAideTippyAttr($clef) { $strTip = trim(fxBibAide($clef, 0)); if ($strTip === '') { return ''; } return ' data-tippy-content="' . fxBibEsc($strTip) . '"'; } /** Attribut data-tippy-content — texte court depuis info_texte (clé bib_v4_tip_*). */ function fxBibTippyAttr($clef) { $strTip = trim(fxBibTexte($clef, 0)); if ($strTip === '' || $strTip === '*' . $clef . '*') { return ''; } return ' data-tippy-content="' . fxBibEsc($strTip) . '"'; } /** Classe CSS de visibilité — toujours appliquée au conteneur entier (bouton + ?). */ function fxBibUiHiddenClass($blnVisible) { return $blnVisible ? '' : ' bib-ui-hidden'; } /** Contenu aide (?) — texte caché ; espace visuel avant le rond via CSS (.epr-action-group gap). */ function fxBibAideInner($clef) { $strAide = trim(fxBibAide($clef, 0)); if ($strAide === '') { return ''; } return '?'; } /** Bouton (?) rond + crayon traduction (via fxAdminTradButton) — $strTone optionnel. */ function fxBibAideButton($clef, $strTone = '', $blnWithTrad = true) { $strHtml = ''; $strInner = fxBibAideInner($clef); if ($strInner !== '') { $strClass = 'btn-aide-bib bib-aide-trigger'; if ($strTone !== '') { $strClass .= ' btn-aide-tone-' . preg_replace('/[^a-z0-9-]/', '', $strTone); } $strHtml = ''; } if ($blnWithTrad) { $strHtml .= fxAdminTradButton($clef, fxBibInfoPage()); } return $strHtml; } /** Clé site promoteur_* puis repli bib_v4_* si absent en BD. */ function fxBibSiteOrBibTexte($strSiteClef, $strBibClef) { $str = trim(afficheTexte($strSiteClef, 0)); if ($str === '' || $str === '*' . $strSiteClef . '*') { $str = fxBibTexte($strBibClef, 0); } return $str; } /** Libellé info_texte + bouton (?) si info_aide renseigné. */ function fxBibTexteAide($clef, $mem_echo = 1) { $str = fxBibTexte($clef, 0); $html = fxBibEsc($str) . fxBibAideButton($clef); if ($mem_echo) { echo $html; } return $html; } /** Titre d'en-tête de bloc (assignation, gestion…) + aide + doc overlay optionnel. */ function fxBibBlockHeader($clef, $docAnchorClef = '') { $html = '' . fxBibEsc(fxBibTexte($clef, 0)) . fxBibAideButton($clef); if ($docAnchorClef !== '') { $html .= fxDocRenderTrigger($docAnchorClef); $html .= fxAdminDocButton($docAnchorClef); } return $html . ''; } /** Pastille résumé dans la bande bleue (quantité dispo, séquences, etc.). */ function fxBibHeaderSummaryBadge($strText, $strVariant = '', $strTradClef = '') { $strClass = 'epr-header-summary'; if ($strVariant !== '') { $strClass .= ' epr-header-summary--' . preg_replace('/[^a-z0-9-]/', '', $strVariant); } $strInner = fxBibEsc($strText); if ($strTradClef !== '') { $strInner .= fxBibTradButton($strTradClef); } return '' . $strInner . ''; } /** Résumé gestion : quantité disponible. */ function fxBibGestionHeaderSummary($epr_id) { $arrQuantites = fxGetQuantites((int)$epr_id); $intDispo = (int)$arrQuantites['qte_restante']; $strLabel = fxBibSiteOrBibTexte('promoteur_qte_epr_qte', 'bib_v4_summary_dispo'); return fxBibHeaderSummaryBadge($strLabel . ' : ' . $intDispo, 'dispo', 'bib_v4_summary_dispo'); } /** Résumé assignation : nombre de séquences (+ auto si actif). */ function fxBibAssignHeaderSummary($intSeqCount, $blnAutoActive = false) { $html = ''; $intSeqCount = (int)$intSeqCount; if ($intSeqCount > 0) { if ($intSeqCount === 1) { $html .= fxBibHeaderSummaryBadge( fxBibTexte('bib_v4_summary_seq_one', 0), 'seq', 'bib_v4_summary_seq_one' ); } else { $html .= fxBibHeaderSummaryBadge( fxBibMsg('bib_v4_summary_seq_many', $intSeqCount), 'seq', 'bib_v4_summary_seq_many' ); } } else { $html .= fxBibHeaderSummaryBadge( fxBibTexte('bib_v4_summary_seq_empty', 0), 'seq-empty', 'bib_v4_summary_seq_empty' ); } if ($blnAutoActive) { $html .= fxBibHeaderSummaryBadge( fxBibTexte('bib_v4_auto_badge', 0), 'auto', 'bib_v4_auto_badge' ); } return $html; } /** Dossards encore disponibles dans une séquence (stats fxInfosBibRange). */ function fxBibRangeDispoFromRow(array $range) { $nb_total = (int)($range['nb_total_range'] ?? 0); $nb_utilises = (int)($range['nb_utilises'] ?? 0); return max(0, $nb_total - $nb_utilises); } /** Séquence utilisable pour l'assignation auto (au moins un dossard libre). */ function fxBibIsRangeAvailableForAuto($epr_id, $epr_bib_id) { $rows = fxInfosBibRange((int)$epr_id, (int)$epr_bib_id); if (empty($rows) || !is_array($rows[1] ?? null)) { return false; } if (fxBibRangeIsLocked($rows[1])) { return false; } return fxBibRangeDispoFromRow($rows[1]) > 0; } /** Séquence verrouillée (epr_bib_locked = 1) — plus de modification ni assignation. */ function fxBibRangeIsLocked(array $range) { return (int)($range['epr_bib_locked'] ?? 0) === 1; } /** Vérifie qu'aucune des séquences listées n'est verrouillée. */ function fxBibAssertRangesUnlocked($epr_id, $rangeIds) { global $objDatabase; $epr_id = (int)$epr_id; if ($epr_id <= 0 || empty($rangeIds) || !is_array($rangeIds)) { return ['success' => true]; } $ids = array_values(array_filter(array_map('intval', $rangeIds))); if (empty($ids)) { return ['success' => true]; } $strIds = implode(',', $ids); $sql = " SELECT COUNT(*) FROM inscriptions_epreuves_bib WHERE epr_id = $epr_id AND epr_bib_id IN ($strIds) AND epr_bib_locked = 1 "; if ((int)$objDatabase->fxGetVar($sql) > 0) { return ['success' => false, 'message' => fxBibMsg('bib_v4_ajax_range_locked')]; } return ['success' => true]; } /** Bascule le verrou d'une séquence (0 = ouvert, 1 = fermé). */ function fxBibSetRangeLocked($epr_bib_id, $locked, $epr_id = 0) { global $objDatabase; $epr_bib_id = (int)$epr_bib_id; $epr_id = (int)$epr_id; $locked = $locked ? 1 : 0; if ($epr_bib_id <= 0) { return ['success' => false, 'message' => fxBibMsg('bib_v4_ajax_id_invalid')]; } $sqlGet = " SELECT epr_id FROM inscriptions_epreuves_bib WHERE epr_bib_id = $epr_bib_id "; $rowEprId = (int)$objDatabase->fxGetVar($sqlGet); if ($rowEprId <= 0) { return ['success' => false, 'message' => fxBibMsg('bib_v4_ajax_range_not_found')]; } if ($epr_id > 0 && $rowEprId !== $epr_id) { return ['success' => false, 'message' => fxBibMsg('bib_v4_ajax_seq_invalid')]; } $objDatabase->fxQuery(" UPDATE inscriptions_epreuves_bib SET epr_bib_locked = $locked WHERE epr_bib_id = $epr_bib_id "); if ($locked === 1) { $objDatabase->fxQuery(" UPDATE inscriptions_epreuves_bib SET epr_bib_auto = 0 WHERE epr_bib_id = $epr_bib_id "); } return [ 'success' => true, 'epr_id' => $rowEprId, 'locked' => $locked === 1, ]; } /** Bouton cadenas (vert ouvert / rouge fermé) devant une séquence enregistrée. */ function fxBibRenderRangeLockButton(array $range, $epr_id = 0) { if ((int)($range['epr_bib_id'] ?? 0) <= 0) { return '
'; } $locked = fxBibRangeIsLocked($range); $strClass = $locked ? 'btn-bib-lock--closed' : 'btn-bib-lock--open'; $strIcon = $locked ? 'fa-lock' : 'fa-unlock'; $strTip = $locked ? 'bib_v4_tip_lock_closed' : 'bib_v4_tip_lock_open'; return '
'; } /** Badge « Assignation automatique » sur une ligne de séquence (epr_bib_auto = 1). */ function fxBibRenderRangeAutoBadge(array $range, $mode = 'assign') { if ($mode !== 'assign') { return ''; } if ((int)($range['epr_bib_id'] ?? 0) <= 0) { return ''; } if ((int)($range['epr_bib_auto'] ?? 0) !== 1) { return ''; } if (fxBibRangeIsLocked($range)) { return ''; } if (fxBibRangeDispoFromRow($range) <= 0) { return ''; } return '' . fxBibEsc(fxBibTexte('bib_v4_auto', 0)) . fxBibTradButton('bib_v4_auto') . ''; } /** MSIN-4436 — Nombre d'équipes actives sur une épreuve équipe. */ function fxBibCountEquipes($epr_id) { global $objDatabase; $epr_id = (int)$epr_id; if ($epr_id <= 0) { return 0; } return (int)$objDatabase->fxGetVar(" SELECT COUNT(DISTINCT p.pec_id) FROM resultats_participants p WHERE p.epr_id = $epr_id AND p.is_cancelled = 0 AND p.pec_id > 0 "); } /** Pastilles épreuve (solo/équipe, inscrits, avec dossard) — bande bleue colonne gauche. */ function fxBibEpreuveHeaderSummaries($tabEpreuve, $infoBib) { $html = ''; if ((int)$tabEpreuve['epr_equipe'] === 1) { $intNbEquipes = (int)($infoBib['nb_equipes'] ?? 0); $html .= fxBibHeaderSummaryBadge( fxBibSiteOrBibTexte('promoteur_epr_equipe', 'bib_v4_summary_team'), 'team', 'bib_v4_summary_team' ); $html .= '' . '' . $intNbEquipes . ' ' . fxBibTexteTrad('bib_v4_summary_teams_unit', 0) . ''; } else { $html .= fxBibHeaderSummaryBadge( fxBibSiteOrBibTexte('promoteur_epr_solo', 'bib_v4_summary_solo'), 'solo', 'bib_v4_summary_solo' ); } $html .= '' . '' . (int)$infoBib['total'] . ' ' . fxBibSiteOrBibTexteTrad('promoteur_qte_epr_qte_ordered', 'bib_v4_summary_registered') . ''; $html .= '' . '' . (int)$infoBib['avec_bib'] . ' ' . fxBibSiteOrBibTexteTrad('promoteur_qte_epr_qte_orderedavec_bib', 'bib_v4_summary_with_bib') . ''; return $html; } /** En-tête bleu colonne gauche : titre de l'épreuve + pastilles (aligné visuellement avec la droite). */ function renderBibEpreuveIdentityHeader($tabEpreuve, $infoBib, $strLangue = 'fr') { $strCat = trim($tabEpreuve['epr_categorie_' . $strLangue] ?? ''); $strType = trim($tabEpreuve['epr_type_' . $strLangue] ?? ''); $strNom = trim($tabEpreuve['epr_nom_' . $strLangue] ?? ''); $intEprId = (int)($tabEpreuve['epr_id'] ?? 0); ob_start(); ?>
' . ''; } return ''; } /** Cellule d'en-tête de colonne séquence + aide optionnelle (info_texte peut être vide). */ function fxBibColHeader($clef) { $strLabel = fxBibTexte($clef, 0, true); $strClass = 'epr-col-header-label'; if ($strLabel === '') { $strClass .= ' epr-col-header-label--empty'; } return '' . fxBibEsc($strLabel) . fxBibAideButton($clef) . fxBibTradButton($clef) . ''; } /** Groupe action (bouton + ?) — $strClass ex. « epr-action-batch-group ». */ function fxBibActionGroupOpen($strClass, $blnVisible = true) { return ''; } function fxBibActionGroupClose() { return ''; } /** Ouvre le segment bouton + ? collés (option A — rangée d'actions). */ function fxBibBtnAideSegmentOpen($strExtraClass = '') { $strClass = 'epr-btn-aide-segment'; if ($strExtraClass !== '') { $strClass .= ' ' . preg_replace('/[^a-z0-9 _-]/', '', $strExtraClass); } return ''; } function fxBibBtnAideSegmentClose($clef = '') { $html = ''; if ($clef !== '') { $html .= fxBibTradButton($clef); } return $html . ''; } /** * Bloc gestion épreuve (quantités lecture seule + liste d'attente). * Pas de bouton OK — les quantités ne sont plus modifiables depuis cet écran. */ function renderBibGestionEpreuve($epr_id, $int_eve_id, $strLangue) { $epr_id = (int)$epr_id; $int_eve_id = (int)$int_eve_id; $arrQuantites = fxGetQuantites($epr_id); $QCheckin = fxGetCheckinqte($epr_id); $nbSurListeA = fx_liste_attente_nombre_surliste($epr_id, 1); $nbSurListe = fx_liste_attente_nombre_surliste($epr_id); $intNbSurListeA = ($nbSurListeA !== false && $nbSurListeA > 0) ? (int)$nbSurListeA : 0; $intNbSurListe = ($nbSurListe !== false && $nbSurListe > 0) ? (int)$nbSurListe : 0; ob_start(); ?>
0 ? $intNbSurListeA : '—'; ?>
0 ? $intNbSurListe : '—'; ?>
0 && $intEnd >= $intStart) { $intQty = $intEnd - $intStart + 1; } return [ 'qty' => $intQty, 'start' => $intStart, 'end' => $intEnd, 'has_values' => ($intId > 0 && $intStart > 0 && $intEnd >= $intStart), ]; } /** MSIN-4429 — Colonne « ou » entre Qté et Fin. */ function fxBibRenderRangeOrCell() { ob_start(); ?>
0; ob_start(); ?>
$intBibId, 'start' => (int)$row['epr_bib_start'], 'end' => (int)$row['epr_bib_finish'], 'dispo' => $intDispo, ]; } return $out; } /** MSIN-4436 — Vérifie épreuve solo de l'événement (batch global). */ function fxBibAssertSoloEpreuveForGlobalBatch($epr_id, $int_eve_id) { global $objDatabase; $epr_id = (int)$epr_id; $int_eve_id = (int)$int_eve_id; $row = $objDatabase->fxGetRow(" SELECT epr_id, eve_id, epr_equipe FROM inscriptions_epreuves WHERE epr_id = $epr_id LIMIT 1 "); if (!$row || (int)$row['eve_id'] !== $int_eve_id) { return ['success' => false, 'message' => fxBibMsg('bib_v4_ajax_epr_invalid')]; } if ((int)$row['epr_equipe'] === 1) { return ['success' => false, 'message' => fxBibMsg('bib_v4_global_batch_team_excluded')]; } return ['success' => true]; } /** MSIN-4436 — Plans batch global validés [{epr_id, ranges[]}]. */ function fxBibParseGlobalBatchPlans($plans, $int_eve_id) { if (!is_array($plans)) { return []; } $out = []; foreach ($plans as $plan) { $epr_id = (int)($plan['epr_id'] ?? 0); $ranges = $plan['ranges'] ?? []; if ($epr_id <= 0 || !is_array($ranges)) { continue; } $check = fxBibAssertSoloEpreuveForGlobalBatch($epr_id, $int_eve_id); if (!$check['success']) { continue; } $ranges = array_values(array_filter(array_map('intval', $ranges))); if (empty($ranges)) { continue; } $lockCheck = fxBibAssertRangesUnlocked($epr_id, $ranges); if (!$lockCheck['success']) { continue; } $out[] = [ 'epr_id' => $epr_id, 'ranges' => $ranges, ]; } return $out; } /** MSIN-4440 — Pastille tuile Anomalies (style Inter épreuve, sans compteur rouge). */ function renderBibAnomaliesNavBadge($intCount = 0, $blnPending = false) { if ($blnPending || (int)$intCount <= 0) { return ''; } $strLabel = fxBibMsg('bib_v4_anomalies_nav_todo_fmt', (int)$intCount); if ($strLabel === '' || $strLabel === 'bib_v4_anomalies_nav_todo_fmt') { $strLabel = fxBibTexte('bib_v4_anomalies_nav_todo', 0) . ' : ' . (int)$intCount; } return '' . fxBibEsc($strLabel) . ''; } /** * MSIN-4440 — Bouton tuile de la grille outils bib v4. */ function renderBibToolNavBtn($strTool, $strTitleClef, $strBadgesHtml = '', $blnAlt = false, $strDocClef = '') { $strClass = 'bib-tool-btn'; if ($blnAlt) { $strClass .= ' bib-tool-btn--alt'; } $strTitle = fxBibTexte($strTitleClef, 0); // div role=button : évite button dans button (icônes aide/doc) — HTML invalide qui casse la grille. $html = ''; return $html; } /** * MSIN-4440 — Grille 3+2 des outils bib v4 + coquille panneau de détail unique. */ function renderBibToolNav($int_eve_id, $tabEpreuves, $strLangue = 'fr') { $int_eve_id = (int)$int_eve_id; $blnAllow = fxBibEventAllowsInterEprDuplicates($int_eve_id); $intSolo = 0; foreach ($tabEpreuves as $epr) { if ((int)($epr['epr_equipe'] ?? 0) !== 1) { $intSolo++; } } $strEventBadges = renderBibEventConfigHeaderNote($blnAllow); $strAnomBadges = renderBibAnomaliesNavBadge(0, true); $strNavLabel = ($strLangue === 'en') ? 'Bib management tools' : 'Outils gestion dossards'; ob_start(); ?>
$epr, 'ranges' => $tabRanges, ]; } if (count($tabSolo) < 2) { if (empty($tabSolo)) { return ''; } ob_start(); ?>

fxGetResults($sqlEpreuves); $sqlBibAssignments = "SELECT * FROM bib_assignements WHERE ba_actif = 1 ORDER BY ba_tri"; $tabBibAssignments = $objDatabase->fxGetResults($sqlBibAssignments); ?>

>
fxGetResults($sqlBibRanges); // MSIN-4436 — Chargement page : stats séquences sans trous/doublons (calcul complet en AJAX refresh). $tabBibStats = fxInfosBibRange(intval($tabEpreuves[$i]['epr_id']), 0, false); $infoBib = fxInfosBibEpreuve(intval($tabEpreuves[$i]['epr_id'])); // MSIN-4379 — Étape 2 : détecte si l'auto est actif (au moins une séquence epr_bib_auto=1). // Prochain tour : étape 6–7 (assignation à l'achat). $blnAutoActive = fxIsBibAutoActive((int)$tabEpreuves[$i]['epr_id']); $blnHasLockedRanges = fxBibEpreuveHasLockedRanges((int)$tabEpreuves[$i]['epr_id']); ?>
0); if ($teamMode <= 0) { $teamMode = 1; } ?>
0, p2.pec_id, p2.par_id)) FROM resultats_participants p2 WHERE p2.epr_id = ($strEprIdExpr) AND $strBibP2 = ($strBibNumExpr) AND p2.is_cancelled = 0 ) ELSE ( SELECT COUNT(*) FROM resultats_participants p2 WHERE p2.epr_id = ($strEprIdExpr) AND $strBibP2 = ($strBibNumExpr) AND p2.is_cancelled = 0 ) END)"; } /** SQL — vrai doublon (anomalie), hors partage légitime entre coéquipiers (mode équipe 3). */ function fxBibSqlIsBibDuplicateAnomaly($strEprIdExpr, $strBibNumExpr) { return fxBibSqlDuplicateUnitCount($strEprIdExpr, $strBibNumExpr) . ' > 1'; } function fxInfosBibRange($epr_id = 0, $epr_bib_id = 0, $blnIncludeGaps = true){ global $objDatabase, $vDomaine; static $memCache = []; $epr_id = (int)$epr_id; $epr_bib_id = (int)$epr_bib_id; $blnIncludeGaps = ($blnIncludeGaps !== false); $strCacheKey = $epr_id . ':' . $epr_bib_id . ':' . ($blnIncludeGaps ? 'g' : 'l'); if (array_key_exists($strCacheKey, $memCache)) { return $memCache[$strCacheKey]; } $where = []; if ($epr_id > 0) { $where[] = "b.epr_id = " . $epr_id; } if ($epr_bib_id > 0) { $where[] = "b.epr_bib_id = " . $epr_bib_id; } if (empty($where)) { $memCache[$strCacheKey] = []; return $memCache[$strCacheKey]; } $where_sql = implode(' AND ', $where); $sqlBibNumP = fxBibSqlNoBibUnsigned('p.no_bib'); $sqlBibNumRp = fxBibSqlNoBibUnsigned('rp.no_bib'); if ($blnIncludeGaps) { $sqlGapCols = " ( SELECT COUNT(*) FROM ( SELECT (n1.N + n2.N * 10 + n3.N * 100 + n4.N * 1000) AS num FROM (SELECT 0 N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) n1 CROSS JOIN (SELECT 0 N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) n2 CROSS JOIN (SELECT 0 N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) n3 CROSS JOIN (SELECT 0 N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) n4 ) t WHERE t.num BETWEEN b.epr_bib_start AND IFNULL(( SELECT MAX($sqlBibNumRp) FROM resultats_participants rp WHERE rp.epr_id = b.epr_id AND rp.is_cancelled = 0 AND $sqlBibNumRp BETWEEN b.epr_bib_start AND b.epr_bib_finish ), b.epr_bib_start - 1) AND t.num NOT IN ( SELECT $sqlBibNumRp FROM resultats_participants rp WHERE rp.epr_id = b.epr_id AND rp.is_cancelled = 0 AND $sqlBibNumRp > 0 ) ) AS nb_trous, ( SELECT GROUP_CONCAT(t.num ORDER BY t.num SEPARATOR ',') FROM ( SELECT (n1.N + n2.N * 10 + n3.N * 100 + n4.N * 1000) AS num FROM (SELECT 0 N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) n1 CROSS JOIN (SELECT 0 N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) n2 CROSS JOIN (SELECT 0 N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) n3 CROSS JOIN (SELECT 0 N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) n4 ) t WHERE t.num BETWEEN b.epr_bib_start AND IFNULL(( SELECT MAX($sqlBibNumRp) FROM resultats_participants rp WHERE rp.epr_id = b.epr_id AND rp.is_cancelled = 0 AND $sqlBibNumRp BETWEEN b.epr_bib_start AND b.epr_bib_finish ), b.epr_bib_start - 1) AND t.num NOT IN ( SELECT $sqlBibNumRp FROM resultats_participants rp WHERE rp.epr_id = b.epr_id AND rp.is_cancelled = 0 AND $sqlBibNumRp > 0 ) ) AS liste_trous, ( SELECT GROUP_CONCAT(DISTINCT $sqlBibNumRp ORDER BY $sqlBibNumRp SEPARATOR ',') FROM resultats_participants rp WHERE rp.epr_id = b.epr_id AND rp.is_cancelled = 0 AND $sqlBibNumRp BETWEEN b.epr_bib_start AND b.epr_bib_finish AND " . fxBibSqlIsBibDuplicateAnomaly('rp.epr_id', $sqlBibNumRp) . " ) AS liste_doublons"; } else { // MSIN-4436 — Chargement page : dispo/utilisés seulement (trous/doublons via refresh AJAX). $sqlGapCols = " 0 AS nb_trous, '' AS liste_trous, '' AS liste_doublons"; } $sqlBibStats = " SELECT b.epr_bib_id, b.epr_id, b.epr_bib_start, b.epr_bib_finish, b.epr_bib_auto, b.epr_bib_locked, COUNT(DISTINCT $sqlBibNumP) AS nb_utilises, MAX($sqlBibNumP) AS dernier_bib, (b.epr_bib_finish - b.epr_bib_start + 1) AS nb_total_range, $sqlGapCols FROM inscriptions_epreuves_bib b LEFT JOIN resultats_participants p ON p.epr_id = b.epr_id AND p.is_cancelled = 0 AND $sqlBibNumP BETWEEN b.epr_bib_start AND b.epr_bib_finish AND $sqlBibNumP > 0 WHERE $where_sql AND b.epr_bib_start IS NOT NULL AND b.epr_bib_finish IS NOT NULL GROUP BY b.epr_bib_id, b.epr_id, b.epr_bib_start, b.epr_bib_finish, b.epr_bib_auto, b.epr_bib_locked ORDER BY b.epr_bib_start "; $tabResult = $objDatabase->fxGetResults($sqlBibStats); $memCache[$strCacheKey] = is_array($tabResult) ? $tabResult : []; return $memCache[$strCacheKey]; } /** MSIN-4379 — Libellé court épreuve pour le dashboard anomalies. */ function fxBibAnomalyEprLabel(array $tabEpreuve, $strLangue = 'fr') { $str = fxBibEpreuveDisplayName($tabEpreuve, $strLangue); $strNom = trim($tabEpreuve['epr_nom_' . $strLangue] ?? ''); if ($strNom !== '' && stripos($str, $strNom) === false) { $str .= ' — ' . $strNom; } return $str; } /** SQL — l'épreuve a au moins une séquence de dossards configurée. */ function fxBibSqlEpreuveHasBibRanges($strEprIdExpr) { return "EXISTS ( SELECT 1 FROM inscriptions_epreuves_bib b0 WHERE b0.epr_id = ($strEprIdExpr) AND b0.epr_bib_start IS NOT NULL AND b0.epr_bib_finish IS NOT NULL )"; } /** * MSIN-4379 — Regroupe les lignes SQL de fxBibAnomalyDuplicateBibs par séquence + no_bib. * * @return array> */ function fxBibAnomalyGroupDuplicateRows(array $rows, $strLangue = 'fr') { $grouped = []; foreach ($rows as $row) { $intRangeId = (int)$row['epr_bib_id']; $key = ($intRangeId > 0 ? $intRangeId : (int)$row['epr_id']) . '-' . (int)$row['no_bib']; if (!isset($grouped[$key])) { $grouped[$key] = [ 'epr_id' => (int)$row['epr_id'], 'epr_label' => fxBibAnomalyEprLabel($row, $strLangue), 'range_id' => $intRangeId, 'range_start' => (int)$row['epr_bib_start'], 'range_end' => (int)$row['epr_bib_finish'], 'no_bib' => (int)$row['no_bib'], 'participants' => [], ]; } $grouped[$key]['participants'][] = [ 'par_id' => (int)$row['par_id'], 'par_prenom' => trim($row['par_prenom'] ?? ''), 'par_nom' => trim($row['par_nom'] ?? ''), ]; } return array_values($grouped); } /** * MSIN-4424 — Dossards en double entre épreuves (même no_bib, epr_id distincts). * Vide si le réglage événement autorise les doublons inter-épreuves. * * @return array> */ function fxBibAnomalyInterEprDuplicateBibs($int_eve_id, $strLangue = 'fr') { global $objDatabase; $int_eve_id = (int)$int_eve_id; if ($int_eve_id <= 0 || fxBibEventAllowsInterEprDuplicates($int_eve_id)) { return []; } $sqlBibNumP = fxBibSqlNoBibUnsigned('p.no_bib'); $sqlBibNumP2 = fxBibSqlNoBibUnsigned('p2.no_bib'); $sql = " SELECT $sqlBibNumP AS no_bib, p.epr_id, p.par_id, p.par_prenom, p.par_nom, e.epr_nom_fr, e.epr_nom_en, e.epr_type_fr, e.epr_type_en, e.epr_categorie_fr, e.epr_categorie_en, e.epr_actif FROM resultats_participants p INNER JOIN inscriptions_epreuves e ON e.epr_id = p.epr_id WHERE e.eve_id = $int_eve_id AND p.is_cancelled = 0 AND $sqlBibNumP > 0 AND $sqlBibNumP IN ( SELECT $sqlBibNumP2 FROM resultats_participants p2 INNER JOIN inscriptions_epreuves e2 ON e2.epr_id = p2.epr_id WHERE e2.eve_id = $int_eve_id AND p2.is_cancelled = 0 AND $sqlBibNumP2 > 0 GROUP BY $sqlBibNumP2 HAVING COUNT(DISTINCT p2.epr_id) > 1 ) ORDER BY $sqlBibNumP, p.epr_id, p.par_nom, p.par_prenom "; $rows = $objDatabase->fxGetResults($sql); if (!is_array($rows) || empty($rows)) { return []; } $grouped = []; foreach ($rows as $row) { $intBib = (int)$row['no_bib']; $key = (string)$intBib; if (!isset($grouped[$key])) { $grouped[$key] = [ 'no_bib' => $intBib, 'entries' => [], ]; } $intEprId = (int)$row['epr_id']; $strEprKey = (string)$intEprId; if (!isset($grouped[$key]['entries'][$strEprKey])) { $grouped[$key]['entries'][$strEprKey] = [ 'epr_id' => $intEprId, 'epr_label' => fxBibAnomalyEprLabel($row, $strLangue), 'participants' => [], ]; } $grouped[$key]['entries'][$strEprKey]['participants'][] = [ 'par_id' => (int)$row['par_id'], 'par_prenom' => trim($row['par_prenom'] ?? ''), 'par_nom' => trim($row['par_nom'] ?? ''), ]; } foreach ($grouped as &$item) { $item['entries'] = array_values($item['entries']); } unset($item); return array_values($grouped); } /** * MSIN-4379 — Dossards en double (même epr_id, même no_bib, >1 unité active). * Couvre les séquences configurées et les épreuves sans séquence (saisie manuelle). * * @return array> */ function fxBibAnomalyDuplicateBibs($int_eve_id, $strLangue = 'fr') { global $objDatabase; $int_eve_id = (int)$int_eve_id; if ($int_eve_id <= 0) { return []; } $sqlBibNumP = fxBibSqlNoBibUnsigned('p.no_bib'); $sqlInRange = " SELECT b.epr_bib_id, b.epr_id, b.epr_bib_start, b.epr_bib_finish, e.epr_nom_fr, e.epr_nom_en, e.epr_type_fr, e.epr_type_en, e.epr_categorie_fr, e.epr_categorie_en, e.epr_actif, $sqlBibNumP AS no_bib, p.par_id, p.par_prenom, p.par_nom FROM inscriptions_epreuves_bib b INNER JOIN inscriptions_epreuves e ON e.epr_id = b.epr_id INNER JOIN resultats_participants p ON p.epr_id = b.epr_id AND p.is_cancelled = 0 AND $sqlBibNumP BETWEEN b.epr_bib_start AND b.epr_bib_finish AND $sqlBibNumP > 0 WHERE e.eve_id = $int_eve_id AND b.epr_bib_start IS NOT NULL AND b.epr_bib_finish IS NOT NULL AND " . fxBibSqlIsBibDuplicateAnomaly('b.epr_id', $sqlBibNumP) . " ORDER BY b.epr_id, b.epr_bib_start, $sqlBibNumP, p.par_nom, p.par_prenom "; $sqlNoRange = " SELECT 0 AS epr_bib_id, p.epr_id, 0 AS epr_bib_start, 0 AS epr_bib_finish, e.epr_nom_fr, e.epr_nom_en, e.epr_type_fr, e.epr_type_en, e.epr_categorie_fr, e.epr_categorie_en, e.epr_actif, $sqlBibNumP AS no_bib, p.par_id, p.par_prenom, p.par_nom FROM resultats_participants p INNER JOIN inscriptions_epreuves e ON e.epr_id = p.epr_id WHERE e.eve_id = $int_eve_id AND p.is_cancelled = 0 AND $sqlBibNumP > 0 AND NOT " . fxBibSqlEpreuveHasBibRanges('p.epr_id') . " AND " . fxBibSqlIsBibDuplicateAnomaly('p.epr_id', $sqlBibNumP) . " ORDER BY p.epr_id, $sqlBibNumP, p.par_nom, p.par_prenom "; $rows = $objDatabase->fxGetResults($sqlInRange); if (!is_array($rows)) { $rows = []; } $rowsNoRange = $objDatabase->fxGetResults($sqlNoRange); if (is_array($rowsNoRange) && !empty($rowsNoRange)) { $rows = array_merge($rows, $rowsNoRange); } if (empty($rows)) { return []; } return fxBibAnomalyGroupDuplicateRows($rows, $strLangue); } /** * MSIN-4405 — Dossards orphelins : inscription annulée dont le no_bib occupe encore une séquence. * Cause alignée sur fxGetAnnulationCause (annule / transfert), pas de collision inter-épreuves. * * @return array> */ function fxBibAnomalyOrphanBibs($int_eve_id, $strLangue = 'fr') { global $objDatabase; $int_eve_id = (int)$int_eve_id; if ($int_eve_id <= 0) { return []; } $sqlBibNumP = fxBibSqlNoBibUnsigned('p.no_bib'); $sql = " SELECT b.epr_bib_id, b.epr_id, b.epr_bib_start, b.epr_bib_finish, e.epr_nom_fr, e.epr_nom_en, e.epr_type_fr, e.epr_type_en, e.epr_categorie_fr, e.epr_categorie_en, e.epr_actif, $sqlBibNumP AS no_bib, p.par_id, p.pec_id, p.par_prenom, p.par_nom, ec.pec_actif FROM inscriptions_epreuves_bib b INNER JOIN inscriptions_epreuves e ON e.epr_id = b.epr_id INNER JOIN resultats_participants p ON p.epr_id = b.epr_id AND p.eve_id = e.eve_id AND p.is_cancelled = 1 AND $sqlBibNumP > 0 AND $sqlBibNumP BETWEEN b.epr_bib_start AND b.epr_bib_finish INNER JOIN resultats_epreuves_commandees ec ON ec.pec_id_original = p.pec_id AND ec.epr_id = b.epr_id AND ec.is_cancelled = 1 WHERE e.eve_id = $int_eve_id AND b.epr_bib_start IS NOT NULL AND b.epr_bib_finish IS NOT NULL ORDER BY b.epr_id, b.epr_bib_start, $sqlBibNumP, p.par_nom, p.par_prenom "; $rows = $objDatabase->fxGetResults($sql); if (!is_array($rows) || empty($rows)) { return []; } $out = []; foreach ($rows as $row) { $intPecId = (int)$row['pec_id']; $strCause = fxGetAnnulationCause($intPecId, isset($row['pec_actif']) ? (int)$row['pec_actif'] : null); $out[] = [ 'epr_id' => (int)$row['epr_id'], 'epr_label' => fxBibAnomalyEprLabel($row, $strLangue), 'range_id' => (int)$row['epr_bib_id'], 'range_start' => (int)$row['epr_bib_start'], 'range_end' => (int)$row['epr_bib_finish'], 'no_bib' => (int)$row['no_bib'], 'orphan_cause' => $strCause, 'par_id' => (int)$row['par_id'], 'pec_id' => $intPecId, 'par_prenom' => trim($row['par_prenom'] ?? ''), 'par_nom' => trim($row['par_nom'] ?? ''), ]; } return $out; } /** MSIN-4405 — Clé i18n bib pour la cause d'orphelin (alignée fxGetAnnulationCause). */ function fxBibAnomalyOrphanCauseKey($strCause) { $strCause = trim((string)$strCause); if (!in_array($strCause, ['annule', 'transfert'], true)) { $strCause = 'annule'; } return 'bib_v4_anomalies_orphan_' . $strCause; } /** * MSIN-4379 — Inscriptions sans dossard : auto-assign échoué (séquences épuisées). * Lit inscriptions_evt_erreur (type bib_auto_miss), purge les lignes déjà corrigées. * @return array> */ function fxBibAnomalyAutoMissBibs($int_eve_id, $strLangue = 'fr') { global $objDatabase; $int_eve_id = (int)$int_eve_id; if ($int_eve_id <= 0 || !fxEvtErreurTableReady()) { return []; } $sqlBibNumP = fxBibSqlNoBibUnsigned('p.no_bib'); $sqlPurge = " UPDATE inscriptions_evt_erreur e INNER JOIN resultats_participants p ON p.par_id = e.par_id AND p.epr_id = e.epr_id SET e.evt_err_statut = 'resolu', e.evt_err_resolved = NOW() WHERE e.eve_id = $int_eve_id AND e.evt_err_type = 'bib_auto_miss' AND e.evt_err_statut = 'ouvert' AND ( p.is_cancelled = 1 OR $sqlBibNumP > 0 ) "; $objDatabase->fxQuery($sqlPurge); $sql = " SELECT e.evt_err_id, e.eve_id, e.epr_id, e.par_id, e.pec_id, e.no_commande, e.no_panier, e.com_id, e.evt_err_created, p.par_prenom, p.par_nom, ep.epr_nom_fr, ep.epr_nom_en, ep.epr_type_fr, ep.epr_type_en, ep.epr_categorie_fr, ep.epr_categorie_en, ep.epr_actif FROM inscriptions_evt_erreur e INNER JOIN resultats_participants p ON p.par_id = e.par_id AND p.epr_id = e.epr_id INNER JOIN inscriptions_epreuves ep ON ep.epr_id = e.epr_id WHERE e.eve_id = $int_eve_id AND e.evt_err_type = 'bib_auto_miss' AND e.evt_err_statut = 'ouvert' AND p.is_cancelled = 0 AND ($sqlBibNumP = 0 OR p.no_bib IS NULL OR p.no_bib = '' OR p.no_bib = '0') ORDER BY e.evt_err_created DESC, e.epr_id, p.par_nom, p.par_prenom "; $rows = $objDatabase->fxGetResults($sql); if (!is_array($rows) || empty($rows)) { return []; } $items = []; foreach ($rows as $row) { $items[] = [ 'evt_err_id' => (int)$row['evt_err_id'], 'epr_id' => (int)$row['epr_id'], 'epr_label' => fxBibAnomalyEprLabel($row, $strLangue), 'par_id' => (int)$row['par_id'], 'par_prenom' => trim($row['par_prenom'] ?? ''), 'par_nom' => trim($row['par_nom'] ?? ''), 'no_commande' => trim($row['no_commande'] ?? ''), 'evt_err_created' => trim($row['evt_err_created'] ?? ''), ]; } return $items; } /** * MSIN-4379 — Dossards assignés hors de toute séquence configurée pour l'épreuve. * Couvre la saisie manuelle (fiche coureur) ou tout assignement ne passant pas par les plages. * Inclut les épreuves sans aucune séquence (dossards uniques seulement — les doublons vont dans dupes). * * @return array> */ function fxBibAnomalyOutOfRangeBibs($int_eve_id, $strLangue = 'fr') { global $objDatabase; $int_eve_id = (int)$int_eve_id; if ($int_eve_id <= 0) { return []; } $sqlBibNumP = fxBibSqlNoBibUnsigned('p.no_bib'); $sql = " SELECT p.par_id, p.pec_id, p.par_prenom, p.par_nom, p.epr_id, $sqlBibNumP AS no_bib, e.epr_nom_fr, e.epr_nom_en, e.epr_type_fr, e.epr_type_en, e.epr_categorie_fr, e.epr_categorie_en, e.epr_actif FROM resultats_participants p INNER JOIN inscriptions_epreuves e ON e.epr_id = p.epr_id WHERE e.eve_id = $int_eve_id AND p.is_cancelled = 0 AND $sqlBibNumP > 0 AND ( ( " . fxBibSqlEpreuveHasBibRanges('p.epr_id') . " AND NOT EXISTS ( SELECT 1 FROM inscriptions_epreuves_bib b WHERE b.epr_id = p.epr_id AND b.epr_bib_start IS NOT NULL AND b.epr_bib_finish IS NOT NULL AND $sqlBibNumP BETWEEN b.epr_bib_start AND b.epr_bib_finish ) ) OR ( NOT " . fxBibSqlEpreuveHasBibRanges('p.epr_id') . " AND NOT " . fxBibSqlIsBibDuplicateAnomaly('p.epr_id', $sqlBibNumP) . " ) ) ORDER BY p.epr_id, $sqlBibNumP, p.par_nom, p.par_prenom "; $rows = $objDatabase->fxGetResults($sql); if (!is_array($rows) || empty($rows)) { return []; } $items = []; foreach ($rows as $row) { $items[] = [ 'epr_id' => (int)$row['epr_id'], 'epr_label' => fxBibAnomalyEprLabel($row, $strLangue), 'par_id' => (int)$row['par_id'], 'pec_id' => (int)$row['pec_id'], 'par_prenom' => trim($row['par_prenom'] ?? ''), 'par_nom' => trim($row['par_nom'] ?? ''), 'no_bib' => (int)$row['no_bib'], ]; } return $items; } /** MSIN-4379 — Agrège toutes les anomalies dossards pour un événement. */ function fxBibCollectAnomalies($int_eve_id, $strLangue = 'fr') { return [ 'dupes' => fxBibAnomalyDuplicateBibs($int_eve_id, $strLangue), 'inter_epr_dupes' => fxBibAnomalyInterEprDuplicateBibs($int_eve_id, $strLangue), 'out_of_range' => fxBibAnomalyOutOfRangeBibs($int_eve_id, $strLangue), 'orphans' => fxBibAnomalyOrphanBibs($int_eve_id, $strLangue), 'auto_miss' => fxBibAnomalyAutoMissBibs($int_eve_id, $strLangue), ]; } function fxBibAnomalyTotalCount(array $tabAnomalies) { $intTotal = 0; foreach (['dupes', 'inter_epr_dupes', 'out_of_range', 'orphans', 'auto_miss'] as $strKey) { if (!empty($tabAnomalies[$strKey]) && is_array($tabAnomalies[$strKey])) { $intTotal += count($tabAnomalies[$strKey]); } } return $intTotal; } /** MSIN-4424 — Corps du panneau configuration globale (réglages événement). */ function renderBibEventConfigPanelContent($int_eve_id) { $int_eve_id = (int)$int_eve_id; $blnAllow = fxBibEventAllowsInterEprDuplicates($int_eve_id); ob_start(); ?> ' . fxBibEsc($strLabel) . ''; } /** MSIN-4424 — Panneau repliable : configuration globale assignation dossards (événement). */ function renderBibEventConfigPanel($int_eve_id, $strLangue = 'fr') { $int_eve_id = (int)$int_eve_id; $blnAllow = fxBibEventAllowsInterEprDuplicates($int_eve_id); ob_start(); ?> , epreuves: array>} */ function fxBibCollectEventQtySummary($int_eve_id, $strLangue = 'fr') { global $objDatabase; $int_eve_id = (int)$int_eve_id; $tabTotals = [ 'a_assigner' => 0, 'bib_restant' => 0, 'orphelins' => 0, 'total' => 0, 'avec_bib' => 0, 'sans_bib' => 0, 'places_dispo' => 0, ]; $tabEpreuvesRows = []; if ($int_eve_id <= 0) { return ['totals' => $tabTotals, 'epreuves' => $tabEpreuvesRows]; } $sqlEpreuves = " SELECT * FROM inscriptions_epreuves WHERE eve_id = $int_eve_id AND epr_actif = 1 ORDER BY epr_id "; $tabEpreuves = $objDatabase->fxGetResults($sqlEpreuves); if (!is_array($tabEpreuves)) { $tabEpreuves = []; } $tabOrphans = fxBibAnomalyOrphanBibs($int_eve_id, $strLangue); $tabOrphansByEpr = []; foreach ($tabOrphans as $item) { $intEprId = (int)($item['epr_id'] ?? 0); if ($intEprId > 0) { $tabOrphansByEpr[$intEprId] = ($tabOrphansByEpr[$intEprId] ?? 0) + 1; } } foreach ($tabEpreuves as $epr) { $epr_id = (int)$epr['epr_id']; $infoBib = fxInfosBibEpreuve($epr_id); $arrQte = fxGetQuantites($epr_id); $intBibRestant = 0; $tabRanges = fxInfosBibRange($epr_id, 0, false); if (is_array($tabRanges)) { foreach ($tabRanges as $range) { if (is_array($range)) { $intBibRestant += fxBibRangeDispoFromRow($range); } } } $tabRow = [ 'epr_id' => $epr_id, 'epr_label' => fxBibAnomalyEprLabel($epr, $strLangue), 'total' => (int)($infoBib['total'] ?? 0), 'avec_bib' => (int)($infoBib['avec_bib'] ?? 0), 'sans_bib' => (int)($infoBib['sans_bib'] ?? 0), 'a_assigner' => (int)($infoBib['a_assigner'] ?? 0), 'bib_restant' => $intBibRestant, 'orphelins' => (int)($tabOrphansByEpr[$epr_id] ?? 0), 'places_dispo' => (int)($arrQte['qte_restante'] ?? 0), ]; $tabEpreuvesRows[] = $tabRow; $tabTotals['a_assigner'] += $tabRow['a_assigner']; $tabTotals['bib_restant'] += $tabRow['bib_restant']; $tabTotals['total'] += $tabRow['total']; $tabTotals['avec_bib'] += $tabRow['avec_bib']; $tabTotals['sans_bib'] += $tabRow['sans_bib']; $tabTotals['places_dispo'] += $tabRow['places_dispo']; } $tabTotals['orphelins'] = count($tabOrphans); return ['totals' => $tabTotals, 'epreuves' => $tabEpreuvesRows]; } /** MSIN-4424 — Coquille sommaire quantités (détail chargé à la première ouverture). */ function renderBibQtySummaryPanelShell($int_eve_id, $strLangue = 'fr') { global $vDomaine; $int_eve_id = (int)$int_eve_id; $strWait = fxBibTexte('bib_v4_js_loader_wait', 0); if ($strWait === '' || $strWait === 'bib_v4_js_loader_wait') { $strWait = ($strLangue === 'en') ? 'Loading...' : 'Chargement...'; } $strRunnerSrc = $vDomaine . '/images/ms1-runner-loader.gif?v=' . _VERSION_CODE; ob_start(); ?> 0; $blnHasOrphans = (int)($tabTotals['orphelins'] ?? 0) > 0; ob_start(); ?> $int_pec_id, 'lng' => $strLangue, 'inscr_return' => $strReturn, ] ); } /** MSIN-4436 — Lien « voir l'inscription » (gestion inscriptions v2). */ function fxBibAnomalyRenderInscrLink($int_eve_id, $int_pec_id, $strLangue = 'fr') { $strUrl = fxBibAnomalyInscrGestionUrl($int_eve_id, $int_pec_id, $strLangue); if ($strUrl === '') { return ''; } $strLabel = fxBibTexte('bib_v4_anomalies_view_inscription', 0); if ($strLabel === '' || $strLabel === 'bib_v4_anomalies_view_inscription') { $strLabel = ($strLangue === 'en') ? 'View registration' : "Voir l'inscription"; } return '' . fxBibEsc($strLabel) . ''; } /** * MSIN-4436 — Coquille anomalies au chargement page (détail via AJAX). * Évite 4 requêtes lourdes avant le premier affichage des épreuves. */ function renderBibAnomaliesPanelShell($int_eve_id, $strLangue = 'fr') { global $vDomaine; $int_eve_id = (int)$int_eve_id; $strWait = fxBibTexte('bib_v4_js_loader_wait', 0); if ($strWait === '' || $strWait === 'bib_v4_js_loader_wait') { $strWait = ($strLangue === 'en') ? 'Loading...' : 'Chargement...'; } $strRunnerSrc = $vDomaine . '/images/ms1-runner-loader.gif?v=' . _VERSION_CODE; ob_start(); ?> 0; ob_start(); ?> fxGetVar($sql) > 0; } /** MSIN-4379 — Refuse l'activation auto tant qu'il reste des inscriptions sans dossard. */ function fxBibAssertAllBibsAssignedForAuto($epr_id) { $info = fxInfosBibEpreuve(intval($epr_id)); $nbPending = (int)($info['a_assigner'] ?? 0); if ($nbPending > 0) { return [ 'success' => false, 'message' => fxBibMsg('bib_v4_ajax_auto_pending_bibs', $nbPending), ]; } return ['success' => true]; } /** * MSIN-4379 — Étape 2 * Enregistre la config auto : remet toutes les séquences à 0, puis marque * epr_bib_auto = 1 sur les epr_bib_id choisis. Si $active = false, tout désactive. * Prochain tour : étape 6 lira ces séquences pour fxAssignAutoBibForParticipant(). */ function fxSaveBibAutoConfig($epr_id, $ranges, $active = true) { global $objDatabase; $epr_id = intval($epr_id); $active = (bool)$active; if ($epr_id <= 0) { return ['success' => false, 'message' => fxBibMsg('bib_v4_ajax_epr_invalid')]; } $objDatabase->fxQuery(" UPDATE inscriptions_epreuves_bib SET epr_bib_auto = 0 WHERE epr_id = $epr_id "); if (!$active) { return ['success' => true, 'auto_active' => false]; } $pendingCheck = fxBibAssertAllBibsAssignedForAuto($epr_id); if (!$pendingCheck['success']) { return $pendingCheck; } if (empty($ranges) || !is_array($ranges)) { return ['success' => false, 'message' => fxBibMsg('bib_v4_ajax_no_seq_selected')]; } $ids = array_values(array_filter(array_map('intval', $ranges))); if (empty($ids)) { return ['success' => false, 'message' => fxBibMsg('bib_v4_ajax_no_seq_selected')]; } $strIds = implode(',', $ids); $sqlCheck = " SELECT COUNT(*) FROM inscriptions_epreuves_bib WHERE epr_id = $epr_id AND epr_bib_id IN ($strIds) "; $nbValid = (int)$objDatabase->fxGetVar($sqlCheck); if ($nbValid !== count($ids)) { return ['success' => false, 'message' => fxBibMsg('bib_v4_ajax_seq_invalid')]; } foreach ($ids as $bibId) { if (!fxBibIsRangeAvailableForAuto($epr_id, $bibId)) { return ['success' => false, 'message' => fxBibMsg('bib_v4_ajax_auto_seq_full')]; } } $lockCheck = fxBibAssertRangesUnlocked($epr_id, $ids); if (!$lockCheck['success']) { return $lockCheck; } $objDatabase->fxQuery(" UPDATE inscriptions_epreuves_bib SET epr_bib_auto = 1 WHERE epr_id = $epr_id AND epr_bib_id IN ($strIds) "); return ['success' => true, 'auto_active' => true]; } /** * MSIN-4379 — Étape 6 * Liste des epr_bib_id marqués epr_bib_auto = 1 pour une épreuve. */ function fxGetAutoBibRangeIds($epr_id) { global $objDatabase; $epr_id = intval($epr_id); if ($epr_id <= 0 || !fxIsBibAutoActive($epr_id)) { return []; } $sql = " SELECT epr_bib_id FROM inscriptions_epreuves_bib WHERE epr_id = $epr_id AND epr_bib_auto = 1 AND epr_bib_locked = 0 ORDER BY epr_bib_start "; $rows = $objDatabase->fxGetResults($sql); $ids = []; if (is_array($rows)) { foreach ($rows as $r) { $id = (int)($r['epr_bib_id'] ?? 0); if ($id > 0) { $ids[] = $id; } } } return $ids; } /** * MSIN-4379 — Étape 6 * Charge un participant resultats pour l'assignation auto (même jointures que le batch). */ function fxLoadParticipantForAutoBib($epr_id, $par_id) { global $objDatabase; $epr_id = intval($epr_id); $par_id = intval($par_id); if ($epr_id <= 0 || $par_id <= 0) { return null; } $sql = " SELECT p.*, p.pec_id, c.pec_id_original, c.pec_nom_equipe, c.no_equipe FROM resultats_participants p LEFT JOIN resultats_epreuves_commandees c ON c.pec_id_original = p.pec_id AND c.epr_id = p.epr_id WHERE p.epr_id = $epr_id AND p.par_id = $par_id AND p.is_cancelled = 0 LIMIT 1 "; return $objDatabase->fxGetRow($sql); } /** * MSIN-4379 — Étape 6 * Assigne un dossard à un participant à l'achat (pools epr_bib_auto = 1). * Réutilise fxFindNextBatchBib / fxApplyBatchParticipantBib et la logique équipe du batch. */ function fxAssignAutoBibForParticipant($epr_id, $par_id) { global $objDatabase; $epr_id = intval($epr_id); $par_id = intval($par_id); if ($epr_id <= 0 || $par_id <= 0) { return ['success' => false, 'message' => 'paramètres invalides']; } if (!fxIsBibAutoActive($epr_id)) { return ['success' => false, 'skipped' => true, 'message' => 'auto inactif']; } $tabRanges = fxGetRangesForBatchAssign($epr_id, fxGetAutoBibRangeIds($epr_id)); if (empty($tabRanges)) { return ['success' => false, 'message' => 'aucune plage auto']; } $p = fxLoadParticipantForAutoBib($epr_id, $par_id); if (!$p) { return ['success' => false, 'message' => 'participant introuvable']; } if (in_array((int)$p['rol_id'], [3, 4], true)) { return ['success' => false, 'skipped' => true, 'message' => 'rôle exclu']; } $currentBib = trim((string)($p['no_bib'] ?? '')); if ($currentBib !== '' && $currentBib !== '0') { return [ 'success' => true, 'skipped' => true, 'no_bib' => (int)$currentBib, ]; } $sqlEpreuve = " SELECT epr_equipe, epr_bib_team_mode FROM inscriptions_epreuves WHERE epr_id = $epr_id LIMIT 1 "; $tabEpreuve = $objDatabase->fxGetRow($sqlEpreuve); $isTeamEvent = $tabEpreuve && (int)$tabEpreuve['epr_equipe'] === 1; $teamMode = (int)($tabEpreuve['epr_bib_team_mode'] ?? 1); if ($teamMode <= 0) { $teamMode = 1; } $teamKey = fxGetBatchTeamKey($p); $foundBib = null; $teamNumber = 0; $usedBib = []; if ($isTeamEvent && $teamMode === 3 && $teamKey > 0) { $sqlTeamBib = " SELECT no_bib FROM resultats_participants WHERE epr_id = $epr_id AND pec_id = $teamKey AND is_cancelled = 0 AND no_bib IS NOT NULL AND no_bib <> '' AND no_bib <> 0 AND par_id <> $par_id LIMIT 1 "; $existingBib = (int)$objDatabase->fxGetVar($sqlTeamBib); if ($existingBib > 0) { $foundBib = $existingBib; $teamNumber = $existingBib; } } if ($foundBib === null) { $findTeamMode = $isTeamEvent ? $teamMode : 1; $foundBib = fxFindNextBatchBib($epr_id, $tabRanges, 'execute', $usedBib, $findTeamMode); } if ($foundBib === null) { if (function_exists('fxcreer_log')) { fxcreer_log('MSIN-4379: aucun dossard libre (epr_id=' . $epr_id . ', par_id=' . $par_id . ')'); } if ($p && function_exists('fxEvtErreurOuvrir')) { fxEvtErreurOuvrir('bib_auto_miss', [ 'eve_id' => (int)($p['eve_id'] ?? 0), 'epr_id' => $epr_id, 'par_id' => $par_id, 'pec_id' => (int)($p['pec_id'] ?? 0), 'no_commande' => trim((string)($p['no_commande'] ?? '')), 'no_panier' => trim((string)($p['no_panier'] ?? '')), 'com_id' => (int)($p['com_id'] ?? 0), ]); } return ['success' => false, 'message' => 'aucun dossard disponible']; } fxApplyBatchParticipantBib($epr_id, $par_id, $foundBib); if ($isTeamEvent && $teamKey > 0) { $updatedTeams = []; if ($teamMode === 3) { fxApplyBatchTeamNumber($epr_id, $teamKey, $foundBib, $updatedTeams); } else { $sqlEquipe = " SELECT no_equipe FROM resultats_epreuves_commandees WHERE pec_id_original = $teamKey AND epr_id = $epr_id LIMIT 1 "; $teamNumber = (int)$objDatabase->fxGetVar($sqlEquipe); if ($teamNumber <= 0) { $sqlMaxTeam = " SELECT MAX(CAST(no_equipe AS UNSIGNED)) FROM resultats_epreuves_commandees WHERE epr_id = $epr_id "; $teamNumber = (int)$objDatabase->fxGetVar($sqlMaxTeam) + 1; } fxApplyBatchTeamNumber($epr_id, $teamKey, $teamNumber, $updatedTeams); } } return [ 'success' => true, 'no_bib' => (int)$foundBib, ]; } function fxInfosBibEpreuve($epr_id){ global $objDatabase; static $memCache = []; $epr_id = intval($epr_id); if ($epr_id <= 0) { return [ 'total' => 0, 'avec_bib' => 0, 'sans_bib' => 0 ]; } if (isset($memCache[$epr_id])) { return $memCache[$epr_id]; } $sqlEpreuve = " SELECT epr_equipe, epr_bib_team_mode FROM inscriptions_epreuves WHERE epr_id = $epr_id LIMIT 1 "; $tabEpreuve = $objDatabase->fxGetRow($sqlEpreuve); $blnTeamOneBib = $tabEpreuve && (int)$tabEpreuve['epr_equipe'] === 1 && (int)($tabEpreuve['epr_bib_team_mode'] ?? 1) === 3; if ($blnTeamOneBib) { // MSIN-4432 — aligné sur fxGetQuantites / fxCountBatchAssignUnits : une équipe = une unité. $sql = " SELECT COUNT(DISTINCT p.pec_id) AS total, COUNT(DISTINCT CASE WHEN p.no_bib IS NOT NULL AND p.no_bib > 0 THEN p.pec_id ELSE NULL END) AS avec_bib FROM resultats_participants p WHERE p.epr_id = $epr_id AND p.is_cancelled = 0 AND p.pec_id > 0 "; $row = $objDatabase->fxGetRow($sql); $intTotal = (int)($row['total'] ?? 0); $intAvecBib = (int)($row['avec_bib'] ?? 0); } else { $sql = " SELECT COUNT(*) AS total, SUM(CASE WHEN no_bib IS NOT NULL AND no_bib > 0 THEN 1 ELSE 0 END) AS avec_bib FROM resultats_participants WHERE epr_id = $epr_id AND is_cancelled = 0 "; $row = $objDatabase->fxGetRow($sql); $intTotal = (int)($row['total'] ?? 0); $intAvecBib = (int)($row['avec_bib'] ?? 0); } $info = [ 'total' => $intTotal, 'avec_bib' => $intAvecBib, 'sans_bib' => max(0, $intTotal - $intAvecBib), 'a_assigner' => fxCountBatchAssignUnits($epr_id) ]; if ($tabEpreuve && (int)$tabEpreuve['epr_equipe'] === 1) { $info['nb_equipes'] = fxBibCountEquipes($epr_id); } $memCache[$epr_id] = $info; return $info; } function renderBibRanges($tabBibRanges, $epr_id = 0, $mode = 'assign') { //print_rsl($tabBibRanges); if (!is_array($tabBibRanges)) { $tabBibRanges = []; } if ((int)$epr_id <= 0 && !empty($tabBibRanges[0]['epr_id'])) { $epr_id = (int)$tabBibRanges[0]['epr_id']; } ob_start(); ?>
0) { ?>
0) { ?>
> > > 0) { ?>
0 && (int)$range['epr_bib_id'] > 0 )) { $showCheckbox = true; } if (!$blnRangeLocked && ( $mode === 'reset' && (int)$range['nb_utilises'] > 0 && (int)$range['epr_bib_id'] > 0 )) { $showCheckbox = true; } // MSIN-4379 — Étape 2 : mode « auto » — séquences avec dossards disponibles seulement. if (!$blnRangeLocked && ( $mode === 'auto' && $mem_dispo > 0 && (int)$range['epr_bib_id'] > 0 )) { $showCheckbox = true; } ?> checked>
0 && !empty($range['liste_trous'])) { ?>
0 && !empty($range['liste_doublons'])) { ?>
0; if ($hasRange) { $sqlRange = " SELECT * FROM inscriptions_epreuves_bib WHERE epr_bib_id = " . intval($range_id); $range = $objDatabase->fxGetRow($sqlRange); if (!$range) { return '
' . fxBibTexteTrad('bib_v4_view_range_not_found', 0) . '
'; } $start = (int)$range['epr_bib_start']; $end = (int)$range['epr_bib_finish']; } // ===================== // 2. GET EPREUVE // ===================== $sqlEpreuve = " SELECT * FROM inscriptions_epreuves WHERE epr_id = " . intval($epr_id); $tabEpreuve = $objDatabase->fxGetRow($sqlEpreuve); if (!$tabEpreuve) { return '
' . fxBibTexteTrad('bib_v4_view_epreuve_not_found', 0) . '
'; } // ===================== // 3. GET PARTICIPANTS (DB ou SIMULATION) // ===================== if (!empty($data)) { $tabParticipants = $data; } else { // DB // 👉 mode normal (DB) $sqlBibNumP = fxBibSqlNoBibUnsigned('p.no_bib'); $strWhereView = "p.epr_id = " . intval($epr_id); if ($hasRange) { $strWhereView .= " AND $sqlBibNumP > 0 AND $sqlBibNumP BETWEEN $start AND $end"; } else { $strWhereView .= " AND ( (COALESCE(p.is_cancelled, 0) = 0 AND COALESCE(c.is_cancelled, 0) = 0) OR $sqlBibNumP > 0 )"; } $sqlParticipants = " SELECT p.*, c.pec_nom_equipe, c.no_equipe, c.is_cancelled AS pec_is_cancelled FROM resultats_participants p LEFT JOIN resultats_epreuves_commandees c ON c.pec_id_original = p.pec_id AND c.epr_id = p.epr_id WHERE $strWhereView ORDER BY $sqlBibNumP, p.par_nom, p.par_prenom"; $tabParticipants = $objDatabase->fxGetResults($sqlParticipants); } ob_start(); ?>
"; if ($tabEpreuve['epr_type_' . $strLangue] != '') echo $tabEpreuve['epr_type_' . $strLangue]; echo " (" . $tabEpreuve['epr_id'] . ")"; ?>
fxGetResults($sql); } function fxGetRangesForBatchAssign($epr_id, $ranges) { global $objDatabase; // ===================== // VALIDATION // ===================== $epr_id = intval($epr_id); if ($epr_id <= 0 || empty($ranges) || !is_array($ranges)) { return []; } // Nettoyage des IDs (sécurité) $ids = array_map('intval', $ranges); $ids = array_filter($ids); if (empty($ids)) { return []; } // ===================== // QUERY LÉGÈRE // ===================== $strIds = implode(',', $ids); $sql = " SELECT epr_bib_id, epr_bib_start, epr_bib_finish FROM inscriptions_epreuves_bib WHERE epr_id = $epr_id AND epr_bib_id IN ($strIds) AND epr_bib_start IS NOT NULL AND epr_bib_finish IS NOT NULL ORDER BY epr_bib_start "; $rows = $objDatabase->fxGetResults($sql); // ===================== // NORMALISATION // ===================== $out = []; if (is_array($rows)) { foreach ($rows as $r) { $start = (int)$r['epr_bib_start']; $end = (int)$r['epr_bib_finish']; // sécurité if ($start > 0 && $end >= $start) { $out[] = [ 'id' => (int)$r['epr_bib_id'], 'start' => $start, 'end' => $end ]; } } } return $out; } function fxGetNextAvailableBib($epr_id, $tabRanges) { global $objDatabase; $epr_id = intval($epr_id); $sqlBibNum = fxBibSqlNoBibUnsigned('no_bib'); if ($epr_id <= 0 || empty($tabRanges)) { return null; } // ===================== // PARCOURIR LES RANGES // ===================== foreach ($tabRanges as $range) { $start = (int)$range['start']; $end = (int)$range['end']; if ($start <= 0 || $end < $start) { continue; } // ===================== // CHERCHER UN BIB LIBRE DANS CE RANGE // ===================== for ($i = $start; $i <= $end; $i++) { $sql = " SELECT COUNT(*) FROM resultats_participants WHERE epr_id = $epr_id AND $sqlBibNum = $i "; $exists = (int)$objDatabase->fxGetVar($sql); // 👉 trouvé un BIB libre if ($exists === 0) { return $i; } } } // 👉 aucun BIB disponible return null; } function fxLoadUsedBibsForBatch($epr_id) { global $objDatabase; $usedBib = []; $sql = " SELECT no_bib FROM resultats_participants WHERE epr_id = " . intval($epr_id) . " AND no_bib IS NOT NULL AND no_bib != '' AND no_bib > 0 "; $rows = $objDatabase->fxGetResults($sql); if (is_array($rows)) { foreach ($rows as $r) { $usedBib[(int)$r['no_bib']] = true; } } return $usedBib; } /** * Cherche le prochain dossard libre dans les séquences sélectionnées. * * Règle anti-doublon (mode execute / GO) : * - solo ou équipe « dossards individuels » : un numéro = une personne (COUNT participants) * - équipe « 1 équipe = 1 dossard » : un numéro = une équipe (COUNT DISTINCT pec_id) * → plusieurs coéquipiers peuvent partager le même no_bib * * @param int $teamMode epr_bib_team_mode (1 = individuel, 3 = 1 équipe = 1 dossard) */ function fxFindNextBatchBib($epr_id, $tabRanges, $mode, &$usedBib, $teamMode = 1) { $epr_id = intval($epr_id); $teamMode = (int)$teamMode; foreach ($tabRanges as $range) { $start = (int)$range['start']; $end = (int)$range['end']; for ($i = $start; $i <= $end; $i++) { if (!isset($usedBib[$i])) { // MSIN-4433 — simulation : réserve en mémoire ; execute : réservation après UPDATE réussi. if ($mode === 'simulation') { $usedBib[$i] = true; } return $i; } } } return null; } /** * MSIN-4433 — Vérifie en BD qu'un numéro est encore libre (assignation manuelle entre-temps). */ function fxBibIsNumberFreeInDb($epr_id, $intBib, $teamMode = 1) { global $objDatabase; $epr_id = (int)$epr_id; $intBib = (int)$intBib; $teamMode = (int)$teamMode; $sqlBibNum = fxBibSqlNoBibUnsigned('no_bib'); if ($epr_id <= 0 || $intBib <= 0) { return false; } if ($teamMode === 3) { $sqlCheck = " SELECT COUNT(DISTINCT pec_id) FROM resultats_participants WHERE epr_id = $epr_id AND $sqlBibNum = $intBib AND pec_id > 0 "; return (int)$objDatabase->fxGetVar($sqlCheck) === 0; } $sqlCheck = " SELECT COUNT(*) FROM resultats_participants WHERE epr_id = $epr_id AND $sqlBibNum = $intBib "; return (int)$objDatabase->fxGetVar($sqlCheck) === 0; } /** * MSIN-4433 — Prochain dossard libre pour un participant (1 UPDATE si execute). * Relecture BD avant écriture : cohérence si changement manuel pendant le batch. * * @return int|null Numéro attribué, ou null si épuisé / participant déjà servi. */ function fxTryAssignNextBibForParticipant($epr_id, $par_id, $tabRanges, $mode, &$usedBib, $teamMode = 1) { $epr_id = (int)$epr_id; $par_id = (int)$par_id; $teamMode = (int)$teamMode; $intMaxAttempts = 50000; for ($intAttempt = 0; $intAttempt < $intMaxAttempts; $intAttempt++) { $candidate = fxFindNextBatchBib($epr_id, $tabRanges, $mode, $usedBib, $teamMode); if ($candidate === null) { return null; } if ($mode === 'execute') { if (!fxBibIsNumberFreeInDb($epr_id, $candidate, $teamMode)) { $usedBib[$candidate] = true; continue; } $intAffected = fxApplyBatchParticipantBib($epr_id, $par_id, $candidate); if ($intAffected <= 0) { return null; } $usedBib[$candidate] = true; return (int)$candidate; } return (int)$candidate; } return null; } /** * Écrit le no_bib d'un participant (batch GO). * Note : en mode « 1 équipe = 1 dossard », plusieurs lignes peuvent légitimement * recevoir le même no_bib — ce n'est pas un doublon erroné. */ function fxApplyBatchParticipantBib($epr_id, $par_id, $foundBib) { global $objDatabase; $sqlUpdate = " UPDATE resultats_participants SET no_bib = " . intval($foundBib) . ", par_date_bib = IFNULL(par_date_bib, NOW()) WHERE par_id = " . intval($par_id) . " AND is_cancelled = 0 AND (no_bib IS NULL OR no_bib = '' OR no_bib = 0) "; $objDatabase->fxQuery($sqlUpdate); $intAffected = (int)$objDatabase->affected_rows; if ($intAffected > 0 && function_exists('fxEvtErreurResoudre')) { fxEvtErreurResoudre('bib_auto_miss', (int)$epr_id, (int)$par_id); } return $intAffected; } function fxApplyBatchTeamNumber($epr_id, $teamKey, $teamNumber, &$updatedTeams) { global $objDatabase; if ($teamKey <= 0 || $teamNumber <= 0 || isset($updatedTeams[$teamKey])) { return; } $sqlTeam = " UPDATE resultats_epreuves_commandees SET no_equipe = " . intval($teamNumber) . " WHERE pec_id_original = " . intval($teamKey) . " AND epr_id = " . intval($epr_id) . " "; $objDatabase->fxQuery($sqlTeam); $updatedTeams[$teamKey] = true; } function fxBuildBatchAssignmentRow($p, $foundBib, $teamNumber) { return [ 'rol_id' => $p['rol_id'], 'par_id' => (int)$p['par_id'], 'par_prenom' => $p['par_prenom'], 'par_nom' => $p['par_nom'], 'no_bib' => $foundBib, 'no_equipe' => $teamNumber, 'pec_nom_equipe' => $p['pec_nom_equipe'] ?? '', ]; } function fxGetBatchTeamKey($p) { return (int)($p['pec_id_original'] ?? $p['pec_id'] ?? 0); } /** * MSIN-4433 — Mode équipe 3 : dossard déjà posé sur un coéquipier (batch par paquets). */ function fxGetExistingTeamBibInDb($epr_id, $teamKey) { global $objDatabase; $epr_id = (int)$epr_id; $teamKey = (int)$teamKey; $sqlBibNum = fxBibSqlNoBibUnsigned('no_bib'); if ($epr_id <= 0 || $teamKey <= 0) { return null; } $sql = " SELECT $sqlBibNum FROM resultats_participants WHERE epr_id = $epr_id AND pec_id = $teamKey AND is_cancelled = 0 AND $sqlBibNum > 0 LIMIT 1 "; $bib = (int)$objDatabase->fxGetVar($sql); return $bib > 0 ? $bib : null; } function fxCountBatchAssignUnits($epr_id, $participants = null) { global $objDatabase; $epr_id = intval($epr_id); if ($epr_id <= 0) { return 0; } $sqlEpreuve = " SELECT epr_equipe, epr_bib_team_mode FROM inscriptions_epreuves WHERE epr_id = $epr_id LIMIT 1 "; $tabEpreuve = $objDatabase->fxGetRow($sqlEpreuve); $countParticipants = function ($rows) { return is_array($rows) ? count($rows) : 0; }; if (!$tabEpreuve || (int)$tabEpreuve['epr_equipe'] !== 1) { if (is_array($participants)) { return $countParticipants($participants); } return (int)$objDatabase->fxGetVar(" SELECT COUNT(*) FROM resultats_participants WHERE epr_id = $epr_id AND is_cancelled = 0 AND (no_bib IS NULL OR no_bib = '' OR no_bib = 0) AND rol_id NOT IN (3, 4) "); } $teamMode = (int)($tabEpreuve['epr_bib_team_mode'] ?? 1); if ($teamMode <= 0) { $teamMode = 1; } if ($teamMode !== 3) { if (is_array($participants)) { return $countParticipants($participants); } return (int)$objDatabase->fxGetVar(" SELECT COUNT(*) FROM resultats_participants WHERE epr_id = $epr_id AND is_cancelled = 0 AND (no_bib IS NULL OR no_bib = '' OR no_bib = 0) AND rol_id NOT IN (3, 4) "); } if (is_array($participants)) { $teams = []; foreach ($participants as $p) { $teamKey = fxGetBatchTeamKey($p); if ($teamKey > 0) { $teams[$teamKey] = true; } } return count($teams); } return (int)$objDatabase->fxGetVar(" SELECT COUNT(DISTINCT p.pec_id) FROM resultats_participants p WHERE p.epr_id = $epr_id AND p.is_cancelled = 0 AND (p.no_bib IS NULL OR p.no_bib = '' OR p.no_bib = 0) AND p.rol_id NOT IN (3, 4) AND p.pec_id > 0 "); } function fxProcessBatchAssignmentsSolo($epr_id, $participants, $tabRanges, $mode = 'simulation') { $assignments = []; // MSIN-4433 — état réel rechargé à chaque paquet (execute) ou simulation. $usedBib = fxLoadUsedBibsForBatch($epr_id); foreach ($participants as $p) { $foundBib = fxTryAssignNextBibForParticipant( $epr_id, (int)$p['par_id'], $tabRanges, $mode, $usedBib, 1 ); if ($foundBib === null) { break; } $assignments[] = fxBuildBatchAssignmentRow($p, $foundBib, 0); } return $assignments; } function fxProcessBatchAssignmentsEquipe($epr_id, $participants, $tabRanges, $mode, $teamMode) { $assignments = []; $teamMap = []; $updatedTeams = []; $teamBibMap = []; $nextTeamNumber = 1; $usedBib = fxLoadUsedBibsForBatch($epr_id); foreach ($participants as $p) { $par_id = (int)$p['par_id']; $teamKey = fxGetBatchTeamKey($p); $teamNumber = 0; $foundBib = null; if ($teamMode === 3 && $teamKey > 0) { if (isset($teamBibMap[$teamKey])) { $foundBib = $teamBibMap[$teamKey]; } else { $foundBib = fxGetExistingTeamBibInDb($epr_id, $teamKey); if ($foundBib !== null) { $teamBibMap[$teamKey] = $foundBib; } } } if ($teamMode === 3 && $teamKey > 0 && $foundBib !== null) { $teamNumber = $foundBib; if ($mode === 'execute') { $intAffected = fxApplyBatchParticipantBib($epr_id, $par_id, $foundBib); if ($intAffected <= 0) { continue; } fxApplyBatchTeamNumber($epr_id, $teamKey, $teamNumber, $updatedTeams); } } else { if ($teamMode !== 3 && $teamKey > 0) { if (!isset($teamMap[$teamKey])) { $teamMap[$teamKey] = $nextTeamNumber; $nextTeamNumber++; } $teamNumber = $teamMap[$teamKey]; } $foundBib = fxTryAssignNextBibForParticipant( $epr_id, $par_id, $tabRanges, $mode, $usedBib, $teamMode ); if ($foundBib === null) { break; } if ($teamMode === 3 && $teamKey > 0) { $teamBibMap[$teamKey] = $foundBib; $teamNumber = $foundBib; } if ($mode === 'execute') { fxApplyBatchTeamNumber($epr_id, $teamKey, $teamNumber, $updatedTeams); } } $assignments[] = fxBuildBatchAssignmentRow($p, $foundBib, $teamNumber); } return $assignments; } function fxProcessBatchAssignments($epr_id, $participants, $tabRanges, $mode = 'simulation') { global $objDatabase; if (empty($participants) || empty($tabRanges)) { return []; } $epr_id = intval($epr_id); $sqlEpreuve = " SELECT epr_equipe, epr_bib_team_mode FROM inscriptions_epreuves WHERE epr_id = $epr_id LIMIT 1 "; $tabEpreuve = $objDatabase->fxGetRow($sqlEpreuve); // Épreuves solo : logique simple, inchangée (1 personne = 1 dossard) if (!$tabEpreuve || (int)$tabEpreuve['epr_equipe'] !== 1) { return fxProcessBatchAssignmentsSolo($epr_id, $participants, $tabRanges, $mode); } // Épreuves équipe uniquement — voir fxProcessBatchAssignmentsEquipe() $teamMode = (int)($tabEpreuve['epr_bib_team_mode'] ?? 1); if ($teamMode <= 0) { $teamMode = 1; } return fxProcessBatchAssignmentsEquipe($epr_id, $participants, $tabRanges, $mode, $teamMode); } /** MSIN-4433 — Clé session pour accumuler les lignes assignées entre paquets AJAX. */ function fxBibBatchExecSessionKey($epr_id) { return 'bib_batch_exec_' . (int)$epr_id; } /** * MSIN-4433 — HTML résultat GO batch (batch_apply et dernier paquet). */ function fxBibBuildBatchApplyHtml($epr_id, array $exec, $intTotalInitial, $strLangue) { $assigned = count($exec); $total = max($assigned, (int)$intTotalInitial); $remaining = max(0, $total - $assigned); $status = 'success'; if ($assigned === 0) { $status = 'error'; } elseif ($assigned < $total) { $status = 'partial'; } if ($assigned === 0) { $info = fxBibMsg('bib_v4_ajax_assign_none'); } elseif ($assigned >= $total) { $info = fxBibMsg('bib_v4_ajax_assign_full', $assigned); } else { $info = fxBibMsg('bib_v4_ajax_assign_partial', $assigned, $remaining); } return renderBibView( $epr_id, 0, $strLangue, 'execute', $exec, fxBibTexte('bib_v4_ajax_title_assign'), $info, $status ); } function fxBuildBatchSummaryFromRanges($tabRangesInfos, $participants, $epr_id = 0) { $nbParticipants = fxCountBatchAssignUnits($epr_id, $participants); $nbDispo = 0; if (!empty($tabRangesInfos)) { foreach ($tabRangesInfos as $r) { $total = (int)($r['nb_total_range'] ?? 0); $used = (int)($r['nb_utilises'] ?? 0); $nbDispo += max(0, $total - $used); } } $nbAssignable = min($nbParticipants, $nbDispo); $nbManque = max(0, $nbParticipants - $nbDispo); return [ 'nb_dispo' => $nbDispo, 'nb_participants' => $nbParticipants, 'nb_assignable' => $nbAssignable, 'nb_manque' => $nbManque ]; } /** MSIN-4436 — Tous les participants avec dossard sur l'épreuve (y compris hors séquences). */ function fxGetParticipantsForEpreuveResetAll($epr_id) { global $objDatabase; $epr_id = (int)$epr_id; if ($epr_id <= 0) { return []; } $sqlBibNumP = fxBibSqlNoBibUnsigned('p.no_bib'); $sql = " SELECT p.* FROM resultats_participants p WHERE p.epr_id = $epr_id AND p.is_cancelled = 0 AND p.no_bib IS NOT NULL AND p.no_bib > 0 ORDER BY $sqlBibNumP "; return $objDatabase->fxGetResults($sql); } function fxGetParticipantsForBatchReset($epr_id, $ranges) { global $objDatabase; $epr_id = intval($epr_id); if ($epr_id <= 0 || empty($ranges) || !is_array($ranges)) { return []; } $ids = array_map('intval', $ranges); $ids = array_filter($ids); if (empty($ids)) { return []; } $tabRanges = fxGetRangesForBatchAssign($epr_id, $ids); if (empty($tabRanges)) { return []; } $sqlBibNumP = fxBibSqlNoBibUnsigned('p.no_bib'); $where = []; foreach ($tabRanges as $r) { $start = (int)$r['start']; $end = (int)$r['end']; $where[] = "($sqlBibNumP BETWEEN $start AND $end)"; } if (empty($where)) { return []; } $sql = " SELECT p.* FROM resultats_participants p WHERE p.epr_id = $epr_id AND p.is_cancelled = 0 AND p.no_bib IS NOT NULL AND p.no_bib > 0 AND ( " . implode(' OR ', $where) . " ) ORDER BY $sqlBibNumP "; return $objDatabase->fxGetResults($sql); } function fxResolveTeamBib( $p, &$teamBibMap, $epr_id ) { global $objDatabase; $teamKey = (int)($p['pec_id'] ?? 0); if ($teamKey <= 0) { return null; } // ===================== // MÉMOIRE // ===================== if (isset($teamBibMap[$teamKey])) { return $teamBibMap[$teamKey]; } // ===================== // DB EXISTANTE // ===================== $sql = " SELECT no_equipe FROM resultats_epreuves_commandees WHERE pec_id_original = $teamKey AND epr_id = " . intval($epr_id) . " LIMIT 1 "; $existingBib = (int)$objDatabase->fxGetVar($sql); if ($existingBib > 0) { $teamBibMap[$teamKey] = $existingBib; return $existingBib; } return null; } // ============================================================================= // MSIN-4433 — Impression PDF distribution des dossards // ============================================================================= /** MSIN-4433 — Limite de colonnes questions (format lettre 8½ × 11). */ function fxBibDistPrintMaxQuestions() { return 5; } /** MSIN-4433 — Nom affiché du compte connecté (promoteur ou superadmin). */ function fxBibGetCurrentPromoteurDisplayName() { if (!empty($_SESSION['usa_id']) && !empty($_SESSION['usa_info']) && is_array($_SESSION['usa_info'])) { $str = trim((string)($_SESSION['usa_info']['com_prenom'] ?? '') . ' ' . (string)($_SESSION['usa_info']['com_nom'] ?? '')); if ($str !== '') { return $str; } } if (!empty($_SESSION['com_info']) && is_array($_SESSION['com_info'])) { $str = trim((string)($_SESSION['com_info']['com_prenom'] ?? '') . ' ' . (string)($_SESSION['com_info']['com_nom'] ?? '')); if ($str !== '') { return $str; } } return trim((string)($_SESSION['com_prenom'] ?? '') . ' ' . (string)($_SESSION['com_nom'] ?? '')); } /** MSIN-4433 — Accès génération PDF (session promoteur + CSRF, pas de granularite v2 pour l'instant). */ function fxBibRequireDistPrintAccess() { if (!fxBibRequirePromoteurSession()) { http_response_code(403); header('Content-Type: text/plain; charset=utf-8'); echo fxBibMsg('bib_v4_ajax_session_invalid'); exit; } $strToken = $_POST['csrf_token'] ?? $_REQUEST['csrf_token'] ?? ''; if (!fxBibValidateCsrf($strToken)) { http_response_code(403); header('Content-Type: text/plain; charset=utf-8'); echo fxBibMsg('bib_v4_ajax_unauthorized'); exit; } } /** MSIN-4433 — Parse liste d'IDs questions depuis chaîne CSV. */ function fxBibParseDistPrintQueIds($strCsv) { if ($strCsv === null || $strCsv === '') { return []; } $out = []; foreach (explode(',', (string)$strCsv) as $part) { $intId = (int)trim($part); if ($intId > 0 && !in_array($intId, $out, true)) { $out[] = $intId; } } return $out; } /** MSIN-4433 — Sérialise les IDs questions pour stockage. */ function fxBibDistPrintQueIdsToCsv(array $tabQueIds) { $out = []; foreach ($tabQueIds as $mixId) { $intId = (int)$mixId; if ($intId > 0 && !in_array($intId, $out, true)) { $out[] = $intId; } } return implode(',', $out); } /** MSIN-4433 — Libellé court sexe pour le PDF. */ function fxBibDistPrintSexeLabel($strSexe, $strLangue = 'fr') { $strSexe = strtolower(trim((string)$strSexe)); if ($strLangue === 'en') { $tab = ['f' => 'F', 'h' => 'M', 'n' => 'X', 'a' => 'O']; } else { $tab = ['f' => 'F', 'h' => 'H', 'n' => 'X', 'a' => 'A']; } return $tab[$strSexe] ?? strtoupper($strSexe); } /** MSIN-4433 — Date/heure affichée (pied de page, panneau). */ function fxBibFormatDistPrintDateTime($strWhen = '', $strLangue = 'fr') { $ts = ($strWhen !== '') ? strtotime($strWhen) : time(); if ($ts === false) { $ts = time(); } if ($strLangue === 'en') { return date('F j, Y, g:i a', $ts); } $tabMonths = [ 1 => 'janvier', 2 => 'février', 3 => 'mars', 4 => 'avril', 5 => 'mai', 6 => 'juin', 7 => 'juillet', 8 => 'août', 9 => 'septembre', 10 => 'octobre', 11 => 'novembre', 12 => 'décembre', ]; $intDay = (int)date('j', $ts); $intMonth = (int)date('n', $ts); $intYear = (int)date('Y', $ts); $strTime = date('H:i', $ts); return $intDay . ' ' . ($tabMonths[$intMonth] ?? '') . ' ' . $intYear . ', ' . $strTime; } /** MSIN-4433 — Métadonnées dernière impression (événement). */ function fxBibGetDistPrintMeta($int_eve_id) { global $objDatabase; $int_eve_id = (int)$int_eve_id; if ($int_eve_id <= 0) { return ['at' => '', 'by' => '', 'com_id' => 0]; } $row = $objDatabase->fxGetRow(" SELECT eve_bib_dist_print_at, eve_bib_dist_print_by, eve_bib_dist_print_com_id FROM inscriptions_evenements WHERE eve_id = $int_eve_id LIMIT 1 "); if (!is_array($row)) { return ['at' => '', 'by' => '', 'com_id' => 0]; } return [ 'at' => trim((string)($row['eve_bib_dist_print_at'] ?? '')), 'by' => trim((string)($row['eve_bib_dist_print_by'] ?? '')), 'com_id' => (int)($row['eve_bib_dist_print_com_id'] ?? 0), ]; } /** MSIN-4433 — Persiste date / auteur de l'impression. */ function fxBibSaveDistPrintMeta($int_eve_id, $int_com_id, $str_by) { global $objDatabase; $int_eve_id = (int)$int_eve_id; $int_com_id = (int)$int_com_id; $str_by = trim((string)$str_by); if ($int_eve_id <= 0) { return; } $sql = " UPDATE inscriptions_evenements SET eve_bib_dist_print_at = '" . $objDatabase->fxEscape(fxGetDateTime()) . "', eve_bib_dist_print_by = '" . $objDatabase->fxEscape($str_by) . "', eve_bib_dist_print_com_id = $int_com_id WHERE eve_id = $int_eve_id LIMIT 1 "; $objDatabase->fxQuery($sql); } /** * MSIN-4433 — Données panneau : épreuves, questions éligibles, sélection mémorisée. * @return array{meta: array, epreuves: array>} */ function fxBibCollectDistPrintPanelData($int_eve_id, $strLangue = 'fr') { global $objDatabase; $int_eve_id = (int)$int_eve_id; $tabOut = ['meta' => fxBibGetDistPrintMeta($int_eve_id), 'epreuves' => []]; if ($int_eve_id <= 0) { return $tabOut; } $sqlEpreuves = " SELECT * FROM inscriptions_epreuves WHERE eve_id = $int_eve_id AND epr_actif = 1 ORDER BY epr_tri, epr_id "; $tabEpreuves = $objDatabase->fxGetResults($sqlEpreuves); if (!is_array($tabEpreuves)) { return $tabOut; } foreach ($tabEpreuves as $epr) { $epr_id = (int)($epr['epr_id'] ?? 0); if ($epr_id <= 0) { continue; } $tabQuestionOpts = fxBibGetQuestionSortOptions($epr_id, $int_eve_id, $strLangue); $tabSelected = fxBibParseDistPrintQueIds($epr['ba_bib_dist_que_ids'] ?? ''); $tabValidIds = []; foreach ($tabQuestionOpts as $opt) { if (preg_match('/^que:(\d+)$/', (string)($opt['key'] ?? ''), $m)) { $tabValidIds[] = (int)$m[1]; } } $tabSelected = array_values(array_intersect($tabSelected, $tabValidIds)); $tabOut['epreuves'][] = [ 'epr_id' => $epr_id, 'epr_label' => fxBibAnomalyEprLabel($epr, $strLangue), 'questions' => $tabQuestionOpts, 'selected' => $tabSelected, 'avec_bib' => (int)(fxInfosBibEpreuve($epr_id)['avec_bib'] ?? 0), ]; } return $tabOut; } /** MSIN-4433 — Parse sélections POST dist_print[epr_id][] = que_id. */ function fxBibParseDistPrintSelectionsFromPost(array $post) { $tabRaw = $post['dist_print'] ?? []; if (!is_array($tabRaw)) { return []; } $out = []; foreach ($tabRaw as $mixEprId => $mixQueIds) { $epr_id = (int)$mixEprId; if ($epr_id <= 0) { continue; } if (!is_array($mixQueIds)) { $mixQueIds = [$mixQueIds]; } $tabIds = []; foreach ($mixQueIds as $mixQueId) { $intQueId = (int)$mixQueId; if ($intQueId > 0 && !in_array($intQueId, $tabIds, true)) { $tabIds[] = $intQueId; } } $out[$epr_id] = $tabIds; } return $out; } /** * MSIN-4433 — Sauvegarde les questions cochées par épreuve (à la génération PDF). * @return array{success: bool, message?: string} */ function fxBibSaveDistPrintSelections($int_eve_id, array $tabSelections, $strLangue = 'fr') { global $objDatabase; $int_eve_id = (int)$int_eve_id; if ($int_eve_id <= 0) { return ['success' => false, 'message' => fxBibMsg('bib_v4_ajax_epr_invalid')]; } $intMax = fxBibDistPrintMaxQuestions(); $tabPanel = fxBibCollectDistPrintPanelData($int_eve_id, $strLangue); foreach ($tabPanel['epreuves'] as $tabEpr) { $epr_id = (int)($tabEpr['epr_id'] ?? 0); if ($epr_id <= 0) { continue; } $tabValidIds = []; foreach ($tabEpr['questions'] as $opt) { if (preg_match('/^que:(\d+)$/', (string)($opt['key'] ?? ''), $m)) { $tabValidIds[] = (int)$m[1]; } } $tabChosen = $tabSelections[$epr_id] ?? []; if (!is_array($tabChosen)) { $tabChosen = []; } $tabFiltered = []; foreach ($tabChosen as $intQueId) { $intQueId = (int)$intQueId; if ($intQueId > 0 && in_array($intQueId, $tabValidIds, true) && !in_array($intQueId, $tabFiltered, true)) { $tabFiltered[] = $intQueId; } } if (count($tabFiltered) > $intMax) { return [ 'success' => false, 'message' => fxBibMsg('bib_v4_dist_print_max_questions', $intMax), ]; } $strCsv = fxBibDistPrintQueIdsToCsv($tabFiltered); $sql = " UPDATE inscriptions_epreuves SET ba_bib_dist_que_ids = '" . $objDatabase->fxEscape($strCsv) . "' WHERE epr_id = $epr_id AND eve_id = $int_eve_id LIMIT 1 "; $objDatabase->fxQuery($sql); } return ['success' => true]; } /** * MSIN-4433 — Participants avec dossard (une ligne par no_bib), tri batch. * @return array> */ function fxBibGetParticipantsForDistPrint($epr_id, $sort1, $sort2 = '', $strLangue = 'fr') { global $objDatabase; $epr_id = (int)$epr_id; if ($epr_id <= 0) { return []; } $strSort1 = fxBibNormalizeSortKey($sort1); $strSort2 = fxBibNormalizeSortKey($sort2); if ($strSort1 === '') { return []; } if ($strSort1 === $strSort2) { $strSort2 = ''; } $sqlBibNumP = fxBibSqlNoBibUnsigned('p.no_bib'); $orderBy = fxBibBuildBatchOrderBy($epr_id, $strSort1, $strSort2, $strLangue); $sql = " SELECT p.par_id, p.par_id_original, p.pec_id, p.no_bib, p.par_nom, p.par_prenom, p.par_sexe FROM resultats_participants p LEFT JOIN resultats_epreuves_commandees c ON c.pec_id_original = p.pec_id AND c.epr_id = p.epr_id WHERE p.epr_id = $epr_id AND p.is_cancelled = 0 AND $sqlBibNumP > 0 AND p.rol_id NOT IN (3, 4) ORDER BY $orderBy "; $rows = $objDatabase->fxGetResults($sql); if (!is_array($rows)) { return []; } $tabSeen = []; $tabOut = []; foreach ($rows as $row) { $strBibKey = (string)(int)($row['no_bib'] ?? 0); if ($strBibKey === '0' || isset($tabSeen[$strBibKey])) { continue; } $tabSeen[$strBibKey] = true; $tabOut[] = $row; } return $tabOut; } /** MSIN-4433 — Clé de correspondance participant ↔ réponse question. */ function fxBibDistPrintAnswerKey(array $row) { $intPecId = (int)($row['pec_id'] ?? 0); $intParLookup = (int)($row['par_id_original'] ?? 0); if ($intParLookup <= 0) { $intParLookup = (int)($row['par_id'] ?? 0); } return $intParLookup . ':' . $intPecId; } /** * MSIN-4433 — Réponses questions pour une liste de participants. * resultats_questions.par_id = resultats_participants.par_id_original (legacy MS1). * @return array> clé "par_lookup:pec_id" => [que_id => réponse] */ function fxBibGetDistPrintQuestionAnswers($epr_id, array $tabQueIds, array $tabParticipants, $strLangue = 'fr') { global $objDatabase; $epr_id = (int)$epr_id; $tabQueIds = array_values(array_filter(array_map('intval', $tabQueIds))); if ($epr_id <= 0 || empty($tabQueIds) || empty($tabParticipants)) { return []; } $tabParIds = []; foreach ($tabParticipants as $row) { $intParId = (int)($row['par_id'] ?? 0); if ($intParId > 0) { $tabParIds[] = $intParId; } } $tabParIds = array_values(array_unique($tabParIds)); if (empty($tabParIds)) { return []; } $strCol = ($strLangue === 'en') ? 'que_choix_en' : 'que_choix_fr'; $strQueIds = implode(',', $tabQueIds); $strParIds = implode(',', $tabParIds); $sql = " SELECT p.par_id, p.par_id_original, p.pec_id, rq.que_id, TRIM(COALESCE(NULLIF(rq.$strCol, ''), NULLIF(rq.pqu_note, ''), NULLIF(rq.que_choix_fr, ''))) AS ans FROM resultats_participants p INNER JOIN resultats_questions rq ON rq.pec_id = p.pec_id AND rq.par_id = IF(p.par_id_original > 0, p.par_id_original, p.par_id) WHERE p.epr_id = $epr_id AND p.par_id IN ($strParIds) AND rq.que_id IN ($strQueIds) "; $rows = $objDatabase->fxGetResults($sql); if (!is_array($rows)) { return []; } $out = []; foreach ($rows as $row) { $strKey = fxBibDistPrintAnswerKey($row); $intQueId = (int)($row['que_id'] ?? 0); $out[$strKey][$intQueId] = trim((string)($row['ans'] ?? '')); } return $out; } /** MSIN-4433 — Tronque texte pour cellule PDF. */ function fxBibDistPrintTruncate($str, $intMax = 28) { $str = trim((string)$str); if ($str === '') { return ''; } if (function_exists('mb_strlen') && function_exists('mb_substr')) { if (mb_strlen($str) <= $intMax) { return $str; } return mb_substr($str, 0, max(1, $intMax - 1)) . '…'; } if (strlen($str) <= $intMax) { return $str; } return substr($str, 0, max(1, $intMax - 1)) . '…'; } /** MSIN-4433 — Génère et envoie le PDF (format lettre). */ function fxBibOutputDistPrintPdf($int_eve_id, $strLangue = 'fr', $strPrintedBy = '') { global $objDatabase; $int_eve_id = (int)$int_eve_id; if ($int_eve_id <= 0) { http_response_code(400); exit; } $tabEvent = $objDatabase->fxGetRow("SELECT * FROM inscriptions_evenements WHERE eve_id = $int_eve_id LIMIT 1"); if (!is_array($tabEvent)) { http_response_code(404); exit; } $strEventTitle = trim((string)($tabEvent['eve_nom_' . $strLangue] ?? '')); if ($strEventTitle === '') { $strEventTitle = trim((string)($tabEvent['eve_nom_fr'] ?? '')); } $strPrintedAt = fxBibFormatDistPrintDateTime('', $strLangue); if (trim((string)$strPrintedBy) === '') { $strPrintedBy = fxBibGetCurrentPromoteurDisplayName(); } $strFooter = sprintf( fxBibTexte('bib_v4_dist_print_footer', 0), $strPrintedAt, $strPrintedBy ); $tabPanel = fxBibCollectDistPrintPanelData($int_eve_id, $strLangue); $strDocRoot = $_SERVER['DOCUMENT_ROOT'] ?? dirname(__DIR__); require_once($strDocRoot . '/fpdf182/fpdf.php'); if (!class_exists('BibDistPrintPdf', false)) { class BibDistPrintPdf extends FPDF { public $footerText = ''; function Footer() { $this->SetY(-12); $this->SetFont('Arial', 'I', 7); $this->Cell(0, 4, utf8_decode($this->footerText), 0, 0, 'L'); $this->Cell(0, 4, utf8_decode('p. ' . $this->PageNo()), 0, 0, 'R'); } } } $pdf = new BibDistPrintPdf('P', 'mm', 'letter'); $pdf->footerText = $strFooter; $pdf->SetMargins(10, 12, 10); $pdf->SetAutoPageBreak(true, 14); $pdf->AddPage(); $pdf->SetFont('Arial', 'B', 14); $pdf->Cell(0, 8, utf8_decode(fxBibDistPrintTruncate($strEventTitle, 80)), 0, 1, 'C'); $pdf->SetFont('Arial', '', 10); $pdf->Cell(0, 6, utf8_decode(fxBibTexte('bib_v4_dist_print_title', 0)), 0, 1, 'C'); $pdf->Ln(4); $fltPageW = $pdf->GetPageWidth() - 20; $fltFixedW = 18 + 42 + 42 + 12; foreach ($tabPanel['epreuves'] as $tabEpr) { $epr_id = (int)($tabEpr['epr_id'] ?? 0); if ($epr_id <= 0) { continue; } $tabEprRow = $objDatabase->fxGetRow("SELECT * FROM inscriptions_epreuves WHERE epr_id = $epr_id LIMIT 1"); if (!is_array($tabEprRow)) { continue; } list($strSort1, $strSort2) = fxBibGetEpreuveDefaultSortKeys($tabEprRow); $tabParticipants = fxBibGetParticipantsForDistPrint($epr_id, $strSort1, $strSort2, $strLangue); // MSIN-4433 — Épreuve sans dossard assigné : ne pas imprimer la section. if (empty($tabParticipants)) { continue; } $tabSelectedQue = $tabEpr['selected'] ?? []; $tabQueCols = []; foreach ($tabEpr['questions'] as $opt) { if (preg_match('/^que:(\d+)$/', (string)($opt['key'] ?? ''), $m)) { $intQueId = (int)$m[1]; if (in_array($intQueId, $tabSelectedQue, true)) { $tabQueCols[] = [ 'que_id' => $intQueId, 'label' => (string)($opt['label'] ?? ('Q' . $intQueId)), ]; } } } $intNbQ = count($tabQueCols); $fltQTotal = max(0, $fltPageW - $fltFixedW); $fltQEach = ($intNbQ > 0) ? ($fltQTotal / $intNbQ) : 0; $pdf->SetFont('Arial', 'B', 11); $pdf->Cell(0, 7, utf8_decode(fxBibDistPrintTruncate($tabEpr['epr_label'] ?? '', 90)), 0, 1, 'L'); $pdf->Ln(1); $tabAnswers = fxBibGetDistPrintQuestionAnswers($epr_id, $tabSelectedQue, $tabParticipants, $strLangue); $pdf->SetFont('Arial', 'B', 8); $pdf->SetFillColor(230, 230, 230); $pdf->Cell(18, 6, utf8_decode(fxBibTexte('bib_v4_dist_print_col_bib', 0)), 1, 0, 'C', true); $pdf->Cell(42, 6, utf8_decode(fxBibTexte('bib_v4_dist_print_col_nom', 0)), 1, 0, 'L', true); $pdf->Cell(42, 6, utf8_decode(fxBibTexte('bib_v4_dist_print_col_prenom', 0)), 1, 0, 'L', true); $pdf->Cell(12, 6, utf8_decode(fxBibTexte('bib_v4_dist_print_col_sexe', 0)), 1, 0, 'C', true); foreach ($tabQueCols as $i => $col) { $blnLast = ($i === $intNbQ - 1); $pdf->Cell($fltQEach, 6, utf8_decode(fxBibDistPrintTruncate($col['label'], 22)), 1, $blnLast ? 1 : 0, 'L', true); } if ($intNbQ === 0) { $pdf->Ln(); } $pdf->SetFont('Arial', '', 8); foreach ($tabParticipants as $row) { $strKey = fxBibDistPrintAnswerKey($row); $pdf->Cell(18, 5, utf8_decode((string)(int)($row['no_bib'] ?? 0)), 1, 0, 'C'); $pdf->Cell(42, 5, utf8_decode(fxBibDistPrintTruncate($row['par_nom'] ?? '', 24)), 1, 0, 'L'); $pdf->Cell(42, 5, utf8_decode(fxBibDistPrintTruncate($row['par_prenom'] ?? '', 24)), 1, 0, 'L'); $pdf->Cell(12, 5, utf8_decode(fxBibDistPrintSexeLabel($row['par_sexe'] ?? '', $strLangue)), 1, 0, 'C'); foreach ($tabQueCols as $i => $col) { $intQueId = (int)$col['que_id']; $strAns = $tabAnswers[$strKey][$intQueId] ?? ''; $blnLast = ($i === $intNbQ - 1); $pdf->Cell($fltQEach, 5, utf8_decode(fxBibDistPrintTruncate($strAns, 24)), 1, $blnLast ? 1 : 0, 'L'); } if ($intNbQ === 0) { $pdf->Ln(); } } $pdf->Ln(5); } $strFilename = 'distribution-dossards-' . $int_eve_id . '.pdf'; $pdf->Output('I', $strFilename); } /** MSIN-4433 — Coquille panneau impression (chargement AJAX). */ function renderBibDistPrintPanelShell($int_eve_id, $strLangue = 'fr') { global $vDomaine; $int_eve_id = (int)$int_eve_id; $strWait = fxBibTexte('bib_v4_js_loader_wait', 0); if ($strWait === '' || $strWait === 'bib_v4_js_loader_wait') { $strWait = ($strLangue === 'en') ? 'Loading...' : 'Chargement...'; } $strRunnerSrc = $vDomaine . '/images/ms1-runner-loader.gif?v=' . _VERSION_CODE; ob_start(); ?>