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:
@ -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,
|
||||
|
||||
@ -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() : ''
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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');
|
||||
|
||||
16
sql/MSIN-4456-bib-team-name-sort.sql
Normal file
16
sql/MSIN-4456-bib-team-name-sort.sql
Normal 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%'
|
||||
);
|
||||
Reference in New Issue
Block a user