MSIN-4445 — Refine free assignment logic by enhancing duplicate bib number checks and improving feedback mechanisms for overlapping ranges. Update backend functions to ensure accurate detection of assigned bibs within specified ranges and adjust UI components to reflect these changes. Increment version code to 4.72.830.

This commit is contained in:
2026-07-16 10:02:14 -04:00
parent ce63b9325e
commit 527e3909e8
4 changed files with 90 additions and 41 deletions

View File

@ -714,10 +714,29 @@ if ($action == 'batch_global_free_analysis') {
$eprIds = json_decode($_POST['epr_ids'] ?? '[]', true); $eprIds = json_decode($_POST['epr_ids'] ?? '[]', true);
$tabEprIds = fxBibParseGlobalBatchGeneratedEprIds(is_array($eprIds) ? $eprIds : [], $int_eve_id); $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([ echo json_encode([
'success' => false, 'success' => false,
'message' => fxBibMsg('bib_v4_global_batch_free_plan_invalid'), '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; exit;
} }

View File

@ -1289,20 +1289,34 @@
function bibSetGlobalFreeDupesWarn(section, blnShow, strMessage) { function bibSetGlobalFreeDupesWarn(section, blnShow, strMessage) {
var fields = bibGetGlobalBatchFreeRangeFields(section); var fields = bibGetGlobalBatchFreeRangeFields(section);
if (!fields || !fields.dupesEl) { if (!fields) {
return; return;
} }
if (!blnShow) { var msg = strMessage
fields.dupesEl.textContent = '';
fields.dupesEl.classList.add('bib-ui-hidden');
return;
}
fields.dupesEl.textContent = strMessage
|| bibJs('jsGlobalFreeDupesWarn') || bibJs('jsGlobalFreeDupesWarn')
|| 'Attention : vous êtes en train de créer des inscriptions avec des dossards en double.'; || '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) { function bibUpdateGlobalFreeQtyFeedback(section) {
@ -1551,6 +1565,7 @@
data.message || bibJs('jsErrGeneric'), data.message || bibJs('jsErrGeneric'),
'error' 'error'
); );
bibSetGlobalFreeDupesWarn(section, false);
return data; return data;
} }

View File

@ -61,68 +61,83 @@ function fxBibFreeQtyFeedbackMessage(array $tabFeedback) {
} }
/** /**
* MSIN-4445 — Nombre de dossards de la plage déjà utilisés sur une autre épreuve. * MSIN-4445 — Nombre de dossards déjà assignés dans [start, end] pour l'événement.
* Ces numéros seraient réassignés (doublon inter-épreuve) car le batch ne les saute pas. * 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; global $objDatabase;
$int_eve_id = (int)$int_eve_id; $int_eve_id = (int)$int_eve_id;
$intStart = (int)$intStart; $intStart = (int)$intStart;
$intEnd = (int)$intEnd; $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; return 0;
} }
$sqlBib = fxBibSqlNoBibUnsigned('no_bib'); $sqlBib = fxBibSqlNoBibUnsigned('p.no_bib');
return (int)$objDatabase->fxGetVar(" return (int)$objDatabase->fxGetVar("
SELECT COUNT(DISTINCT $sqlBib) SELECT COUNT(DISTINCT $sqlBib)
FROM resultats_participants FROM resultats_participants p
WHERE eve_id = $int_eve_id INNER JOIN inscriptions_epreuves e ON e.epr_id = p.epr_id
AND epr_id <> $intEprId WHERE e.eve_id = $int_eve_id
AND is_cancelled = 0 AND IFNULL(p.is_cancelled, 0) = 0
AND no_bib IS NOT NULL AND p.no_bib IS NOT NULL
AND TRIM(no_bib) <> '' AND TRIM(p.no_bib) <> ''
AND $sqlBib BETWEEN $intStart AND $intEnd 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} * @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; $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' => '']; return ['has' => false, 'count' => 0, 'message' => ''];
} }
foreach ($tabPlan as $item) { $intCount = fxBibCountAssignedBibsInRange($int_eve_id, $intStart, $intEnd);
if (!empty($item['skip'])) {
continue;
}
$intCount += fxBibCountBibsUsedOutsideEpr( // Séquences déjà configurées qui chevauchent la plage (autre épreuve).
$int_eve_id, $tabOverlaps = fxBibGetOverlappingRanges($int_eve_id, $intStart, $intEnd, 0, 0);
(int)($item['start'] ?? 0), $intOverlapSeq = is_array($tabOverlaps) ? count($tabOverlaps) : 0;
(int)($item['end'] ?? 0),
(int)($item['epr_id'] ?? 0)
);
}
if ($intCount <= 0) { if ($intCount <= 0 && $intOverlapSeq <= 0) {
return ['has' => false, 'count' => 0, 'message' => '']; return ['has' => false, 'count' => 0, 'message' => ''];
} }
return [ return [
'has' => true, 'has' => true,
'count' => $intCount, 'count' => max($intCount, $intOverlapSeq),
'message' => fxBibMsg('bib_v4_global_batch_free_dupes_warn'), '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); $tabFeedback = fxBibFreeQtyFeedback($intQty, $intPendingAll);
$tabDupes = fxBibFreeDuplicateRisk($int_eve_id, $tabPlan); $tabDupes = fxBibFreeDuplicateRisk($int_eve_id, $tabPlan, $intStart, $intEnd);
return [ return [
'stats' => $tabStats, 'stats' => $tabStats,

View File

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