MSIN-4424 Implement inter-epreuve duplicate handling and update version code to 4.72.750
This commit introduces functionality to allow the same bib numbers across different epreuves, enhancing the flexibility of event management. A new AJAX action, `save_allow_inter_epr`, is added to manage this setting, along with corresponding JavaScript to handle user interactions. The database functions are updated to support this feature, ensuring proper validation and anomaly reporting. The version code is incremented to reflect these changes.
This commit is contained in:
@ -35,6 +35,7 @@ $arrBibAjaxActions = [
|
||||
'reset_preview',
|
||||
'reset_apply',
|
||||
'reset_all_apply',
|
||||
'save_allow_inter_epr',
|
||||
'save_team_mode',
|
||||
'toggle_lock',
|
||||
'save_auto_config',
|
||||
@ -257,7 +258,7 @@ if ($action == 'save') {
|
||||
// =====================
|
||||
// VALIDATION 4 — OVERLAP pas de double epreuves
|
||||
// =====================
|
||||
$tabOverlaps = fxBibGetOverlappingRanges($eve_id, $start, $end, $is_new ? 0 : $id);
|
||||
$tabOverlaps = fxBibGetOverlappingRanges($eve_id, $start, $end, $is_new ? 0 : $id, $epr_id);
|
||||
|
||||
if (!empty($tabOverlaps)) {
|
||||
echo json_encode([
|
||||
@ -356,6 +357,41 @@ if ($action == 'batch_analysis') {
|
||||
exit;
|
||||
}
|
||||
|
||||
// =====================
|
||||
// MSIN-4424 — Réglage doublons inter-épreuves (événement)
|
||||
// =====================
|
||||
if ($action == 'save_allow_inter_epr') {
|
||||
|
||||
$int_eve_id = (int)($_POST['eve_id'] ?? 0);
|
||||
$blnAllow = !empty($_POST['allow']) && (string)$_POST['allow'] !== '0';
|
||||
|
||||
if ($int_eve_id <= 0) {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => fxBibMsg('bib_v4_ajax_epr_invalid'),
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$result = fxBibSetEventAllowsInterEprDuplicates($int_eve_id, $blnAllow);
|
||||
|
||||
if (empty($result['success'])) {
|
||||
echo json_encode($result);
|
||||
exit;
|
||||
}
|
||||
|
||||
$strLangueAnom = $_SESSION['lang'] ?? 'fr';
|
||||
$tabAnomalies = fxBibCollectAnomalies($int_eve_id, $strLangueAnom);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'allow_inter_epr' => !empty($result['allow_inter_epr']) ? 1 : 0,
|
||||
'anomalies_html' => renderBibAnomaliesPanel($int_eve_id, $strLangueAnom, $tabAnomalies),
|
||||
'anomaly_count' => fxBibAnomalyTotalCount($tabAnomalies),
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// MSIN-4436 — Panneau assignation globale (chargement différé à l'ouverture).
|
||||
if ($action == 'global_batch_panel') {
|
||||
|
||||
|
||||
@ -147,7 +147,11 @@ switch ($strAction) {
|
||||
. ' WHERE p.par_id = ' . $intParId . ' AND p.pec_id = e.pec_id_original LIMIT 1'
|
||||
));
|
||||
|
||||
$arrDup = fxInscrGestionFindBibDuplicates($intEveId, $strNoBib, $intPecOrig, $blnTeam);
|
||||
$intCurrentEprId = (int)$objDatabase->fxGetVar(
|
||||
'SELECT epr_id FROM resultats_participants WHERE par_id = ' . $intParId . ' LIMIT 1'
|
||||
);
|
||||
|
||||
$arrDup = fxInscrGestionFindBibDuplicates($intEveId, $strNoBib, $intPecOrig, $blnTeam, $intCurrentEprId);
|
||||
$arrWho = array();
|
||||
foreach ($arrDup as $rowDup) {
|
||||
$arrWho[] = array(
|
||||
|
||||
@ -1064,6 +1064,39 @@ ul.ms1-menu-compte li a:hover, ul.ms1-menu-compte li a:active {
|
||||
line-height:1.45;
|
||||
}
|
||||
|
||||
/* MSIN-4424 — Bandeau réglages dossards (haut du module). */
|
||||
.bib-event-settings{
|
||||
border:1px solid #c8d6e5;
|
||||
border-radius:6px;
|
||||
background:#f8fafc;
|
||||
padding:10px 14px;
|
||||
margin-bottom:4px;
|
||||
}
|
||||
|
||||
.bib-event-settings__label{
|
||||
display:flex;
|
||||
align-items:flex-start;
|
||||
gap:8px;
|
||||
margin:0;
|
||||
font-weight:600;
|
||||
font-size:14px;
|
||||
line-height:1.4;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
.bib-event-settings__label input[type="checkbox"]{
|
||||
margin-top:3px;
|
||||
flex-shrink:0;
|
||||
}
|
||||
|
||||
.bib-event-settings__text{
|
||||
flex:1 1 auto;
|
||||
}
|
||||
|
||||
.bib-anomaly-block--inter-epr-dupes .bib-anomaly-block-title{
|
||||
color:#8a3b12;
|
||||
}
|
||||
|
||||
/* MSIN-4436 — Assignation globale (épreuves solo) */
|
||||
.bib-global-batch{
|
||||
border:1px solid #c8d6e5;
|
||||
|
||||
@ -2711,6 +2711,49 @@
|
||||
});
|
||||
});
|
||||
|
||||
// MSIN-4424 — Autoriser mêmes numéros entre épreuves (réglage événement).
|
||||
let chkAllowInterEpr = moduleBib.querySelector('.bib-event-allow-inter-epr');
|
||||
if (chkAllowInterEpr) {
|
||||
chkAllowInterEpr.addEventListener('change', function () {
|
||||
let eveId = moduleBib.dataset.eveId || '';
|
||||
if (!eveId) {
|
||||
return;
|
||||
}
|
||||
|
||||
bibFetch('/ajax_bib_range.php', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
},
|
||||
body:
|
||||
'action=save_allow_inter_epr'
|
||||
+ '&eve_id=' + encodeURIComponent(eveId)
|
||||
+ '&allow=' + (chkAllowInterEpr.checked ? '1' : '0')
|
||||
+ bibAjaxLangSuffix()
|
||||
})
|
||||
.then(function (res) { return res.json(); })
|
||||
.then(function (data) {
|
||||
if (!data.success) {
|
||||
chkAllowInterEpr.checked = !chkAllowInterEpr.checked;
|
||||
alert(data.message || bibJs('jsErrGeneric'));
|
||||
return;
|
||||
}
|
||||
|
||||
moduleBib.dataset.allowInterEpr = data.allow_inter_epr ? '1' : '0';
|
||||
|
||||
let mount = document.getElementById('bib-anomalies-mount');
|
||||
if (mount && data.anomalies_html) {
|
||||
mount.innerHTML = data.anomalies_html;
|
||||
initModuleBibTippy(mount);
|
||||
}
|
||||
})
|
||||
.catch(function () {
|
||||
chkAllowInterEpr.checked = !chkAllowInterEpr.checked;
|
||||
alert(bibJs('jsErrGeneric'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// MSIN-4436 — Anomalies : coquille légère au chargement, détail en AJAX.
|
||||
if (document.querySelector('#bib-anomalies-panel[data-bib-anomalies-deferred="1"]')) {
|
||||
refreshBibAnomaliesPanel();
|
||||
|
||||
@ -619,10 +619,12 @@ function fxInscrGestionBibDuplicatePolicy($intEveId) {
|
||||
* @param bool $blnTeam true = dossard d'equipe (e.no_equipe), false = individuel (p.no_bib)
|
||||
* @return array liste de lignes ['par_nom','par_prenom','epr_id']
|
||||
*/
|
||||
function fxInscrGestionFindBibDuplicates($intEveId, $strNoBib, $intExceptPec, $blnTeam) {
|
||||
function fxInscrGestionFindBibDuplicates($intEveId, $strNoBib, $intExceptPec, $blnTeam, $intCurrentEprId = 0) {
|
||||
global $objDatabase;
|
||||
|
||||
$strNoBib = trim((string)$strNoBib);
|
||||
$intCurrentEprId = (int)$intCurrentEprId;
|
||||
|
||||
if (intval($intEveId) <= 0 || $strNoBib === '') {
|
||||
return array();
|
||||
}
|
||||
@ -639,7 +641,25 @@ function fxInscrGestionFindBibDuplicates($intEveId, $strNoBib, $intExceptPec, $b
|
||||
. ' ORDER BY p.par_nom, p.par_prenom';
|
||||
|
||||
$tab = $objDatabase->fxGetResults($sql);
|
||||
return is_array($tab) ? $tab : array();
|
||||
if (!is_array($tab) || empty($tab)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$blnAllowInter = function_exists('fxBibEventAllowsInterEprDuplicates')
|
||||
&& fxBibEventAllowsInterEprDuplicates($intEveId);
|
||||
|
||||
$arrOut = array();
|
||||
foreach ($tab as $rowDup) {
|
||||
$intDupEprId = (int)($rowDup['epr_id'] ?? 0);
|
||||
|
||||
if ($intCurrentEprId > 0 && $intDupEprId !== $intCurrentEprId && $blnAllowInter) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$arrOut[] = $rowDup;
|
||||
}
|
||||
|
||||
return $arrOut;
|
||||
}
|
||||
|
||||
function fxInscrGestionEpreuveLabel($intEprId, $strLangue) {
|
||||
|
||||
@ -1539,8 +1539,27 @@ function fxShowDupes($int_eve_id) {
|
||||
$strPage = '/account';
|
||||
}
|
||||
|
||||
$sqlNoBib = "SELECT no_bib, epr_id, COUNT(*) c FROM resultats_participants where eve_id = " . intval($int_eve_id) . " AND no_bib <> '' GROUP BY no_bib HAVING c > 1";
|
||||
$tabNoBib = $objDatabase->fxGetResults($sqlNoBib);
|
||||
$sqlNoBib = "SELECT no_bib, epr_id, COUNT(*) c FROM resultats_participants where eve_id = " . intval($int_eve_id) . " AND no_bib <> '' AND is_cancelled = 0 GROUP BY no_bib, epr_id HAVING c > 1";
|
||||
$tabIntraDupes = $objDatabase->fxGetResults($sqlNoBib);
|
||||
|
||||
$tabInterDupes = [];
|
||||
if (!fxBibEventAllowsInterEprDuplicates((int)$int_eve_id)) {
|
||||
$sqlInter = "SELECT no_bib FROM resultats_participants WHERE eve_id = " . intval($int_eve_id)
|
||||
. " AND no_bib <> '' AND is_cancelled = 0 GROUP BY no_bib HAVING COUNT(DISTINCT epr_id) > 1";
|
||||
$tabInterDupes = $objDatabase->fxGetResults($sqlInter);
|
||||
}
|
||||
|
||||
$tabBibKeys = [];
|
||||
if (is_array($tabIntraDupes)) {
|
||||
foreach ($tabIntraDupes as $row) {
|
||||
$tabBibKeys[(int)$row['no_bib']] = true;
|
||||
}
|
||||
}
|
||||
if (is_array($tabInterDupes)) {
|
||||
foreach ($tabInterDupes as $row) {
|
||||
$tabBibKeys[(int)$row['no_bib']] = true;
|
||||
}
|
||||
}
|
||||
?>
|
||||
<div class="box_participant card mb-3">
|
||||
|
||||
@ -1559,11 +1578,11 @@ function fxShowDupes($int_eve_id) {
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php
|
||||
if ($tabNoBib != null) {
|
||||
if (!empty($tabBibKeys)) {
|
||||
$intCtr = 1;
|
||||
|
||||
foreach ($tabNoBib as $tab) {
|
||||
$sqlDupes = "select par_prenom, par_nom, epr_id from resultats_participants where eve_id = " . intval($int_eve_id) . " and no_bib = " . intval($tab['no_bib']);
|
||||
foreach (array_keys($tabBibKeys) as $intNoBib) {
|
||||
$sqlDupes = "select par_prenom, par_nom, epr_id from resultats_participants where eve_id = " . intval($int_eve_id) . " and is_cancelled = 0 and no_bib = " . intval($intNoBib);
|
||||
$tabDupes = $objDatabase->fxGetResults($sqlDupes);
|
||||
|
||||
if ($tabDupes != null) {
|
||||
@ -1571,7 +1590,7 @@ function fxShowDupes($int_eve_id) {
|
||||
$tabEpreuve = fxGetEpreuve($dupe['epr_id']);
|
||||
?>
|
||||
<tr>
|
||||
<td><?php echo $tab['no_bib']; ?></td>
|
||||
<td><?php echo (int)$intNoBib; ?></td>
|
||||
<td><?php echo $dupe['par_prenom'] . " " . $dupe['par_nom']; ?></td>
|
||||
<td>
|
||||
<?php
|
||||
@ -2771,6 +2790,50 @@ function fxBibAssertEpreuveNoLockedRanges($epr_id) {
|
||||
return ['success' => true];
|
||||
}
|
||||
|
||||
/** MSIN-4424 — Mêmes numéros de dossard autorisés entre épreuves (réglage événement). */
|
||||
function fxBibEventAllowsInterEprDuplicates($int_eve_id) {
|
||||
global $objDatabase;
|
||||
|
||||
static $memCache = [];
|
||||
|
||||
$int_eve_id = (int)$int_eve_id;
|
||||
|
||||
if ($int_eve_id <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (array_key_exists($int_eve_id, $memCache)) {
|
||||
return $memCache[$int_eve_id];
|
||||
}
|
||||
|
||||
$intVal = (int)$objDatabase->fxGetVar(
|
||||
'SELECT eve_bib_allow_inter_epr FROM inscriptions_evenements WHERE eve_id = ' . $int_eve_id . ' LIMIT 1'
|
||||
);
|
||||
|
||||
$memCache[$int_eve_id] = ($intVal === 1);
|
||||
|
||||
return $memCache[$int_eve_id];
|
||||
}
|
||||
|
||||
/** MSIN-4424 — Persiste le réglage doublons inter-épreuves. */
|
||||
function fxBibSetEventAllowsInterEprDuplicates($int_eve_id, $blnAllow) {
|
||||
global $objDatabase;
|
||||
|
||||
$int_eve_id = (int)$int_eve_id;
|
||||
$intAllow = $blnAllow ? 1 : 0;
|
||||
|
||||
if ($int_eve_id <= 0) {
|
||||
return ['success' => false, 'message' => fxBibMsg('bib_v4_ajax_epr_invalid')];
|
||||
}
|
||||
|
||||
$objDatabase->fxQuery(
|
||||
'UPDATE inscriptions_evenements SET eve_bib_allow_inter_epr = ' . $intAllow
|
||||
. ' WHERE eve_id = ' . $int_eve_id . ' LIMIT 1'
|
||||
);
|
||||
|
||||
return ['success' => true, 'allow_inter_epr' => ($intAllow === 1)];
|
||||
}
|
||||
|
||||
/** Resout eve_id depuis la requete AJAX bib (eve_id direct ou via epr_id). */
|
||||
function fxBibResolveEveIdFromRequest() {
|
||||
global $objDatabase;
|
||||
@ -3034,22 +3097,30 @@ function fxBibEpreuveDisplayName(array $row, $strLangue = 'fr') {
|
||||
}
|
||||
|
||||
/**
|
||||
* Séquences de dossards qui chevauchent [start, end] sur le même événement.
|
||||
* MSIN-4424 — Séquences qui chevauchent [start, end].
|
||||
* Par défaut : tout l'événement. Si doublons inter-épreuves autorisés : même epr_id seulement.
|
||||
*
|
||||
* @return array|null Tableau 1-based fxGetResults, ou null si paramètres invalides.
|
||||
*/
|
||||
function fxBibGetOverlappingRanges($eve_id, $start, $end, $exclude_bib_id = 0) {
|
||||
function fxBibGetOverlappingRanges($eve_id, $start, $end, $exclude_bib_id = 0, $epr_id = 0) {
|
||||
global $objDatabase;
|
||||
|
||||
$eve_id = (int)$eve_id;
|
||||
$start = (int)$start;
|
||||
$end = (int)$end;
|
||||
$exclude_bib_id = (int)$exclude_bib_id;
|
||||
$epr_id = (int)$epr_id;
|
||||
|
||||
if ($eve_id <= 0 || $start <= 0 || $end <= 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$sqlExclude = ($exclude_bib_id > 0) ? "AND b.epr_bib_id <> $exclude_bib_id" : '';
|
||||
$sqlEprOnly = '';
|
||||
|
||||
if ($epr_id > 0 && fxBibEventAllowsInterEprDuplicates($eve_id)) {
|
||||
$sqlEprOnly = "AND b.epr_id = $epr_id";
|
||||
}
|
||||
|
||||
$sql = "
|
||||
SELECT
|
||||
@ -3070,6 +3141,7 @@ function fxBibGetOverlappingRanges($eve_id, $start, $end, $exclude_bib_id = 0) {
|
||||
AND b.epr_bib_start IS NOT NULL
|
||||
AND b.epr_bib_finish IS NOT NULL
|
||||
$sqlExclude
|
||||
$sqlEprOnly
|
||||
AND ($start <= b.epr_bib_finish AND $end >= b.epr_bib_start)
|
||||
ORDER BY b.epr_bib_start, b.epr_bib_id
|
||||
";
|
||||
@ -4152,7 +4224,9 @@ function fxShowBibTool4($str_code, $int_eve_id, $strLangue){
|
||||
?>
|
||||
</div>
|
||||
|
||||
<div id="module-bib" class="epr-wrapper" data-csrf-token="<?php echo fxBibEsc(fxBibCsrfToken()); ?>" data-lang="<?php echo fxBibEsc($strLangue); ?>" data-eve-id="<?php echo (int)$int_eve_id; ?>"<?php echo fxBibModuleJsDataAttrs(); ?><?php echo fxAdminTextEditModeActive() ? ' data-bib-trad="1"' : ''; ?>>
|
||||
<div id="module-bib" class="epr-wrapper" data-csrf-token="<?php echo fxBibEsc(fxBibCsrfToken()); ?>" data-lang="<?php echo fxBibEsc($strLangue); ?>" data-eve-id="<?php echo (int)$int_eve_id; ?>" data-allow-inter-epr="<?php echo fxBibEventAllowsInterEprDuplicates((int)$int_eve_id) ? '1' : '0'; ?>"<?php echo fxBibModuleJsDataAttrs(); ?><?php echo fxAdminTextEditModeActive() ? ' data-bib-trad="1"' : ''; ?>>
|
||||
|
||||
<?php echo renderBibEventSettingsBanner((int)$int_eve_id, $strLangue); ?>
|
||||
|
||||
<div id="bib-anomalies-mount">
|
||||
<?php echo renderBibAnomaliesPanelShell((int)$int_eve_id, $strLangue); ?>
|
||||
@ -4574,6 +4648,101 @@ function fxBibAnomalyGroupDuplicateRows(array $rows, $strLangue = 'fr') {
|
||||
return array_values($grouped);
|
||||
}
|
||||
|
||||
/**
|
||||
* MSIN-4424 — Dossards en double entre épreuves (même no_bib, epr_id distincts).
|
||||
* Vide si le réglage événement autorise les doublons inter-épreuves.
|
||||
*
|
||||
* @return array<int, array<string, mixed>>
|
||||
*/
|
||||
function fxBibAnomalyInterEprDuplicateBibs($int_eve_id, $strLangue = 'fr') {
|
||||
global $objDatabase;
|
||||
|
||||
$int_eve_id = (int)$int_eve_id;
|
||||
|
||||
if ($int_eve_id <= 0 || fxBibEventAllowsInterEprDuplicates($int_eve_id)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$sqlBibNumP = fxBibSqlNoBibUnsigned('p.no_bib');
|
||||
$sqlBibNumP2 = fxBibSqlNoBibUnsigned('p2.no_bib');
|
||||
|
||||
$sql = "
|
||||
SELECT
|
||||
$sqlBibNumP AS no_bib,
|
||||
p.epr_id,
|
||||
p.par_id,
|
||||
p.par_prenom,
|
||||
p.par_nom,
|
||||
e.epr_nom_fr,
|
||||
e.epr_nom_en,
|
||||
e.epr_type_fr,
|
||||
e.epr_type_en,
|
||||
e.epr_categorie_fr,
|
||||
e.epr_categorie_en,
|
||||
e.epr_actif
|
||||
FROM resultats_participants p
|
||||
INNER JOIN inscriptions_epreuves e ON e.epr_id = p.epr_id
|
||||
WHERE e.eve_id = $int_eve_id
|
||||
AND p.is_cancelled = 0
|
||||
AND $sqlBibNumP > 0
|
||||
AND $sqlBibNumP IN (
|
||||
SELECT $sqlBibNumP2
|
||||
FROM resultats_participants p2
|
||||
INNER JOIN inscriptions_epreuves e2 ON e2.epr_id = p2.epr_id
|
||||
WHERE e2.eve_id = $int_eve_id
|
||||
AND p2.is_cancelled = 0
|
||||
AND $sqlBibNumP2 > 0
|
||||
GROUP BY $sqlBibNumP2
|
||||
HAVING COUNT(DISTINCT p2.epr_id) > 1
|
||||
)
|
||||
ORDER BY $sqlBibNumP, p.epr_id, p.par_nom, p.par_prenom
|
||||
";
|
||||
|
||||
$rows = $objDatabase->fxGetResults($sql);
|
||||
|
||||
if (!is_array($rows) || empty($rows)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$grouped = [];
|
||||
|
||||
foreach ($rows as $row) {
|
||||
$intBib = (int)$row['no_bib'];
|
||||
$key = (string)$intBib;
|
||||
|
||||
if (!isset($grouped[$key])) {
|
||||
$grouped[$key] = [
|
||||
'no_bib' => $intBib,
|
||||
'entries' => [],
|
||||
];
|
||||
}
|
||||
|
||||
$intEprId = (int)$row['epr_id'];
|
||||
$strEprKey = (string)$intEprId;
|
||||
|
||||
if (!isset($grouped[$key]['entries'][$strEprKey])) {
|
||||
$grouped[$key]['entries'][$strEprKey] = [
|
||||
'epr_id' => $intEprId,
|
||||
'epr_label' => fxBibAnomalyEprLabel($row, $strLangue),
|
||||
'participants' => [],
|
||||
];
|
||||
}
|
||||
|
||||
$grouped[$key]['entries'][$strEprKey]['participants'][] = [
|
||||
'par_id' => (int)$row['par_id'],
|
||||
'par_prenom' => trim($row['par_prenom'] ?? ''),
|
||||
'par_nom' => trim($row['par_nom'] ?? ''),
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($grouped as &$item) {
|
||||
$item['entries'] = array_values($item['entries']);
|
||||
}
|
||||
unset($item);
|
||||
|
||||
return array_values($grouped);
|
||||
}
|
||||
|
||||
/**
|
||||
* MSIN-4379 — Dossards en double (même epr_id, même no_bib, >1 unité active).
|
||||
* Couvre les séquences configurées et les épreuves sans séquence (saisie manuelle).
|
||||
@ -4925,16 +5094,17 @@ function fxBibAnomalyOutOfRangeBibs($int_eve_id, $strLangue = 'fr') {
|
||||
/** MSIN-4379 — Agrège toutes les anomalies dossards pour un événement. */
|
||||
function fxBibCollectAnomalies($int_eve_id, $strLangue = 'fr') {
|
||||
return [
|
||||
'dupes' => fxBibAnomalyDuplicateBibs($int_eve_id, $strLangue),
|
||||
'out_of_range' => fxBibAnomalyOutOfRangeBibs($int_eve_id, $strLangue),
|
||||
'orphans' => fxBibAnomalyOrphanBibs($int_eve_id, $strLangue),
|
||||
'auto_miss' => fxBibAnomalyAutoMissBibs($int_eve_id, $strLangue),
|
||||
'dupes' => fxBibAnomalyDuplicateBibs($int_eve_id, $strLangue),
|
||||
'inter_epr_dupes' => fxBibAnomalyInterEprDuplicateBibs($int_eve_id, $strLangue),
|
||||
'out_of_range' => fxBibAnomalyOutOfRangeBibs($int_eve_id, $strLangue),
|
||||
'orphans' => fxBibAnomalyOrphanBibs($int_eve_id, $strLangue),
|
||||
'auto_miss' => fxBibAnomalyAutoMissBibs($int_eve_id, $strLangue),
|
||||
];
|
||||
}
|
||||
|
||||
function fxBibAnomalyTotalCount(array $tabAnomalies) {
|
||||
$intTotal = 0;
|
||||
foreach (['dupes', 'out_of_range', 'orphans', 'auto_miss'] as $strKey) {
|
||||
foreach (['dupes', 'inter_epr_dupes', 'out_of_range', 'orphans', 'auto_miss'] as $strKey) {
|
||||
if (!empty($tabAnomalies[$strKey]) && is_array($tabAnomalies[$strKey])) {
|
||||
$intTotal += count($tabAnomalies[$strKey]);
|
||||
}
|
||||
@ -4942,6 +5112,27 @@ function fxBibAnomalyTotalCount(array $tabAnomalies) {
|
||||
return $intTotal;
|
||||
}
|
||||
|
||||
/** MSIN-4424 — Bandeau réglages dossards (haut du module, au-dessus des épreuves). */
|
||||
function renderBibEventSettingsBanner($int_eve_id, $strLangue = 'fr') {
|
||||
$int_eve_id = (int)$int_eve_id;
|
||||
$blnAllow = fxBibEventAllowsInterEprDuplicates($int_eve_id);
|
||||
|
||||
ob_start();
|
||||
?>
|
||||
<div class="bib-event-settings" data-eve-id="<?php echo $int_eve_id; ?>">
|
||||
<label class="bib-event-settings__label">
|
||||
<input type="checkbox"
|
||||
class="bib-event-allow-inter-epr"
|
||||
value="1"
|
||||
<?php echo $blnAllow ? ' checked' : ''; ?>>
|
||||
<span class="bib-event-settings__text"><?php fxBibTexte('bib_v4_allow_inter_epr', 1); ?></span>
|
||||
<?php echo fxBibAideButton('bib_v4_allow_inter_epr', '', false); ?>
|
||||
</label>
|
||||
</div>
|
||||
<?php
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* MSIN-4436 — Coquille anomalies au chargement page (détail via AJAX).
|
||||
* Évite 4 requêtes lourdes avant le premier affichage des épreuves.
|
||||
@ -5054,6 +5245,35 @@ function renderBibAnomaliesPanel($int_eve_id, $strLangue = 'fr', $tabAnomalies =
|
||||
</section>
|
||||
<?php } ?>
|
||||
|
||||
<?php if (!empty($tabAnomalies['inter_epr_dupes'])) { ?>
|
||||
<section class="bib-anomaly-block bib-anomaly-block--inter-epr-dupes">
|
||||
<h3 class="bib-anomaly-block-title">
|
||||
<?php fxBibTexte('bib_v4_anomalies_inter_epr_dupes_title', 1); ?>
|
||||
<?php echo fxBibAideButton('bib_v4_anomalies_inter_epr_dupes_title'); ?>
|
||||
<span class="badge badge-secondary"><?php echo count($tabAnomalies['inter_epr_dupes']); ?></span>
|
||||
</h3>
|
||||
<ul class="bib-anomaly-list list-unstyled mb-0">
|
||||
<?php foreach ($tabAnomalies['inter_epr_dupes'] as $item) { ?>
|
||||
<li class="bib-anomaly-item">
|
||||
<?php echo fxBibMsgTrad('bib_v4_anomalies_bib_fmt', (int)$item['no_bib']); ?>
|
||||
<ul class="bib-anomaly-sublist list-unstyled mb-0">
|
||||
<?php foreach ($item['entries'] as $entry) { ?>
|
||||
<li>
|
||||
<strong><?php echo fxBibEsc($entry['epr_label']); ?></strong>
|
||||
<ul class="bib-anomaly-sublist list-unstyled mb-0">
|
||||
<?php foreach ($entry['participants'] as $p) { ?>
|
||||
<li><?php echo fxBibEsc(trim($p['par_prenom'] . ' ' . $p['par_nom'])); ?></li>
|
||||
<?php } ?>
|
||||
</ul>
|
||||
</li>
|
||||
<?php } ?>
|
||||
</ul>
|
||||
</li>
|
||||
<?php } ?>
|
||||
</ul>
|
||||
</section>
|
||||
<?php } ?>
|
||||
|
||||
<?php if (!empty($tabAnomalies['out_of_range'])) { ?>
|
||||
<section class="bib-anomaly-block bib-anomaly-block--out-of-range">
|
||||
<h3 class="bib-anomaly-block-title">
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
* Constantes *
|
||||
*
|
||||
**************/
|
||||
define('_VERSION_CODE', '4.72.749');
|
||||
define('_VERSION_CODE', '4.72.750');
|
||||
define('_DATE_CODE', '2026-07-08');
|
||||
//MSIN-4290
|
||||
define('QR_SECRET_KEY', 'ms1_qr_2026_cle_secrete_longue_et_fixe');
|
||||
|
||||
38
sql/MSIN-4424-bib-allow-inter-epr.sql
Normal file
38
sql/MSIN-4424-bib-allow-inter-epr.sql
Normal file
@ -0,0 +1,38 @@
|
||||
-- MSIN-4424 — Autoriser les mêmes numéros de dossard entre épreuves (réglage événement)
|
||||
-- Idempotent. Exécuter avant déploiement PHP/JS.
|
||||
|
||||
SET @db := DATABASE();
|
||||
|
||||
SET @col_exists := (
|
||||
SELECT COUNT(*)
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = @db
|
||||
AND TABLE_NAME = 'inscriptions_evenements'
|
||||
AND COLUMN_NAME = 'eve_bib_allow_inter_epr'
|
||||
);
|
||||
|
||||
SET @sql_col := IF(
|
||||
@col_exists = 0,
|
||||
'ALTER TABLE inscriptions_evenements ADD COLUMN eve_bib_allow_inter_epr TINYINT(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT ''MSIN-4424 — 1 = mêmes no_bib autorisés entre épreuves'' AFTER eve_epreuve_modifiable',
|
||||
'SELECT ''eve_bib_allow_inter_epr already exists'' AS note'
|
||||
);
|
||||
|
||||
PREPARE stmt_col FROM @sql_col;
|
||||
EXECUTE stmt_col;
|
||||
DEALLOCATE PREPARE stmt_col;
|
||||
|
||||
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_allow_inter_epr', 'fr', 'Autoriser les mêmes numéros de dossard entre épreuves', 'Permet d''utiliser les mêmes numéros sur plusieurs épreuves (ex. 5 km et 10 km). Les doublons à l''intérieur d''une même épreuve restent interdits.', 'compte.php', '', 0, 1, '', '', '', NOW()
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM info WHERE info_clef = 'bib_v4_allow_inter_epr' 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_allow_inter_epr', 'en', 'Allow the same bib numbers across races', 'Lets multiple races share the same bib numbers (e.g. 5K and 10K). Duplicates within a single race remain forbidden.', 'compte.php', '', 0, 1, '', '', '', NOW()
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM info WHERE info_clef = 'bib_v4_allow_inter_epr' AND info_langue = 'en' 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_anomalies_inter_epr_dupes_title', 'fr', 'Dossards en double entre épreuves', 'Même numéro de dossard utilisé sur plus d''une épreuve alors que l''option n''est pas activée.', 'compte.php', '', 0, 1, '', '', '', NOW()
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM info WHERE info_clef = 'bib_v4_anomalies_inter_epr_dupes_title' 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_anomalies_inter_epr_dupes_title', 'en', 'Duplicate bibs across races', 'Same bib number used on more than one race while the option is disabled.', 'compte.php', '', 0, 1, '', '', '', NOW()
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM info WHERE info_clef = 'bib_v4_anomalies_inter_epr_dupes_title' AND info_langue = 'en' AND info_prg = 'compte.php');
|
||||
Reference in New Issue
Block a user