MSIN-4445 — Enhance free assignment functionality by adding duplicate bib number warnings for overlapping ranges. Implement backend checks for bibs already assigned to other events and update UI components to display relevant feedback. Increment version code to 4.72.829.
This commit is contained in:
@ -723,13 +723,14 @@ if ($action == 'batch_global_free_analysis') {
|
||||
}
|
||||
|
||||
$tabPlan = fxBibBuildFreeRangePlan($int_eve_id, $tabEprIds, $intStart, $intEnd, $strSort1, $strSort2);
|
||||
$tabAnalysis = fxBibBuildGlobalBatchFreeAnalysis($tabPlan, $intStart, $intEnd);
|
||||
$tabAnalysis = fxBibBuildGlobalBatchFreeAnalysis($int_eve_id, $tabPlan, $intStart, $intEnd);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'info' => $tabAnalysis['info'],
|
||||
'stats' => $tabAnalysis['stats'],
|
||||
'feedback' => $tabAnalysis['feedback'],
|
||||
'duplicates' => $tabAnalysis['duplicates'],
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
@ -1472,6 +1472,10 @@ ul.ms1-menu-compte li a:hover, ul.ms1-menu-compte li a:active {
|
||||
.bib-global-free-range__feedback{
|
||||
margin-top:6px;
|
||||
}
|
||||
.bib-global-free-range__qty,
|
||||
.bib-global-free-range__dupes{
|
||||
font-size:12.5px;
|
||||
}
|
||||
.bib-global-free-range__feedback--juste{
|
||||
color:#155724;
|
||||
font-weight:600;
|
||||
@ -1484,6 +1488,11 @@ ul.ms1-menu-compte li a:hover, ul.ms1-menu-compte li a:active {
|
||||
color:#004085;
|
||||
font-weight:600;
|
||||
}
|
||||
.bib-global-free-range__dupes{
|
||||
color:#a94442;
|
||||
font-weight:600;
|
||||
margin-top:2px;
|
||||
}
|
||||
.bib-global-epr--inactive{
|
||||
opacity:0.55;
|
||||
background:#f1f3f5;
|
||||
|
||||
@ -1238,7 +1238,9 @@
|
||||
start: wrap.querySelector('.bib-global-free-start'),
|
||||
qty: wrap.querySelector('.bib-global-free-qty'),
|
||||
end: wrap.querySelector('.bib-global-free-end'),
|
||||
feedback: wrap.querySelector('.bib-global-free-range__feedback')
|
||||
feedback: wrap.querySelector('.bib-global-free-range__feedback'),
|
||||
qtyEl: wrap.querySelector('.bib-global-free-range__qty'),
|
||||
dupesEl: wrap.querySelector('.bib-global-free-range__dupes')
|
||||
};
|
||||
}
|
||||
|
||||
@ -1285,32 +1287,56 @@
|
||||
|| (('Quantité manquante : %d').replace('%d', String(Math.abs(diff))));
|
||||
}
|
||||
|
||||
function bibSetGlobalFreeDupesWarn(section, blnShow, strMessage) {
|
||||
var fields = bibGetGlobalBatchFreeRangeFields(section);
|
||||
if (!fields || !fields.dupesEl) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!blnShow) {
|
||||
fields.dupesEl.textContent = '';
|
||||
fields.dupesEl.classList.add('bib-ui-hidden');
|
||||
return;
|
||||
}
|
||||
|
||||
fields.dupesEl.textContent = 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');
|
||||
}
|
||||
|
||||
function bibUpdateGlobalFreeQtyFeedback(section) {
|
||||
var fields = bibGetGlobalBatchFreeRangeFields(section);
|
||||
if (!fields || !fields.feedback) {
|
||||
if (!fields) {
|
||||
return;
|
||||
}
|
||||
|
||||
var bounds = bibGetGlobalBatchFreeBounds(section);
|
||||
var pending = bibCollectGlobalBatchFreePending();
|
||||
var msg = bibFormatGlobalFreeQtyFeedback(bounds.qty, pending);
|
||||
fields.feedback.textContent = msg;
|
||||
fields.feedback.classList.remove(
|
||||
'bib-global-free-range__feedback--juste',
|
||||
'bib-global-free-range__feedback--manquante',
|
||||
'bib-global-free-range__feedback--supplementaire'
|
||||
);
|
||||
var qtyEl = fields.qtyEl || fields.feedback;
|
||||
|
||||
if (!msg) {
|
||||
return;
|
||||
if (qtyEl) {
|
||||
qtyEl.textContent = msg;
|
||||
qtyEl.classList.remove(
|
||||
'bib-global-free-range__feedback--juste',
|
||||
'bib-global-free-range__feedback--manquante',
|
||||
'bib-global-free-range__feedback--supplementaire'
|
||||
);
|
||||
|
||||
if (msg) {
|
||||
if (bounds.qty === pending) {
|
||||
qtyEl.classList.add('bib-global-free-range__feedback--juste');
|
||||
} else if (bounds.qty < pending) {
|
||||
qtyEl.classList.add('bib-global-free-range__feedback--manquante');
|
||||
} else {
|
||||
qtyEl.classList.add('bib-global-free-range__feedback--supplementaire');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bounds.qty === pending) {
|
||||
fields.feedback.classList.add('bib-global-free-range__feedback--juste');
|
||||
} else if (bounds.qty < pending) {
|
||||
fields.feedback.classList.add('bib-global-free-range__feedback--manquante');
|
||||
} else {
|
||||
fields.feedback.classList.add('bib-global-free-range__feedback--supplementaire');
|
||||
if (!msg) {
|
||||
bibSetGlobalFreeDupesWarn(section, false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1492,6 +1518,7 @@
|
||||
bibJs('jsGlobalNoEpr') || bibJs('jsBatchNone'),
|
||||
'error'
|
||||
);
|
||||
bibSetGlobalFreeDupesWarn(section, false);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
@ -1501,6 +1528,7 @@
|
||||
bibJs('jsGlobalFreePlanInvalid') || bibJs('jsFieldsRequired'),
|
||||
'error'
|
||||
);
|
||||
bibSetGlobalFreeDupesWarn(section, false);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
@ -1530,11 +1558,21 @@
|
||||
if (data.feedback && data.feedback.status === 'manquante') {
|
||||
state = 'warning';
|
||||
}
|
||||
if (data.duplicates && data.duplicates.has) {
|
||||
state = 'warning';
|
||||
}
|
||||
bibSetGlobalBatchInfo(infoEl, data.info || '', state);
|
||||
bibUpdateGlobalFreeQtyFeedback(section);
|
||||
bibSetGlobalFreeDupesWarn(
|
||||
section,
|
||||
!!(data.duplicates && data.duplicates.has),
|
||||
(data.duplicates && data.duplicates.message) || ''
|
||||
);
|
||||
return data;
|
||||
})
|
||||
.catch(function () {
|
||||
bibSetGlobalBatchInfo(infoEl, bibJs('jsErrGeneric'), 'error');
|
||||
bibSetGlobalFreeDupesWarn(section, false);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
@ -60,6 +60,73 @@ function fxBibFreeQtyFeedbackMessage(array $tabFeedback) {
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
function fxBibCountBibsUsedOutsideEpr($int_eve_id, $intStart, $intEnd, $intEprId) {
|
||||
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) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$sqlBib = fxBibSqlNoBibUnsigned('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) <> ''
|
||||
AND $sqlBib BETWEEN $intStart AND $intEnd
|
||||
");
|
||||
}
|
||||
|
||||
/**
|
||||
* MSIN-4445 — Risque de doublons pour le plan libre (plages virtuelles vs dossards déjà pris ailleurs).
|
||||
*
|
||||
* @return array{has:bool, count:int, message:string}
|
||||
*/
|
||||
function fxBibFreeDuplicateRisk($int_eve_id, $tabPlan) {
|
||||
$int_eve_id = (int)$int_eve_id;
|
||||
$intCount = 0;
|
||||
|
||||
if (!is_array($tabPlan)) {
|
||||
return ['has' => false, 'count' => 0, 'message' => ''];
|
||||
}
|
||||
|
||||
foreach ($tabPlan as $item) {
|
||||
if (!empty($item['skip'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$intCount += fxBibCountBibsUsedOutsideEpr(
|
||||
$int_eve_id,
|
||||
(int)($item['start'] ?? 0),
|
||||
(int)($item['end'] ?? 0),
|
||||
(int)($item['epr_id'] ?? 0)
|
||||
);
|
||||
}
|
||||
|
||||
if ($intCount <= 0) {
|
||||
return ['has' => false, 'count' => 0, 'message' => ''];
|
||||
}
|
||||
|
||||
return [
|
||||
'has' => true,
|
||||
'count' => $intCount,
|
||||
'message' => fxBibMsg('bib_v4_global_batch_free_dupes_warn'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Plan de plages virtuelles bornées par Début/Fin (sans écriture BD).
|
||||
* Enchaîne les épreuves comme le bloc généré, mais s'arrête à $intEnd.
|
||||
@ -181,7 +248,8 @@ function fxBibValidateFreeRangePlan($int_eve_id, $tabPlan, $intStart, $intEnd, $
|
||||
}
|
||||
|
||||
/** Stats agrégées + feedback quantité pour l'analyse libre. */
|
||||
function fxBibBuildGlobalBatchFreeAnalysis($tabPlan, $intStart, $intEnd) {
|
||||
function fxBibBuildGlobalBatchFreeAnalysis($int_eve_id, $tabPlan, $intStart, $intEnd) {
|
||||
$int_eve_id = (int)$int_eve_id;
|
||||
$intStart = (int)$intStart;
|
||||
$intEnd = (int)$intEnd;
|
||||
$intQty = ($intStart > 0 && $intEnd >= $intStart) ? ($intEnd - $intStart + 1) : 0;
|
||||
@ -194,11 +262,13 @@ function fxBibBuildGlobalBatchFreeAnalysis($tabPlan, $intStart, $intEnd) {
|
||||
}
|
||||
|
||||
$tabFeedback = fxBibFreeQtyFeedback($intQty, $intPendingAll);
|
||||
$tabDupes = fxBibFreeDuplicateRisk($int_eve_id, $tabPlan);
|
||||
|
||||
return [
|
||||
'stats' => $tabStats,
|
||||
'feedback' => $tabFeedback,
|
||||
'info' => fxBibMsg(
|
||||
'stats' => $tabStats,
|
||||
'feedback' => $tabFeedback,
|
||||
'duplicates' => $tabDupes,
|
||||
'info' => fxBibMsg(
|
||||
'bib_v4_ajax_global_free_analysis',
|
||||
$tabStats['nb_epreuves'],
|
||||
$intStart,
|
||||
@ -271,7 +341,7 @@ function fxBibSimulateGlobalBatchFree($int_eve_id, $tabPlan, $intStart, $intEnd,
|
||||
. '</div>';
|
||||
}
|
||||
|
||||
$tabAnalysis = fxBibBuildGlobalBatchFreeAnalysis($tabPlan, $intStart, $intEnd);
|
||||
$tabAnalysis = fxBibBuildGlobalBatchFreeAnalysis($int_eve_id, $tabPlan, $intStart, $intEnd);
|
||||
$info = fxBibMsg(
|
||||
'bib_v4_ajax_global_free_sim_summary',
|
||||
$tabAnalysis['stats']['nb_epreuves'],
|
||||
@ -316,12 +386,13 @@ function fxBibPrepareFreeRangePlans($int_eve_id, $tabPlan, $intStart, $intEnd, $
|
||||
];
|
||||
}
|
||||
|
||||
$tabAnalysis = fxBibBuildGlobalBatchFreeAnalysis($tabPlan, $intStart, $intEnd);
|
||||
$tabAnalysis = fxBibBuildGlobalBatchFreeAnalysis($int_eve_id, $tabPlan, $intStart, $intEnd);
|
||||
|
||||
return [
|
||||
'success' => true,
|
||||
'plans' => $tabPlans,
|
||||
'stats' => $tabAnalysis['stats'],
|
||||
'feedback' => $tabAnalysis['feedback'],
|
||||
'duplicates' => $tabAnalysis['duplicates'],
|
||||
];
|
||||
}
|
||||
|
||||
@ -3450,6 +3450,7 @@ function fxBibStaticFallback($clef) {
|
||||
'bib_v4_global_batch_free_qty_juste' => ['fr' => 'Quantité juste', 'en' => 'Exact quantity'],
|
||||
'bib_v4_global_batch_free_qty_manquante' => ['fr' => 'Quantité manquante : %d', 'en' => 'Missing quantity: %d'],
|
||||
'bib_v4_global_batch_free_qty_supplementaire' => ['fr' => 'Quantité supplémentaire : %d', 'en' => 'Extra quantity: %d'],
|
||||
'bib_v4_global_batch_free_dupes_warn' => ['fr' => 'Attention : vous êtes en train de créer des inscriptions avec des dossards en double.', 'en' => 'Warning: you are about to create registrations with duplicate bib numbers.'],
|
||||
'bib_v4_global_batch_free_plan_invalid' => ['fr' => 'Sélection invalide : Début, Fin (ou Qté) et au moins une épreuve avec inscriptions à assigner.', 'en' => 'Invalid selection: Start, End (or Qty) and at least one race with registrations to assign.'],
|
||||
'bib_v4_ajax_global_free_analysis' => ['fr' => '%d épreuve(s) · plage %d → %d · %d à assigner · %s', 'en' => '%d race(s) · range %d → %d · %d to assign · %s'],
|
||||
'bib_v4_ajax_global_free_sim_summary' => ['fr' => '%d épreuve(s) · %d dossards · %d à assigner · %s', 'en' => '%d race(s) · %d bibs · %d to assign · %s'],
|
||||
@ -3714,6 +3715,7 @@ function fxBibModuleJsDataAttrs() {
|
||||
'global-free-qty-juste' => 'bib_v4_global_batch_free_qty_juste',
|
||||
'global-free-qty-manquante' => 'bib_v4_global_batch_free_qty_manquante',
|
||||
'global-free-qty-supplementaire' => 'bib_v4_global_batch_free_qty_supplementaire',
|
||||
'global-free-dupes-warn' => 'bib_v4_global_batch_free_dupes_warn',
|
||||
'global-free-plan-invalid' => 'bib_v4_global_batch_free_plan_invalid',
|
||||
'reset-all-confirm' => 'bib_v4_js_reset_all_confirm',
|
||||
'reset-all-locked' => 'bib_v4_js_reset_all_locked',
|
||||
@ -5017,7 +5019,10 @@ function renderBibGlobalBatchPanelFree($int_eve_id, $tabEpreuves, $strLangue) {
|
||||
inputmode="numeric"
|
||||
disabled>
|
||||
</div>
|
||||
<p class="bib-global-free-range__feedback text-muted small mb-0" aria-live="polite"></p>
|
||||
<div class="bib-global-free-range__feedback" aria-live="polite">
|
||||
<p class="bib-global-free-range__qty mb-0"></p>
|
||||
<p class="bib-global-free-range__dupes mb-0 bib-ui-hidden"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bib-global-batch__epreuves">
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
* Constantes *
|
||||
*
|
||||
**************/
|
||||
define('_VERSION_CODE', '4.72.828');
|
||||
define('_VERSION_CODE', '4.72.829');
|
||||
define('_DATE_CODE', '2026-07-16');
|
||||
//MSIN-4290
|
||||
define('QR_SECRET_KEY', 'ms1_qr_2026_cle_secrete_longue_et_fixe');
|
||||
|
||||
@ -65,3 +65,12 @@ FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM info WHERE info_clef = 'bib_v4_ajax_gl
|
||||
INSERT INTO info (info_clef, info_langue, info_texte, info_aide, info_prg, info_description, info_trie, info_actif, info_option1, info_option2, info_option3, info_creation)
|
||||
SELECT 'bib_v4_ajax_global_free_sim_summary', 'en', '%d race(s) · %d bibs · %d to assign · %s', '', 'compte.php', '', 0, 1, '', '', '', NOW()
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM info WHERE info_clef = 'bib_v4_ajax_global_free_sim_summary' AND info_langue = 'en' AND info_prg = 'compte.php');
|
||||
|
||||
-- MSIN-4445 — Avertissement doublons (plage libre chevauchant des dossards déjà assignés ailleurs)
|
||||
INSERT INTO info (info_clef, info_langue, info_texte, info_aide, info_prg, info_description, info_trie, info_actif, info_option1, info_option2, info_option3, info_creation)
|
||||
SELECT 'bib_v4_global_batch_free_dupes_warn', 'fr', 'Attention : vous êtes en train de créer des inscriptions avec des dossards en double.', 'Affiché lorsque la plage libre chevauche des dossards déjà assignés sur une autre épreuve.', 'compte.php', '', 0, 1, '', '', '', NOW()
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM info WHERE info_clef = 'bib_v4_global_batch_free_dupes_warn' AND info_langue = 'fr' AND info_prg = 'compte.php');
|
||||
|
||||
INSERT INTO info (info_clef, info_langue, info_texte, info_aide, info_prg, info_description, info_trie, info_actif, info_option1, info_option2, info_option3, info_creation)
|
||||
SELECT 'bib_v4_global_batch_free_dupes_warn', 'en', 'Warning: you are about to create registrations with duplicate bib numbers.', 'Shown when the free range overlaps bibs already assigned on another race.', 'compte.php', '', 0, 1, '', '', '', NOW()
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM info WHERE info_clef = 'bib_v4_global_batch_free_dupes_warn' AND info_langue = 'en' AND info_prg = 'compte.php');
|
||||
|
||||
Reference in New Issue
Block a user