From 527e3909e89505051fb57da62df37f63aafc0d98 Mon Sep 17 00:00:00 2001 From: stephan Date: Thu, 16 Jul 2026 10:02:14 -0400 Subject: [PATCH] =?UTF-8?q?MSIN-4445=20=E2=80=94=20Refine=20free=20assignm?= =?UTF-8?q?ent=20logic=20by=20enhancing=20duplicate=20bib=20number=20check?= =?UTF-8?q?s=20and=20improving=20feedback=20mechanisms=20for=20overlapping?= =?UTF-8?q?=20ranges.=20Update=20backend=20functions=20to=20ensure=20accur?= =?UTF-8?q?ate=20detection=20of=20assigned=20bibs=20within=20specified=20r?= =?UTF-8?q?anges=20and=20adjust=20UI=20components=20to=20reflect=20these?= =?UTF-8?q?=20changes.=20Increment=20version=20code=20to=204.72.830.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ajax_bib_range.php | 21 +++++++++- js/v2/bib-assignments.js | 33 +++++++++++---- php/inc_fx_bib_global_free.php | 75 ++++++++++++++++++++-------------- php/inc_settings.php | 2 +- 4 files changed, 90 insertions(+), 41 deletions(-) diff --git a/ajax_bib_range.php b/ajax_bib_range.php index b03c991..1904f5d 100644 --- a/ajax_bib_range.php +++ b/ajax_bib_range.php @@ -714,10 +714,29 @@ if ($action == 'batch_global_free_analysis') { $eprIds = json_decode($_POST['epr_ids'] ?? '[]', true); $tabEprIds = fxBibParseGlobalBatchGeneratedEprIds(is_array($eprIds) ? $eprIds : [], $int_eve_id); - if ($int_eve_id <= 0 || $intStart <= 0 || $intEnd < $intStart || $strSort1 === '' || empty($tabEprIds)) { + if ($int_eve_id <= 0 || $intStart <= 0 || $intEnd < $intStart) { echo json_encode([ 'success' => false, 'message' => fxBibMsg('bib_v4_global_batch_free_plan_invalid'), + 'duplicates' => ['has' => false, 'count' => 0, 'message' => ''], + ]); + exit; + } + + // MSIN-4445 — Doublons basés sur la plage entière (même si tri / épreuves incomplets). + $tabDupesOnly = fxBibFreeDuplicateRisk($int_eve_id, [], $intStart, $intEnd); + + if ($strSort1 === '') { + $strSort1 = 'ba:2'; + } + + if (empty($tabEprIds)) { + echo json_encode([ + 'success' => true, + 'info' => $tabDupesOnly['message'], + 'stats' => ['nb_epreuves' => 0, 'nb_participants' => 0, 'nb_bibs' => 0], + 'feedback' => fxBibFreeQtyFeedback($intEnd - $intStart + 1, 0), + 'duplicates' => $tabDupesOnly, ]); exit; } diff --git a/js/v2/bib-assignments.js b/js/v2/bib-assignments.js index b8354e8..2780f8f 100644 --- a/js/v2/bib-assignments.js +++ b/js/v2/bib-assignments.js @@ -1289,20 +1289,34 @@ function bibSetGlobalFreeDupesWarn(section, blnShow, strMessage) { var fields = bibGetGlobalBatchFreeRangeFields(section); - if (!fields || !fields.dupesEl) { + if (!fields) { return; } - if (!blnShow) { - fields.dupesEl.textContent = ''; - fields.dupesEl.classList.add('bib-ui-hidden'); - return; - } - - fields.dupesEl.textContent = strMessage + var msg = strMessage || bibJs('jsGlobalFreeDupesWarn') || 'Attention : vous êtes en train de créer des inscriptions avec des dossards en double.'; - fields.dupesEl.classList.remove('bib-ui-hidden'); + + if (fields.dupesEl) { + if (!blnShow) { + fields.dupesEl.textContent = ''; + fields.dupesEl.classList.add('bib-ui-hidden'); + } else { + fields.dupesEl.textContent = msg; + fields.dupesEl.classList.remove('bib-ui-hidden'); + } + return; + } + + // Repli si l'ancien markup n'a pas la ligne dédiée. + if (fields.feedback && blnShow) { + var base = (fields.qtyEl && fields.qtyEl.textContent) + ? fields.qtyEl.textContent + : (fields.feedback.textContent || ''); + fields.feedback.textContent = base + ? (base + ' — ' + msg) + : msg; + } } function bibUpdateGlobalFreeQtyFeedback(section) { @@ -1551,6 +1565,7 @@ data.message || bibJs('jsErrGeneric'), 'error' ); + bibSetGlobalFreeDupesWarn(section, false); return data; } diff --git a/php/inc_fx_bib_global_free.php b/php/inc_fx_bib_global_free.php index 4e16998..901890b 100644 --- a/php/inc_fx_bib_global_free.php +++ b/php/inc_fx_bib_global_free.php @@ -61,68 +61,83 @@ function fxBibFreeQtyFeedbackMessage(array $tabFeedback) { } /** - * MSIN-4445 — Nombre de dossards de la plage déjà utilisés sur une autre épreuve. - * Ces numéros seraient réassignés (doublon inter-épreuve) car le batch ne les saute pas. + * MSIN-4445 — Nombre de dossards déjà assignés dans [start, end] pour l'événement. + * Jointure via épreuve : plus fiable que p.eve_id seul. */ -function fxBibCountBibsUsedOutsideEpr($int_eve_id, $intStart, $intEnd, $intEprId) { +function fxBibCountAssignedBibsInRange($int_eve_id, $intStart, $intEnd) { global $objDatabase; $int_eve_id = (int)$int_eve_id; $intStart = (int)$intStart; $intEnd = (int)$intEnd; - $intEprId = (int)$intEprId; - if ($int_eve_id <= 0 || $intEprId <= 0 || $intStart <= 0 || $intEnd < $intStart) { + if ($int_eve_id <= 0 || $intStart <= 0 || $intEnd < $intStart) { return 0; } - $sqlBib = fxBibSqlNoBibUnsigned('no_bib'); + $sqlBib = fxBibSqlNoBibUnsigned('p.no_bib'); return (int)$objDatabase->fxGetVar(" SELECT COUNT(DISTINCT $sqlBib) - FROM resultats_participants - WHERE eve_id = $int_eve_id - AND epr_id <> $intEprId - AND is_cancelled = 0 - AND no_bib IS NOT NULL - AND TRIM(no_bib) <> '' + FROM resultats_participants p + INNER JOIN inscriptions_epreuves e ON e.epr_id = p.epr_id + WHERE e.eve_id = $int_eve_id + AND IFNULL(p.is_cancelled, 0) = 0 + AND p.no_bib IS NOT NULL + AND TRIM(p.no_bib) <> '' AND $sqlBib BETWEEN $intStart AND $intEnd "); } /** - * MSIN-4445 — Risque de doublons pour le plan libre (plages virtuelles vs dossards déjà pris ailleurs). + * MSIN-4445 — Risque de doublons pour une plage libre. + * Avertit si des dossards de la plage sont déjà portés, ou si une séquence + * existante chevauche la plage (même hors épreuves sélectionnées). * * @return array{has:bool, count:int, message:string} */ -function fxBibFreeDuplicateRisk($int_eve_id, $tabPlan) { +function fxBibFreeDuplicateRisk($int_eve_id, $tabPlan, $intStart = 0, $intEnd = 0) { $int_eve_id = (int)$int_eve_id; - $intCount = 0; + $intStart = (int)$intStart; + $intEnd = (int)$intEnd; - if (!is_array($tabPlan)) { + if ($intStart <= 0 || $intEnd < $intStart) { + // Repli : bornes du plan si Début/Fin absents. + foreach ((array)$tabPlan as $item) { + if (!empty($item['skip'])) { + continue; + } + $intItemStart = (int)($item['start'] ?? 0); + $intItemEnd = (int)($item['end'] ?? 0); + if ($intItemStart <= 0 || $intItemEnd < $intItemStart) { + continue; + } + if ($intStart <= 0 || $intItemStart < $intStart) { + $intStart = $intItemStart; + } + if ($intItemEnd > $intEnd) { + $intEnd = $intItemEnd; + } + } + } + + if ($int_eve_id <= 0 || $intStart <= 0 || $intEnd < $intStart) { return ['has' => false, 'count' => 0, 'message' => '']; } - foreach ($tabPlan as $item) { - if (!empty($item['skip'])) { - continue; - } + $intCount = fxBibCountAssignedBibsInRange($int_eve_id, $intStart, $intEnd); - $intCount += fxBibCountBibsUsedOutsideEpr( - $int_eve_id, - (int)($item['start'] ?? 0), - (int)($item['end'] ?? 0), - (int)($item['epr_id'] ?? 0) - ); - } + // Séquences déjà configurées qui chevauchent la plage (autre épreuve). + $tabOverlaps = fxBibGetOverlappingRanges($int_eve_id, $intStart, $intEnd, 0, 0); + $intOverlapSeq = is_array($tabOverlaps) ? count($tabOverlaps) : 0; - if ($intCount <= 0) { + if ($intCount <= 0 && $intOverlapSeq <= 0) { return ['has' => false, 'count' => 0, 'message' => '']; } return [ 'has' => true, - 'count' => $intCount, + 'count' => max($intCount, $intOverlapSeq), 'message' => fxBibMsg('bib_v4_global_batch_free_dupes_warn'), ]; } @@ -262,7 +277,7 @@ function fxBibBuildGlobalBatchFreeAnalysis($int_eve_id, $tabPlan, $intStart, $in } $tabFeedback = fxBibFreeQtyFeedback($intQty, $intPendingAll); - $tabDupes = fxBibFreeDuplicateRisk($int_eve_id, $tabPlan); + $tabDupes = fxBibFreeDuplicateRisk($int_eve_id, $tabPlan, $intStart, $intEnd); return [ 'stats' => $tabStats, diff --git a/php/inc_settings.php b/php/inc_settings.php index afb2fe7..310ab64 100644 --- a/php/inc_settings.php +++ b/php/inc_settings.php @@ -7,7 +7,7 @@ * Constantes * * **************/ -define('_VERSION_CODE', '4.72.829'); +define('_VERSION_CODE', '4.72.830'); define('_DATE_CODE', '2026-07-16'); //MSIN-4290 define('QR_SECRET_KEY', 'ms1_qr_2026_cle_secrete_longue_et_fixe');