This commit introduces a new function, fxBibGetOverlappingRanges, to check for overlapping bib sequences within a specified event. The validation logic in ajax_bib_range.php is updated to utilize this function, enhancing the accuracy of overlap detection. Additionally, detailed error messages are formatted for both single and multiple overlaps, improving user feedback in the interface. SQL scripts are updated to include new localized messages for overlap conflicts in both French and English. No version code change is included in this update.
675 lines
16 KiB
PHP
675 lines
16 KiB
PHP
<?php
|
||
session_start();
|
||
|
||
// MSIN-4379 — Traductions toujours au nom de compte.php (jamais ajax_bib_range.php).
|
||
define('MS1_BIB_AJAX', true);
|
||
$_SERVER['PHP_SELF'] = '/compte.php';
|
||
$_SERVER['SCRIPT_NAME'] = '/compte.php';
|
||
|
||
require_once('php/inc_fonctions.php');
|
||
require_once('php/inc_fx_promoteur.php');
|
||
|
||
$strLangue = $_SESSION['lang'] ?? 'fr';
|
||
$reqLang = $_REQUEST['lang'] ?? $_REQUEST['lng'] ?? '';
|
||
if (!empty($reqLang) && in_array($reqLang, ['fr', 'en'], true)) {
|
||
$strLangue = $reqLang;
|
||
}
|
||
$vPage = 'compte.php';
|
||
$vtexte_page = obtenirTextepage($vPage, $strLangue, 2);
|
||
|
||
header('Content-Type: application/json; charset=utf-8');
|
||
|
||
$action = $_REQUEST['action'] ?? '';
|
||
|
||
|
||
// =====================
|
||
// CREATE
|
||
// =====================
|
||
if ($action == 'create') {
|
||
|
||
$epr_id = intval($_REQUEST['epr_id'] ?? 0);
|
||
|
||
if ($epr_id > 0) {
|
||
|
||
// ⚠️ On ne crée PLUS en DB
|
||
|
||
// On récupère les ranges existants AVEC stats
|
||
$tabBibRanges = fxInfosBibRange($epr_id);
|
||
|
||
// 👉 On ajoute UNE ligne vide (UI seulement)
|
||
$tabBibRanges[] = [
|
||
'epr_bib_id' => 0, // ID temporaire
|
||
'epr_id' => $epr_id,
|
||
'epr_bib_start' => '',
|
||
'epr_bib_finish' => '',
|
||
'dernier_bib' => '',
|
||
'nb_utilises' => '',
|
||
'liste_trous' => '',
|
||
'liste_doublons' => ''
|
||
];
|
||
|
||
echo json_encode([
|
||
'success' => true,
|
||
'html' => renderBibRanges($tabBibRanges, $epr_id),
|
||
'error' => ''
|
||
]);
|
||
exit;
|
||
}
|
||
|
||
echo json_encode([
|
||
'success' => false,
|
||
'message' => fxBibMsg('bib_v4_ajax_epr_invalid')
|
||
]);
|
||
exit;
|
||
}
|
||
|
||
|
||
// =====================
|
||
// DELETE
|
||
// =====================
|
||
if ($action == 'delete') {
|
||
|
||
$id = intval($_REQUEST['id'] ?? 0);
|
||
|
||
if ($id > 0) {
|
||
|
||
$range = fxInfosBibRange(0,$id);
|
||
|
||
$epr_id = $range[1]["epr_id"];
|
||
|
||
|
||
|
||
|
||
|
||
if (!$range) {
|
||
echo json_encode([
|
||
'success' => false,
|
||
'message' => fxBibMsg('bib_v4_ajax_range_not_found')
|
||
]);
|
||
exit;
|
||
}
|
||
|
||
if ((int)$range[1]['nb_utilises'] > 0) {
|
||
echo json_encode([
|
||
'success' => false,
|
||
'message' => fxBibMsg('bib_v4_ajax_delete_used')
|
||
]);
|
||
exit;
|
||
}
|
||
|
||
|
||
|
||
$sql = "
|
||
DELETE FROM inscriptions_epreuves_bib
|
||
WHERE epr_bib_id = $id
|
||
";
|
||
|
||
$objDatabase->fxQuery($sql);
|
||
|
||
$tabBibRanges = fxInfosBibRange($epr_id);
|
||
|
||
echo json_encode([
|
||
'success' => true,
|
||
'html' => renderBibRanges($tabBibRanges,$epr_id),
|
||
'error' => ''
|
||
]);
|
||
exit;
|
||
}
|
||
|
||
echo json_encode([
|
||
'success' => false,
|
||
'message' => fxBibMsg('bib_v4_ajax_id_invalid')
|
||
]);
|
||
exit;
|
||
}
|
||
// =====================
|
||
// SAVE
|
||
// =====================
|
||
if ($action == 'save') {
|
||
|
||
$id = intval($_REQUEST['id'] ?? 0);
|
||
$epr_id = intval($_REQUEST['epr_id'] ?? 0);
|
||
$start = intval($_REQUEST['start'] ?? 0);
|
||
$end = intval($_REQUEST['end'] ?? 0);
|
||
|
||
$is_new = ($id == 0);
|
||
|
||
// =====================
|
||
// VALIDATION 1 — epr_id
|
||
// =====================
|
||
if ($is_new && $epr_id <= 0) {
|
||
echo json_encode(['success' => false, 'message' => fxBibMsg('bib_v4_ajax_epr_invalid')]);
|
||
exit;
|
||
}
|
||
|
||
// =====================
|
||
// recherche eve_id
|
||
// =====================
|
||
$sqlGet = "SELECT eve_id FROM inscriptions_epreuves WHERE epr_id = $epr_id";
|
||
$eve_id = (int)$objDatabase->fxGetVar($sqlGet);
|
||
|
||
// =====================
|
||
// VALIDATION 2 — VALEURS NUMÉRIQUES
|
||
// =====================
|
||
if ($start <= 0 || $end <= 0) {
|
||
echo json_encode(['success' => false, 'message' => fxBibMsg('bib_v4_ajax_values_invalid')]);
|
||
exit;
|
||
}
|
||
|
||
// =====================
|
||
// VALIDATION 3 — LOGIQUE INTERVALLE
|
||
// =====================
|
||
if ($start > $end) {
|
||
echo json_encode(['success' => false, 'message' => fxBibMsg('bib_v4_ajax_start_gt_end')]);
|
||
exit;
|
||
}
|
||
|
||
// =====================
|
||
// RÉCUPÉRER epr_id + PROTÉGER LES DOSSARDS SI UPDATE
|
||
// =====================
|
||
if (!$is_new) {
|
||
|
||
$sqlGet = "SELECT epr_id FROM inscriptions_epreuves_bib WHERE epr_bib_id = $id";
|
||
$epr_id = (int)$objDatabase->fxGetVar($sqlGet);
|
||
|
||
if ($epr_id <= 0) {
|
||
echo json_encode(['success' => false, 'message' => fxBibMsg('bib_v4_ajax_range_not_found')]);
|
||
exit;
|
||
}
|
||
|
||
$sqlCheckOrphans = "
|
||
SELECT COUNT(*)
|
||
FROM resultats_participants rp
|
||
INNER JOIN inscriptions_epreuves_bib b
|
||
ON b.epr_bib_id = $id
|
||
WHERE rp.epr_id = b.epr_id
|
||
AND rp.no_bib BETWEEN b.epr_bib_start AND b.epr_bib_finish
|
||
AND (
|
||
rp.no_bib < $start
|
||
OR rp.no_bib > $end
|
||
)
|
||
";
|
||
|
||
$nbOrphans = (int)$objDatabase->fxGetVar($sqlCheckOrphans);
|
||
|
||
if ($nbOrphans > 0) {
|
||
echo json_encode([
|
||
'success' => false,
|
||
'message' => fxBibMsg('bib_v4_ajax_orphans')
|
||
]);
|
||
exit;
|
||
}
|
||
}
|
||
|
||
// =====================
|
||
// VALIDATION 4 — OVERLAP pas de double epreuves
|
||
// =====================
|
||
$tabOverlaps = fxBibGetOverlappingRanges($eve_id, $start, $end, $is_new ? 0 : $id);
|
||
|
||
if (!empty($tabOverlaps)) {
|
||
echo json_encode([
|
||
'success' => false,
|
||
'message' => fxBibFormatOverlapConflictMessage($tabOverlaps, $strLangue)
|
||
]);
|
||
exit;
|
||
}
|
||
|
||
// =====================
|
||
// INSERT OU UPDATE
|
||
// =====================
|
||
if ($is_new) {
|
||
|
||
$sql = "
|
||
INSERT INTO inscriptions_epreuves_bib
|
||
(epr_id, epr_bib_start, epr_bib_finish, update_date, update_qui, eve_id)
|
||
VALUES
|
||
($epr_id, $start, $end, NOW(), 'admin', $eve_id)
|
||
";
|
||
|
||
} else {
|
||
|
||
$sql = "
|
||
UPDATE inscriptions_epreuves_bib
|
||
SET epr_bib_start = $start,
|
||
epr_bib_finish = $end,
|
||
update_date = NOW(),
|
||
eve_id = $eve_id
|
||
WHERE epr_bib_id = $id
|
||
";
|
||
}
|
||
|
||
$objDatabase->fxQuery($sql);
|
||
|
||
// =====================
|
||
// RELOAD HTML
|
||
// =====================
|
||
$tabBibRanges = fxInfosBibRange($epr_id);
|
||
|
||
echo json_encode([
|
||
'success' => true,
|
||
'html' => renderBibRanges($tabBibRanges, $epr_id)
|
||
]);
|
||
|
||
exit;
|
||
}
|
||
// =====================
|
||
// VIEW
|
||
// =====================
|
||
if ($action == 'view') {
|
||
|
||
$epr_id = intval($_REQUEST['epr_id'] ?? 0);
|
||
$range_id = intval($_REQUEST['range_id'] ?? 0);
|
||
|
||
if ($epr_id <= 0 || $range_id < 0) {
|
||
echo json_encode([
|
||
'success' => false,
|
||
'message' => fxBibMsg('bib_v4_ajax_params_invalid')
|
||
]);
|
||
exit;
|
||
}
|
||
|
||
// TEMPORAIRE : affichage simple pour test
|
||
$html = renderBibView($epr_id, $range_id, $strLangue);
|
||
|
||
echo json_encode([
|
||
'success' => true,
|
||
'html' => $html
|
||
]);
|
||
exit;
|
||
|
||
|
||
}
|
||
// =====================
|
||
// ANALYSE BATCH
|
||
// =====================
|
||
if ($action == 'batch_analysis') {
|
||
|
||
$epr_id = intval($_POST['epr_id'] ?? 0);
|
||
|
||
if ($epr_id <= 0) {
|
||
echo json_encode([
|
||
'success' => false,
|
||
'message' => fxBibMsg('bib_v4_ajax_epr_invalid')
|
||
]);
|
||
exit;
|
||
}
|
||
|
||
$infoBib = fxInfosBibEpreuve($epr_id);
|
||
|
||
echo json_encode([
|
||
'success' => true,
|
||
'info' => $infoBib
|
||
]);
|
||
exit;
|
||
}
|
||
if ($action == 'refresh_ranges') {
|
||
|
||
$epr_id = intval($_POST['epr_id'] ?? $_GET['epr_id'] ?? 0);
|
||
$mode = $_POST['mode'] ?? $_GET['mode'] ?? 'assign';
|
||
if ($epr_id <= 0) {
|
||
echo json_encode([
|
||
'success' => false,
|
||
'message' => fxBibMsg('bib_v4_ajax_epr_invalid')
|
||
]);
|
||
exit;
|
||
}
|
||
|
||
$tabBibStats = fxInfosBibRange($epr_id);
|
||
|
||
echo json_encode([
|
||
'success' => true,
|
||
'html' => renderBibRanges($tabBibStats, $epr_id, $mode)
|
||
]);
|
||
exit;
|
||
}
|
||
|
||
if ($action == 'refresh_ranges_data') {
|
||
|
||
$epr_id = intval($_POST['epr_id'] ?? 0);
|
||
|
||
if ($epr_id <= 0) {
|
||
echo json_encode(['success' => false]);
|
||
exit;
|
||
}
|
||
|
||
$tab = fxInfosBibRange($epr_id);
|
||
|
||
$out = [];
|
||
|
||
foreach ($tab as $r) {
|
||
$nb_total = $r['nb_total_range'] ?? 0;
|
||
$nb_used = (int)($r['nb_utilises'] ?? 0);
|
||
|
||
$out[] = [
|
||
'id' => (int)$r['epr_bib_id'],
|
||
'last' => (int)$r['dernier_bib'],
|
||
'used' => $nb_used,
|
||
'dispo' => $nb_total - $nb_used,
|
||
'trous' => $r['liste_trous'] ?? '',
|
||
'doublons' => $r['liste_doublons'] ?? ''
|
||
];
|
||
}
|
||
|
||
echo json_encode([
|
||
'success' => true,
|
||
'ranges' => $out
|
||
]);
|
||
exit;
|
||
}
|
||
|
||
// =====================
|
||
// BATCH GO (DEBUG SEULEMENT)
|
||
// =====================
|
||
// Objectif :
|
||
// - recevoir les données du bouton GO
|
||
// - NE RIEN ÉCRIRE en DB
|
||
// - retourner les infos pour validation
|
||
|
||
if ($action == 'batch_go') {
|
||
|
||
// ID de l’épreuve
|
||
$epr_id = intval($_POST['epr_id'] ?? 0);
|
||
|
||
// Liste des ranges sélectionnés (JSON → array PHP)
|
||
$ranges = json_decode($_POST['ranges'] ?? '[]', true);
|
||
|
||
// Ordre choisi (table bib_assignements)
|
||
$ba_id = intval($_POST['ba_id'] ?? 0);
|
||
|
||
// DEBUG : on retourne tout pour validation
|
||
// =====================
|
||
// TEST fxGetParticipantsForBatchAssign
|
||
// =====================
|
||
|
||
$participants = fxGetParticipantsForBatchAssign($epr_id, $ba_id);
|
||
// =====================
|
||
// UTILISER LA FONCTION PROPRE
|
||
// =====================
|
||
$tabRanges = fxGetRangesForBatchAssign($epr_id, $ranges);
|
||
$tabRangesInfos = fxInfosBibRange($epr_id); // ← ICI
|
||
$ids = array_map('intval', $ranges);
|
||
$tabRangesInfos = array_filter($tabRangesInfos, function($r) use ($ids) {
|
||
return in_array((int)$r['epr_bib_id'], $ids);
|
||
});
|
||
|
||
$nextBib = fxGetNextAvailableBib($epr_id, $tabRanges);
|
||
$simu = fxProcessBatchAssignments($epr_id, $participants, $tabRanges, 'simulation');
|
||
$summary = fxBuildBatchSummaryFromRanges($tabRangesInfos, $participants, $epr_id);
|
||
|
||
$info = fxBibMsg(
|
||
'bib_v4_ajax_sim_summary',
|
||
$summary['nb_dispo'],
|
||
$summary['nb_participants'],
|
||
$summary['nb_manque']
|
||
);
|
||
// =====================
|
||
// SIMULATION (temporaire simple)
|
||
// =====================
|
||
|
||
|
||
// =====================
|
||
// RENDER VIEW EN MODE SIMULATION
|
||
// =====================
|
||
$html = renderBibView(
|
||
$epr_id,
|
||
0,
|
||
$strLangue,
|
||
'simulation',
|
||
$simu,
|
||
fxBibTexte('bib_v4_ajax_title_simulation'),
|
||
$info
|
||
);
|
||
|
||
echo json_encode([
|
||
'success' => true,
|
||
'html' => $html
|
||
]);
|
||
|
||
exit;
|
||
|
||
|
||
|
||
}
|
||
|
||
if ($action == 'batch_apply') {
|
||
|
||
// =====================
|
||
// GO BATCH (EXÉCUTION RÉELLE)
|
||
// - Assigne les BIB en DB
|
||
// - Utilise fxProcessBatchAssignments en mode 'execute'
|
||
// - Affiche le résultat réel ($exec), pas de simulation
|
||
// =====================
|
||
|
||
$epr_id = intval($_POST['epr_id'] ?? 0);
|
||
$ranges = json_decode($_POST['ranges'] ?? '[]', true);
|
||
$ba_id = intval($_POST['ba_id'] ?? 0);
|
||
|
||
$participants = fxGetParticipantsForBatchAssign($epr_id, $ba_id);
|
||
$tabRanges = fxGetRangesForBatchAssign($epr_id, $ranges);
|
||
|
||
$exec = fxProcessBatchAssignments($epr_id, $participants, $tabRanges, 'execute');
|
||
|
||
$tabRangesInfos = fxInfosBibRange($epr_id);
|
||
$ids = array_map('intval', $ranges);
|
||
|
||
$tabRangesInfos = array_filter($tabRangesInfos, function($r) use ($ids) {
|
||
return in_array((int)$r['epr_bib_id'], $ids);
|
||
});
|
||
|
||
$summary = fxBuildBatchSummaryFromRanges($tabRangesInfos, $participants, $epr_id);
|
||
|
||
$assigned = count($exec);
|
||
$total = count($participants);
|
||
$remaining = max(0, $total - $assigned);
|
||
$status = 'success';
|
||
|
||
if ($assigned === 0) {
|
||
$status = 'error';
|
||
} elseif ($assigned < $total) {
|
||
$status = 'partial';
|
||
}
|
||
if ($assigned === 0) {
|
||
|
||
$info = fxBibMsg('bib_v4_ajax_assign_none');
|
||
|
||
} elseif ($assigned === $total) {
|
||
|
||
$info = fxBibMsg('bib_v4_ajax_assign_full', $assigned);
|
||
|
||
} else {
|
||
|
||
$info = fxBibMsg('bib_v4_ajax_assign_partial', $assigned, $remaining);
|
||
}
|
||
$html = renderBibView(
|
||
$epr_id,
|
||
0,
|
||
$strLangue,
|
||
'execute',
|
||
$exec,
|
||
fxBibTexte('bib_v4_ajax_title_assign'),
|
||
$info,$status
|
||
);
|
||
|
||
echo json_encode([
|
||
'success' => true,
|
||
'html' => $html
|
||
]);
|
||
|
||
exit;
|
||
}
|
||
// =====================
|
||
// RESET PREVIEW
|
||
// =====================
|
||
if ($action == 'reset_preview') {
|
||
|
||
$epr_id = intval($_POST['epr_id'] ?? 0);
|
||
|
||
$ranges = json_decode($_POST['ranges'] ?? '[]', true);
|
||
|
||
$participants = fxGetParticipantsForBatchReset($epr_id, $ranges);
|
||
|
||
$html = renderBibView(
|
||
$epr_id,
|
||
0,
|
||
$strLangue,
|
||
'reset',
|
||
$participants,
|
||
fxBibTexte('bib_v4_ajax_title_reset_preview'),
|
||
fxBibMsg('bib_v4_ajax_reset_preview_count', count($participants)),
|
||
'warning'
|
||
);
|
||
|
||
echo json_encode([
|
||
'success' => true,
|
||
'html' => $html
|
||
]);
|
||
|
||
exit;
|
||
}
|
||
// =====================
|
||
// RESET APPLY
|
||
// =====================
|
||
if ($action == 'reset_apply') {
|
||
|
||
$epr_id = intval($_POST['epr_id'] ?? 0);
|
||
|
||
$ranges = json_decode($_POST['ranges'] ?? '[]', true);
|
||
|
||
$participants = fxGetParticipantsForBatchReset($epr_id, $ranges);
|
||
|
||
$count = 0;
|
||
|
||
foreach ($participants as $p) {
|
||
|
||
$par_id = intval($p['par_id']);
|
||
|
||
if ($par_id <= 0) {
|
||
continue;
|
||
}
|
||
|
||
$sql = "
|
||
UPDATE resultats_participants
|
||
SET no_bib = NULL
|
||
WHERE par_id = $par_id
|
||
AND is_cancelled = 0
|
||
";
|
||
|
||
$objDatabase->fxQuery($sql);
|
||
|
||
$count++;
|
||
}
|
||
|
||
$html = renderBibView(
|
||
$epr_id,
|
||
0,
|
||
$strLangue,
|
||
'reset',
|
||
$participants,
|
||
fxBibTexte('bib_v4_ajax_title_reset_done'),
|
||
fxBibMsg('bib_v4_ajax_reset_done_count', $count),
|
||
'success'
|
||
);
|
||
|
||
echo json_encode([
|
||
'success' => true,
|
||
'html' => $html
|
||
]);
|
||
|
||
exit;
|
||
}
|
||
// =====================
|
||
// SAVE TEAM MODE
|
||
// =====================
|
||
if ($action == 'save_team_mode') {
|
||
|
||
$epr_id = intval($_REQUEST['epr_id'] ?? 0);
|
||
$team_mode = intval($_REQUEST['team_mode'] ?? 0);
|
||
|
||
if ($epr_id <= 0 || !in_array($team_mode, [1, 2, 3])) {
|
||
|
||
echo json_encode([
|
||
'success' => false,
|
||
'message' => fxBibMsg('bib_v4_ajax_params_invalid')
|
||
]);
|
||
|
||
exit;
|
||
}
|
||
|
||
$sql = "
|
||
UPDATE inscriptions_epreuves
|
||
SET epr_bib_team_mode = $team_mode
|
||
WHERE epr_id = $epr_id
|
||
LIMIT 1
|
||
";
|
||
|
||
$objDatabase->fxQuery($sql);
|
||
|
||
echo json_encode([
|
||
'success' => true
|
||
]);
|
||
|
||
exit;
|
||
}
|
||
|
||
// MSIN-4379 — Étape 2
|
||
// Endpoint AJAX : sauvegarde ou désactivation de la config auto (epr_bib_auto).
|
||
// Paramètres : epr_id, active (0|1), ranges (JSON des epr_bib_id), csrf_token.
|
||
// Prochain tour : étape 6–7 (assignation réelle à l'achat, sans passer par cet endpoint).
|
||
if ($action == 'save_auto_config') {
|
||
|
||
if (!fxBibRequirePromoteurSession()) {
|
||
echo json_encode([
|
||
'success' => false,
|
||
'message' => fxBibMsg('bib_v4_ajax_session_invalid')
|
||
]);
|
||
exit;
|
||
}
|
||
|
||
if (!fxBibValidateCsrf($_POST['csrf_token'] ?? '')) {
|
||
echo json_encode([
|
||
'success' => false,
|
||
'message' => fxBibMsg('bib_v4_ajax_unauthorized')
|
||
]);
|
||
exit;
|
||
}
|
||
|
||
$epr_id = intval($_POST['epr_id'] ?? 0);
|
||
$active = intval($_POST['active'] ?? 1) === 1;
|
||
$ranges = json_decode($_POST['ranges'] ?? '[]', true);
|
||
|
||
if ($epr_id <= 0) {
|
||
echo json_encode([
|
||
'success' => false,
|
||
'message' => fxBibMsg('bib_v4_ajax_epr_invalid')
|
||
]);
|
||
exit;
|
||
}
|
||
|
||
$result = fxSaveBibAutoConfig($epr_id, is_array($ranges) ? $ranges : [], $active);
|
||
|
||
if (!$result['success']) {
|
||
echo json_encode([
|
||
'success' => false,
|
||
'message' => $result['message'] ?? fxBibMsg('bib_v4_ajax_error')
|
||
]);
|
||
exit;
|
||
}
|
||
|
||
$tabBibStats = fxInfosBibRange($epr_id);
|
||
|
||
echo json_encode([
|
||
'success' => true,
|
||
'auto_active' => !empty($result['auto_active']),
|
||
'html' => renderBibRanges($tabBibStats, $epr_id, 'assign')
|
||
]);
|
||
exit;
|
||
}
|
||
|
||
// =====================
|
||
// ACTION INVALIDE
|
||
// =====================
|
||
echo json_encode([
|
||
'success' => false,
|
||
'message' => fxBibMsg('bib_v4_ajax_action_invalid')
|
||
]);
|
||
exit; |