MSIN-4456 — Enhance sorting functionality for team names in bib assignments. Introduce new functions to ensure reliable ordering by team name and implement PHP-based sorting for participants when the primary sort criterion is a team. Update AJAX responses to include additional sorting parameters. Increment version code to 4.72.833.

This commit is contained in:
2026-07-16 10:42:33 -04:00
parent 26b85d1892
commit e9752ddcb7
5 changed files with 213 additions and 11 deletions

View File

@ -941,7 +941,10 @@ if ($action == 'batch_go') {
echo json_encode([
'success' => true,
'html' => $html
'html' => $html,
// MSIN-4456 — diagnostic temporaire (Network) : vérifier sort_1=ba:5 pour Nom d'équipe
'sort_1' => $strSort1,
'sort_2' => $strSort2,
]);
exit;
@ -1271,6 +1274,9 @@ if ($action == 'reset_apply') {
$count++;
}
// MSIN-4453 — Effacer aussi no_equipe (sinon le team# original reste et est réutilisé)
fxClearTeamNumbersAfterBibReset($epr_id, $participants);
$html = renderBibView(
$epr_id,
0,
@ -1337,6 +1343,9 @@ if ($action == 'reset_all_apply') {
$count++;
}
// MSIN-4453 — Effacer aussi no_equipe (aligné reset legacy)
fxClearTeamNumbersAfterBibReset($epr_id);
$html = renderBibView(
$epr_id,
0,

View File

@ -205,11 +205,16 @@
if (!row) {
return { sort1: '', sort2: '' };
}
var s1 = row.querySelector('.batch-order');
var s2 = row.querySelector('.batch-order-2');
// MSIN-4456 — Lire explicitement primaire vs secondaire (évite toute ambiguïté de sélecteur).
var wrap1 = row.querySelector('.epr-batch-order-wrap:not(.epr-batch-order-wrap--secondary)');
var wrap2 = row.querySelector('.epr-batch-order-wrap--secondary');
var s1 = (wrap1 && wrap1.querySelector('select.batch-order'))
|| row.querySelector('select.batch-order');
var s2 = (wrap2 && wrap2.querySelector('select.batch-order-2'))
|| row.querySelector('select.batch-order-2');
return {
sort1: s1 ? s1.value : '',
sort2: s2 ? s2.value : ''
sort1: s1 ? String(s1.value || '').trim() : '',
sort2: s2 ? String(s2.value || '').trim() : ''
};
}

View File

@ -2934,6 +2934,85 @@ function fxBibQuestionSortAllowedForEpreuve($epr_id, $que_id, $eve_id) {
return (int)$objDatabase->fxGetVar($sql) > 0;
}
/**
* MSIN-4456 — Clause ORDER BY fiable pour « Nom d'équipe ».
* Regroupe d'abord par nom (pec / repli par_nom_equipe), puis pec_id (même nom ≠ même équipe).
*/
function fxBibTeamNameOrderClause() {
return "TRIM(IFNULL(NULLIF(c.pec_nom_equipe, ''), IFNULL(p.par_nom_equipe, ''))), p.pec_id";
}
/** MSIN-4456 — La clé de tri est-elle un critère ba_equipe (ex. Nom d'équipe) ? */
function fxBibSortKeyIsTeamCriterion($sortKey) {
global $objDatabase;
$sortKey = fxBibNormalizeSortKey($sortKey);
if (!preg_match('/^ba:(\d+)$/', $sortKey, $m)) {
return false;
}
return (int)$objDatabase->fxGetVar(
"SELECT ba_equipe FROM bib_assignements WHERE ba_id = " . (int)$m[1] . " AND ba_actif = 1 LIMIT 1"
) === 1;
}
/**
* MSIN-4456 — Re-tri PHP des participants (filet si l'ORDER BY SQL ne regroupe pas).
* Appliqué seulement quand le tri principal est un critère équipe.
*/
function fxBibSortParticipantsByTeamName(array $participants) {
if (empty($participants)) {
return $participants;
}
$rows = [];
foreach ($participants as $row) {
$rows[] = $row;
}
usort($rows, function ($a, $b) {
$strTeamA = trim((string)($a['pec_nom_equipe'] ?? ''));
if ($strTeamA === '') {
$strTeamA = trim((string)($a['par_nom_equipe'] ?? ''));
}
$strTeamB = trim((string)($b['pec_nom_equipe'] ?? ''));
if ($strTeamB === '') {
$strTeamB = trim((string)($b['par_nom_equipe'] ?? ''));
}
$intCmp = strcasecmp($strTeamA, $strTeamB);
if ($intCmp !== 0) {
return $intCmp;
}
$intPecA = (int)($a['pec_id_original'] ?? $a['pec_id'] ?? 0);
$intPecB = (int)($b['pec_id_original'] ?? $b['pec_id'] ?? 0);
if ($intPecA !== $intPecB) {
return $intPecA <=> $intPecB;
}
$intCmp = strcasecmp((string)($a['par_nom'] ?? ''), (string)($b['par_nom'] ?? ''));
if ($intCmp !== 0) {
return $intCmp;
}
$intCmp = strcasecmp((string)($a['par_prenom'] ?? ''), (string)($b['par_prenom'] ?? ''));
if ($intCmp !== 0) {
return $intCmp;
}
return ((int)($a['par_id'] ?? 0)) <=> ((int)($b['par_id'] ?? 0));
});
$out = [];
$intCtr = 1;
foreach ($rows as $row) {
$out[$intCtr++] = $row;
}
return $out;
}
/** MSIN-4436 — Fragment ORDER BY pour une clé de tri. */
function fxBibResolveSortClause($epr_id, $sortKey, $strLangue = 'fr', $eve_id = 0) {
global $objDatabase;
@ -2945,9 +3024,17 @@ function fxBibResolveSortClause($epr_id, $sortKey, $strLangue = 'fr', $eve_id =
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"
));
$tabBa = $objDatabase->fxGetRow(
"SELECT ba_sql_order, ba_equipe FROM bib_assignements WHERE ba_id = $intBaId AND ba_actif = 1 LIMIT 1"
);
if (!$tabBa) {
return '';
}
// MSIN-4456 — Ne pas se fier seul à ba_sql_order historique (souvent déjà + nom/prénom).
if ((int)($tabBa['ba_equipe'] ?? 0) === 1) {
return fxBibTeamNameOrderClause();
}
$orderBy = trim((string)($tabBa['ba_sql_order'] ?? ''));
return $orderBy !== '' ? $orderBy : '';
}
@ -2990,9 +3077,23 @@ function fxBibBuildBatchOrderBy($epr_id, $sort1, $sort2 = '', $strLangue = 'fr',
);
}
$strSort1 = fxBibNormalizeSortKey($sort1);
$strSort2 = fxBibNormalizeSortKey($sort2);
if ($strSort1 === $strSort2) {
$strSort2 = '';
}
$tabParts = [];
foreach ([$sort1, $sort2] as $mixKey) {
foreach ([$strSort1, $strSort2] as $mixKey) {
if ($mixKey === '') {
continue;
}
$strClause = fxBibResolveSortClause($epr_id, $mixKey, $strLangue, $eve_id);
// MSIN-4456 — Tri principal « nom d'équipe » + secondaire alphabétique :
// la clause équipe ne doit PAS déjà embarquer par_nom (sinon le 2e niveau est noyé).
if ($mixKey === $strSort1 && fxBibSortKeyIsTeamCriterion($mixKey) && $strSort2 !== '') {
$strClause = fxBibTeamNameOrderClause();
}
if ($strClause !== '' && !in_array($strClause, $tabParts, true)) {
$tabParts[] = $strClause;
}
@ -7590,7 +7691,18 @@ $sql = "
ORDER BY $orderBy
";
return $objDatabase->fxGetResults($sql);
$tabParticipants = $objDatabase->fxGetResults($sql);
if (!is_array($tabParticipants) || empty($tabParticipants)) {
return is_array($tabParticipants) ? $tabParticipants : [];
}
// MSIN-4456 — Filet : si tri principal = nom d'équipe, forcer le regroupement en PHP
// (dossards individuels comme 1 équipe = 1 dossard).
if (fxBibSortKeyIsTeamCriterion($strSort1)) {
$tabParticipants = fxBibSortParticipantsByTeamName($tabParticipants);
}
return $tabParticipants;
}
function fxGetRangesForBatchAssign($epr_id, $ranges) {
global $objDatabase;
@ -8202,6 +8314,66 @@ function fxBuildBatchSummaryFromRanges($tabRangesInfos, $participants, $epr_id =
'nb_manque' => $nbManque
];
}
/**
* MSIN-4453 — Efface no_equipe après réinitialisation des dossards (aligné sur le reset legacy).
* Sans $tabParticipants : toute l'épreuve. Avec liste (reset par séquences) : uniquement les équipes
* dont plus aucun membre actif n'a de dossard.
*/
function fxClearTeamNumbersAfterBibReset($epr_id, $tabParticipants = null) {
global $objDatabase;
$epr_id = (int)$epr_id;
if ($epr_id <= 0) {
return;
}
if ($tabParticipants === null) {
$sql = "
UPDATE resultats_epreuves_commandees
SET no_equipe = NULL
WHERE epr_id = $epr_id
";
$objDatabase->fxQuery($sql);
return;
}
$teamKeys = [];
foreach ($tabParticipants as $p) {
$teamKey = fxGetBatchTeamKey($p);
if ($teamKey > 0) {
$teamKeys[$teamKey] = true;
}
}
if (empty($teamKeys)) {
return;
}
$ids = implode(',', array_map('intval', array_keys($teamKeys)));
$sqlBibNum = fxBibSqlNoBibUnsigned('p.no_bib');
$sql = "
UPDATE resultats_epreuves_commandees c
SET c.no_equipe = NULL
WHERE c.epr_id = $epr_id
AND c.pec_id_original IN ($ids)
AND NOT EXISTS (
SELECT 1
FROM resultats_participants p
WHERE p.pec_id = c.pec_id_original
AND p.epr_id = c.epr_id
AND p.is_cancelled = 0
AND p.no_bib IS NOT NULL
AND $sqlBibNum > 0
)
";
$objDatabase->fxQuery($sql);
}
/** MSIN-4436 — Tous les participants avec dossard sur l'épreuve (y compris hors séquences). */
function fxGetParticipantsForEpreuveResetAll($epr_id) {
global $objDatabase;

View File

@ -7,7 +7,7 @@
* Constantes *
*
**************/
define('_VERSION_CODE', '4.72.832');
define('_VERSION_CODE', '4.72.833');
define('_DATE_CODE', '2026-07-16');
//MSIN-4290
define('QR_SECRET_KEY', 'ms1_qr_2026_cle_secrete_longue_et_fixe');

View File

@ -0,0 +1,16 @@
-- MSIN-4456 — Tri batch « Nom d'équipe » : clause SQL sans nom/prénom embarqués
-- Contexte : en mode dossards individuels, le ba_sql_order historique
-- TRIM(c.pec_nom_equipe), p.par_nom, p.par_prenom
-- noyait le tri secondaire et pouvait faire paraître un ordre purement alphabétique.
-- Notes : idempotent. Le PHP force aussi cette clause pour ba_equipe = 1.
UPDATE bib_assignements
SET ba_sql_order = 'TRIM(IFNULL(NULLIF(c.pec_nom_equipe, ''''), IFNULL(p.par_nom_equipe, ''''))), p.pec_id'
WHERE ba_actif = 1
AND ba_equipe = 1
AND (
ba_sql_order LIKE '%pec_nom_equipe%'
OR ba_nom_fr LIKE '%équipe%'
OR ba_nom_fr LIKE '%equipe%'
OR ba_nom_en LIKE '%team%'
);