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; } // MSIN-4436 — Panneau assignation globale (chargement différé à l'ouverture). if ($action == 'global_batch_panel') { $int_eve_id = (int)($_POST['eve_id'] ?? 0); if ($int_eve_id <= 0) { echo json_encode([ 'success' => false, 'message' => fxBibMsg('bib_v4_ajax_epr_invalid'), ]); exit; } $tabEpreuves = $objDatabase->fxGetResults( "SELECT * FROM inscriptions_epreuves e WHERE e.eve_id = " . $int_eve_id . " AND epr_actif = 1 ORDER BY epr_id" ); $tabBibAssignments = $objDatabase->fxGetResults( "SELECT * FROM bib_assignements WHERE ba_actif = 1 ORDER BY ba_tri" ); $html = renderBibGlobalBatchPanel($int_eve_id, $tabEpreuves, $tabBibAssignments, $strLangue); if ($html === '') { echo json_encode([ 'success' => false, 'message' => fxBibMsg('bib_v4_global_batch_need_two'), ]); exit; } echo json_encode([ 'success' => true, 'html' => $html, ]); exit; } // MSIN-4436 — Analyse batch global (épreuves solo, séquences par épreuve). if ($action == 'batch_global_analysis') { $int_eve_id = (int)($_POST['eve_id'] ?? 0); $ba_id = (int)($_POST['ba_id'] ?? 0); $plans = json_decode($_POST['plans'] ?? '[]', true); $tabPlans = fxBibParseGlobalBatchPlans($plans, $int_eve_id); if ($int_eve_id <= 0 || $ba_id <= 0 || empty($tabPlans)) { echo json_encode([ 'success' => false, 'message' => fxBibMsg('bib_v4_global_batch_plan_invalid'), ]); exit; } $intParticipants = 0; $intDispo = 0; $intAssignable = 0; $intManque = 0; foreach ($tabPlans as $plan) { $epr_id = (int)$plan['epr_id']; $ranges = $plan['ranges']; $participants = fxGetParticipantsForBatchAssign($epr_id, $ba_id); $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, true); }); $summary = fxBuildBatchSummaryFromRanges($tabRangesInfos, $participants, $epr_id); $intParticipants += (int)$summary['nb_participants']; $intDispo += (int)$summary['nb_dispo']; $intAssignable += (int)$summary['nb_assignable']; $intManque += (int)$summary['nb_manque']; } $info = fxBibMsg( 'bib_v4_ajax_global_sim_summary', count($tabPlans), $intDispo, $intParticipants, $intAssignable, $intManque ); echo json_encode([ 'success' => true, 'info' => $info, 'stats' => [ 'nb_epreuves' => count($tabPlans), 'nb_dispo' => $intDispo, 'nb_participants' => $intParticipants, 'nb_assignable' => $intAssignable, 'nb_manque' => $intManque, ], ]); exit; } // MSIN-4436 — Simulation batch global. if ($action == 'batch_global_simulate') { $int_eve_id = (int)($_POST['eve_id'] ?? 0); $ba_id = (int)($_POST['ba_id'] ?? 0); $plans = json_decode($_POST['plans'] ?? '[]', true); $tabPlans = fxBibParseGlobalBatchPlans($plans, $int_eve_id); if ($int_eve_id <= 0 || $ba_id <= 0 || empty($tabPlans)) { echo json_encode([ 'success' => false, 'message' => fxBibMsg('bib_v4_global_batch_plan_invalid'), ]); exit; } $htmlParts = []; $intParticipants = 0; $intDispo = 0; $intAssignable = 0; $intManque = 0; foreach ($tabPlans as $plan) { $epr_id = (int)$plan['epr_id']; $ranges = $plan['ranges']; $participants = fxGetParticipantsForBatchAssign($epr_id, $ba_id); $tabRanges = fxGetRangesForBatchAssign($epr_id, $ranges); $simu = fxProcessBatchAssignments($epr_id, $participants, $tabRanges, 'simulation'); $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, true); }); $summary = fxBuildBatchSummaryFromRanges($tabRangesInfos, $participants, $epr_id); $intParticipants += (int)$summary['nb_participants']; $intDispo += (int)$summary['nb_dispo']; $intAssignable += (int)$summary['nb_assignable']; $intManque += (int)$summary['nb_manque']; $tabEpreuve = fxGetEpreuve($epr_id); $strEprLabel = $tabEpreuve ? fxBibEpreuveDisplayName($tabEpreuve, $strLangue) : ('#' . $epr_id); $htmlParts[] = '
' . '

' . fxBibEsc($strEprLabel) . '

' . renderBibView( $epr_id, 0, $strLangue, 'simulation', $simu, fxBibTexte('bib_v4_ajax_title_simulation'), fxBibMsg( 'bib_v4_ajax_sim_summary', $summary['nb_dispo'], $summary['nb_participants'], $summary['nb_manque'] ) ) . '
'; } $info = fxBibMsg( 'bib_v4_ajax_global_sim_summary', count($tabPlans), $intDispo, $intParticipants, $intAssignable, $intManque ); $html = '
' . '
' . fxBibEsc($info) . '
' . implode('', $htmlParts) . '
'; echo json_encode([ 'success' => true, 'html' => $html, 'info' => $info, ]); 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'); $html = fxBibBuildBatchApplyHtml($epr_id, $exec, count($participants), $strLangue); echo json_encode([ 'success' => true, 'html' => $html ]); exit; } // MSIN-4433 — GO batch par paquets : progression réelle + relecture BD entre paquets. if ($action == 'batch_apply_chunk') { $epr_id = intval($_POST['epr_id'] ?? 0); $ranges = json_decode($_POST['ranges'] ?? '[]', true); $ba_id = intval($_POST['ba_id'] ?? 0); $intChunkSize = (int)($_POST['chunk_size'] ?? 50); $intTotalInitial = (int)($_POST['total_initial'] ?? 0); $blnBatchReset = ((int)($_POST['batch_reset'] ?? 0) === 1); if ($intChunkSize < 1) { $intChunkSize = 50; } if ($intChunkSize > 100) { $intChunkSize = 100; } $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; } $strSessionKey = fxBibBatchExecSessionKey($epr_id); if ($blnBatchReset) { $_SESSION[$strSessionKey] = []; } elseif (!isset($_SESSION[$strSessionKey]) || !is_array($_SESSION[$strSessionKey])) { $_SESSION[$strSessionKey] = []; } $participants = fxGetParticipantsForBatchAssign($epr_id, $ba_id); if ($intTotalInitial <= 0) { $intTotalInitial = count($participants) + count($_SESSION[$strSessionKey]); } if (empty($participants)) { $execAll = $_SESSION[$strSessionKey]; unset($_SESSION[$strSessionKey]); $html = fxBibBuildBatchApplyHtml($epr_id, $execAll, $intTotalInitial, $strLangue); echo json_encode([ 'success' => true, 'complete' => true, 'total_initial' => $intTotalInitial, 'processed' => $intTotalInitial, 'assigned_total' => count($execAll), 'assigned_chunk' => 0, 'html' => $html ]); exit; } $tabChunk = array_slice($participants, 0, $intChunkSize); $tabRanges = fxGetRangesForBatchAssign($epr_id, $ranges); $execChunk = fxProcessBatchAssignments($epr_id, $tabChunk, $tabRanges, 'execute'); $_SESSION[$strSessionKey] = array_merge($_SESSION[$strSessionKey], $execChunk); $intRemaining = count(fxGetParticipantsForBatchAssign($epr_id, $ba_id)); $intProcessed = max(0, $intTotalInitial - $intRemaining); $blnComplete = ($intRemaining === 0); // MSIN-4433 — Dossards épuisés : clôturer si aucune progression sur ce paquet. if (!$blnComplete && empty($execChunk)) { $blnComplete = true; } $arrResponse = [ 'success' => true, 'complete' => $blnComplete, 'total_initial' => $intTotalInitial, 'processed' => $intProcessed, 'assigned_total' => count($_SESSION[$strSessionKey]), 'assigned_chunk' => count($execChunk), ]; if ($blnComplete) { $execAll = $_SESSION[$strSessionKey]; unset($_SESSION[$strSessionKey]); $arrResponse['html'] = fxBibBuildBatchApplyHtml($epr_id, $execAll, $intTotalInitial, $strLangue); } echo json_encode($arrResponse); 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;