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:
2026-07-08 10:17:15 -04:00
parent a7e027177c
commit 70f4dac177
8 changed files with 413 additions and 19 deletions

View File

@ -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') {

View File

@ -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(

View File

@ -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;

View File

@ -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();

View File

@ -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) {

View File

@ -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'] . "&nbsp;" . $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">

View File

@ -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');

View 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');