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) { $rowLock = $objDatabase->fxGetRow(" SELECT epr_bib_locked FROM inscriptions_epreuves_bib WHERE epr_bib_id = $id "); if ($rowLock && (int)($rowLock['epr_bib_locked'] ?? 0) === 1) { echo json_encode([ 'success' => false, 'message' => fxBibMsg('bib_v4_ajax_range_locked') ]); exit; } $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, epr_bib_locked FROM inscriptions_epreuves_bib WHERE epr_bib_id = $id"; $rowRange = $objDatabase->fxGetRow($sqlGet); if (!$rowRange || (int)($rowRange['epr_id'] ?? 0) <= 0) { echo json_encode(['success' => false, 'message' => fxBibMsg('bib_v4_ajax_range_not_found')]); exit; } if ((int)($rowRange['epr_bib_locked'] ?? 0) === 1) { echo json_encode(['success' => false, 'message' => fxBibMsg('bib_v4_ajax_range_locked')]); exit; } $epr_id = (int)$rowRange['epr_id']; if ($epr_id <= 0) { echo json_encode(['success' => false, 'message' => fxBibMsg('bib_v4_ajax_range_not_found')]); exit; } $sqlBibNumRp = fxBibSqlNoBibUnsigned('rp.no_bib'); $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 $sqlBibNumRp BETWEEN b.epr_bib_start AND b.epr_bib_finish AND ( $sqlBibNumRp < $start OR $sqlBibNumRp > $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); $lockCheck = fxBibAssertRangesUnlocked($epr_id, is_array($ranges) ? $ranges : []); if (!$lockCheck['success']) { echo json_encode([ 'success' => false, 'message' => $lockCheck['message'] ?? fxBibMsg('bib_v4_ajax_range_locked') ]); exit; } // 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); $lockCheck = fxBibAssertRangesUnlocked($epr_id, is_array($ranges) ? $ranges : []); if (!$lockCheck['success']) { echo json_encode([ 'success' => false, 'message' => $lockCheck['message'] ?? fxBibMsg('bib_v4_ajax_range_locked') ]); exit; } $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); $lockCheck = fxBibAssertRangesUnlocked($epr_id, is_array($ranges) ? $ranges : []); if (!$lockCheck['success']) { echo json_encode([ 'success' => false, 'message' => $lockCheck['message'] ?? fxBibMsg('bib_v4_ajax_range_locked') ]); exit; } $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); $lockCheck = fxBibAssertRangesUnlocked($epr_id, is_array($ranges) ? $ranges : []); if (!$lockCheck['success']) { echo json_encode([ 'success' => false, 'message' => $lockCheck['message'] ?? fxBibMsg('bib_v4_ajax_range_locked') ]); exit; } $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 — Verrouillage séquence (cadenas ouvert/fermé) if ($action == 'toggle_lock') { $epr_bib_id = intval($_POST['epr_bib_id'] ?? 0); $epr_id = intval($_POST['epr_id'] ?? 0); $locked = intval($_POST['locked'] ?? 0) === 1; $result = fxBibSetRangeLocked($epr_bib_id, $locked, $epr_id); if (!$result['success']) { echo json_encode([ 'success' => false, 'message' => $result['message'] ?? fxBibMsg('bib_v4_ajax_error') ]); exit; } $epr_id = (int)$result['epr_id']; $tabBibStats = fxInfosBibRange($epr_id); echo json_encode([ 'success' => true, 'locked' => !empty($result['locked']), 'auto_active' => fxIsBibAutoActive($epr_id), 'html' => renderBibRanges($tabBibStats, $epr_id, 'assign') ]); 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') { $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; } // MSIN-4379 — Dashboard anomalies (dossards en double, orphelins, etc.) if ($action == 'anomalies') { $int_eve_id = (int)($_POST['eve_id'] ?? $_GET['eve_id'] ?? 0); if ($int_eve_id <= 0) { echo json_encode([ 'success' => false, 'message' => fxBibMsg('bib_v4_ajax_epr_invalid') ]); exit; } $tabAnomalies = fxBibCollectAnomalies($int_eve_id, $strLangue); echo json_encode([ 'success' => true, 'count' => fxBibAnomalyTotalCount($tabAnomalies), 'html' => renderBibAnomaliesPanel($int_eve_id, $strLangue, $tabAnomalies), ]); exit; } // ===================== // ACTION INVALIDE // ===================== echo json_encode([ 'success' => false, 'message' => fxBibMsg('bib_v4_ajax_action_invalid') ]); exit;