0 && fxBibProductionEventIsLocked($intProductionEveId)) {
echo json_encode([
'success' => false,
'message' => fxBibMsg('bib_v5_production_locked_error'),
]);
exit;
}
}
// =====================
// 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' => ''
];
// MSIN-4446 — summary_html pour pastille « N séquence(s) » hors du conteneur.
echo json_encode(array_merge([
'success' => true,
'html' => renderBibRanges($tabBibRanges, $epr_id),
'error' => ''
], fxBibAssignRangesAjaxExtras($epr_id, $tabBibRanges)));
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);
// MSIN-4446 — Mettre à jour la pastille après suppression (ex. « Aucune séquence »).
echo json_encode(array_merge([
'success' => true,
'html' => renderBibRanges($tabBibRanges, $epr_id),
'error' => ''
], fxBibAssignRangesAjaxExtras($epr_id, $tabBibRanges)));
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, $epr_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);
// MSIN-4446 — Pastille résumé après création / maj séquence.
echo json_encode(array_merge([
'success' => true,
'html' => renderBibRanges($tabBibRanges, $epr_id)
], fxBibAssignRangesAjaxExtras($epr_id, $tabBibRanges)));
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-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') {
$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_unavailable'),
]);
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);
list($strSort1, $strSort2) = fxBibParseBatchSortFromPost();
$plans = json_decode($_POST['plans'] ?? '[]', true);
$tabPlans = fxBibParseGlobalBatchPlans($plans, $int_eve_id);
if ($int_eve_id <= 0 || $strSort1 === '' || 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, $strSort1, $strSort2);
$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);
list($strSort1, $strSort2) = fxBibParseBatchSortFromPost();
$plans = json_decode($_POST['plans'] ?? '[]', true);
$tabPlans = fxBibParseGlobalBatchPlans($plans, $int_eve_id);
if ($int_eve_id <= 0 || $strSort1 === '' || 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, $strSort1, $strSort2);
$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 = fxBibWrapGlobalSimView($info, implode('', $htmlParts));
echo json_encode([
'success' => true,
'html' => $html,
'info' => $info,
]);
exit;
}
// MSIN-4443 — Analyse batch global sans séquence (plages générées).
if ($action == 'batch_global_gen_analysis') {
$int_eve_id = (int)($_POST['eve_id'] ?? 0);
$intStartBib = (int)($_POST['start_bib'] ?? 0);
list($strSort1, $strSort2) = fxBibParseBatchSortFromPost();
$eprIds = json_decode($_POST['epr_ids'] ?? '[]', true);
$tabEprIds = fxBibParseGlobalBatchGeneratedEprIds(is_array($eprIds) ? $eprIds : [], $int_eve_id);
if ($int_eve_id <= 0 || $intStartBib <= 0 || $strSort1 === '' || empty($tabEprIds)) {
echo json_encode([
'success' => false,
'message' => fxBibMsg('bib_v4_global_batch_gen_plan_invalid'),
]);
exit;
}
$tabPlan = fxBibBuildGeneratedRangePlan($int_eve_id, $tabEprIds, $intStartBib, $strSort1, $strSort2);
$check = fxBibValidateGeneratedRangePlan($int_eve_id, $tabPlan, $strLangue);
if (!$check['success']) {
echo json_encode($check);
exit;
}
$tabStats = fxBibBuildGlobalBatchGeneratedAnalysisStats($tabPlan);
$intRangeStart = $intStartBib;
$intRangeEnd = $intStartBib - 1;
foreach ($tabPlan as $item) {
if (!empty($item['skip'])) {
continue;
}
if ($intRangeEnd < (int)$item['start']) {
$intRangeEnd = (int)$item['end'];
} else {
$intRangeEnd = (int)$item['end'];
}
}
$info = fxBibMsg(
'bib_v4_ajax_global_gen_analysis',
$tabStats['nb_epreuves'],
$intRangeStart,
$intRangeEnd,
$tabStats['nb_participants']
);
echo json_encode([
'success' => true,
'info' => $info,
'stats' => $tabStats,
]);
exit;
}
// MSIN-4443 — Simulation batch global sans séquence.
if ($action == 'batch_global_gen_simulate') {
$int_eve_id = (int)($_POST['eve_id'] ?? 0);
$intStartBib = (int)($_POST['start_bib'] ?? 0);
list($strSort1, $strSort2) = fxBibParseBatchSortFromPost();
$eprIds = json_decode($_POST['epr_ids'] ?? '[]', true);
$tabEprIds = fxBibParseGlobalBatchGeneratedEprIds(is_array($eprIds) ? $eprIds : [], $int_eve_id);
if ($int_eve_id <= 0 || $intStartBib <= 0 || $strSort1 === '' || empty($tabEprIds)) {
echo json_encode([
'success' => false,
'message' => fxBibMsg('bib_v4_global_batch_gen_plan_invalid'),
]);
exit;
}
$tabPlan = fxBibBuildGeneratedRangePlan($int_eve_id, $tabEprIds, $intStartBib, $strSort1, $strSort2);
$result = fxBibSimulateGlobalBatchGenerated($int_eve_id, $tabPlan, $strSort1, $strSort2, $strLangue);
echo json_encode($result);
exit;
}
// MSIN-4443 — Création des séquences générées (phase 1 du GO).
if ($action == 'batch_global_gen_create') {
$int_eve_id = (int)($_POST['eve_id'] ?? 0);
$intStartBib = (int)($_POST['start_bib'] ?? 0);
list($strSort1, $strSort2) = fxBibParseBatchSortFromPost();
$eprIds = json_decode($_POST['epr_ids'] ?? '[]', true);
$tabEprIds = fxBibParseGlobalBatchGeneratedEprIds(is_array($eprIds) ? $eprIds : [], $int_eve_id);
if ($int_eve_id <= 0 || $intStartBib <= 0 || $strSort1 === '' || empty($tabEprIds)) {
echo json_encode([
'success' => false,
'message' => fxBibMsg('bib_v4_global_batch_gen_plan_invalid'),
]);
exit;
}
$tabPlan = fxBibBuildGeneratedRangePlan($int_eve_id, $tabEprIds, $intStartBib, $strSort1, $strSort2);
$check = fxBibValidateGeneratedRangePlan($int_eve_id, $tabPlan, $strLangue);
if (!$check['success']) {
echo json_encode($check);
exit;
}
$create = fxBibCreateGeneratedRanges($int_eve_id, $tabPlan, $strLangue);
if (!$create['success']) {
echo json_encode($create);
exit;
}
$tabStats = fxBibBuildGlobalBatchGeneratedAnalysisStats($tabPlan);
echo json_encode([
'success' => true,
'plans' => $create['plans'],
'stats' => $tabStats,
]);
exit;
}
// MSIN-4445 — Analyse batch global libre (Début/Fin, sans séquence).
if ($action == 'batch_global_free_analysis') {
$int_eve_id = (int)($_POST['eve_id'] ?? 0);
$intStart = (int)($_POST['start_bib'] ?? 0);
$intEnd = (int)($_POST['end_bib'] ?? 0);
list($strSort1, $strSort2) = fxBibParseBatchSortFromPost();
$eprIds = json_decode($_POST['epr_ids'] ?? '[]', true);
$tabEprIds = fxBibParseGlobalBatchFreeEprIds(is_array($eprIds) ? $eprIds : [], $int_eve_id);
if ($int_eve_id <= 0 || $intStart <= 0 || $intEnd < $intStart) {
echo json_encode([
'success' => false,
'message' => fxBibMsg('bib_v4_global_batch_free_plan_invalid'),
'duplicates' => ['has' => false, 'count' => 0, 'message' => ''],
]);
exit;
}
// MSIN-4445 — Doublons basés sur la plage entière (même si tri / épreuves incomplets).
$tabDupesOnly = fxBibFreeDuplicateRisk($int_eve_id, [], $intStart, $intEnd);
if ($strSort1 === '') {
$strSort1 = 'ba:2';
}
if (empty($tabEprIds)) {
echo json_encode([
'success' => true,
'info' => $tabDupesOnly['message'],
'stats' => ['nb_epreuves' => 0, 'nb_participants' => 0, 'nb_bibs' => 0],
'feedback' => fxBibFreeQtyFeedback($intEnd - $intStart + 1, 0),
'duplicates' => $tabDupesOnly,
]);
exit;
}
$tabPlan = fxBibBuildFreeRangePlan($int_eve_id, $tabEprIds, $intStart, $intEnd, $strSort1, $strSort2);
$tabAnalysis = fxBibBuildGlobalBatchFreeAnalysis($int_eve_id, $tabPlan, $intStart, $intEnd);
echo json_encode([
'success' => true,
'info' => $tabAnalysis['info'],
'stats' => $tabAnalysis['stats'],
'feedback' => $tabAnalysis['feedback'],
'duplicates' => $tabAnalysis['duplicates'],
]);
exit;
}
// MSIN-4445 — Simulation batch global libre.
if ($action == 'batch_global_free_simulate') {
$int_eve_id = (int)($_POST['eve_id'] ?? 0);
$intStart = (int)($_POST['start_bib'] ?? 0);
$intEnd = (int)($_POST['end_bib'] ?? 0);
list($strSort1, $strSort2) = fxBibParseBatchSortFromPost();
$eprIds = json_decode($_POST['epr_ids'] ?? '[]', true);
$tabEprIds = fxBibParseGlobalBatchFreeEprIds(is_array($eprIds) ? $eprIds : [], $int_eve_id);
if ($int_eve_id <= 0 || $intStart <= 0 || $intEnd < $intStart || $strSort1 === '' || empty($tabEprIds)) {
echo json_encode([
'success' => false,
'message' => fxBibMsg('bib_v4_global_batch_free_plan_invalid'),
]);
exit;
}
$tabPlan = fxBibBuildFreeRangePlan($int_eve_id, $tabEprIds, $intStart, $intEnd, $strSort1, $strSort2);
$result = fxBibSimulateGlobalBatchFree(
$int_eve_id,
$tabPlan,
$intStart,
$intEnd,
$strSort1,
$strSort2,
$strLangue
);
echo json_encode($result);
exit;
}
// MSIN-4445 — Prépare les plans GO libres (aucune création de séquence).
if ($action == 'batch_global_free_plan') {
$int_eve_id = (int)($_POST['eve_id'] ?? 0);
$intStart = (int)($_POST['start_bib'] ?? 0);
$intEnd = (int)($_POST['end_bib'] ?? 0);
list($strSort1, $strSort2) = fxBibParseBatchSortFromPost();
$eprIds = json_decode($_POST['epr_ids'] ?? '[]', true);
$tabEprIds = fxBibParseGlobalBatchFreeEprIds(is_array($eprIds) ? $eprIds : [], $int_eve_id);
if ($int_eve_id <= 0 || $intStart <= 0 || $intEnd < $intStart || $strSort1 === '' || empty($tabEprIds)) {
echo json_encode([
'success' => false,
'message' => fxBibMsg('bib_v4_global_batch_free_plan_invalid'),
]);
exit;
}
$tabPlan = fxBibBuildFreeRangePlan($int_eve_id, $tabEprIds, $intStart, $intEnd, $strSort1, $strSort2);
$prepare = fxBibPrepareFreeRangePlans($int_eve_id, $tabPlan, $intStart, $intEnd, $strLangue);
echo json_encode($prepare);
exit;
}
if ($action == 'refresh_ranges') {
$epr_id = intval($_POST['epr_id'] ?? $_GET['epr_id'] ?? 0);
$mode = $_POST['mode'] ?? $_GET['mode'] ?? 'assign';
$blnLite = !empty($_POST['lite']) || !empty($_GET['lite']);
if ($epr_id <= 0) {
echo json_encode([
'success' => false,
'message' => fxBibMsg('bib_v4_ajax_epr_invalid')
]);
exit;
}
$tabBibStats = fxInfosBibRange($epr_id, 0, $blnLite ? false : true);
// MSIN-4446 — Pastille résumé synchronisée avec le refresh des plages.
echo json_encode(array_merge([
'success' => true,
'html' => renderBibRanges($tabBibStats, $epr_id, $mode)
], fxBibAssignRangesAjaxExtras($epr_id, $tabBibStats)));
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;
}
list($strSort1, $strSort2) = fxBibParseBatchSortFromPost();
fxBibSaveEpreuveBatchSort($epr_id, $strSort1, $strSort2);
$participants = fxGetParticipantsForBatchAssign($epr_id, $strSort1, $strSort2);
// =====================
// 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);
list($strSort1, $strSort2) = fxBibParseBatchSortFromPost();
$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;
}
fxBibSaveEpreuveBatchSort($epr_id, $strSort1, $strSort2);
$participants = fxGetParticipantsForBatchAssign($epr_id, $strSort1, $strSort2);
$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);
list($strSort1, $strSort2) = fxBibParseBatchSortFromPost();
$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] = [];
fxBibSaveEpreuveBatchSort($epr_id, $strSort1, $strSort2);
} elseif (!isset($_SESSION[$strSessionKey]) || !is_array($_SESSION[$strSessionKey])) {
$_SESSION[$strSessionKey] = [];
}
$participants = fxGetParticipantsForBatchAssign($epr_id, $strSort1, $strSort2);
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, $strSort1, $strSort2));
$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;
}
// MSIN-4445 — GO batch par paquets avec plage virtuelle (sans séquence en BD).
if ($action == 'batch_apply_free_chunk') {
$epr_id = intval($_POST['epr_id'] ?? 0);
$intStart = (int)($_POST['start_bib'] ?? 0);
$intEnd = (int)($_POST['end_bib'] ?? 0);
list($strSort1, $strSort2) = fxBibParseBatchSortFromPost();
$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;
}
if ($epr_id <= 0 || $intStart <= 0 || $intEnd < $intStart) {
echo json_encode([
'success' => false,
'message' => fxBibMsg('bib_v4_global_batch_free_plan_invalid'),
]);
exit;
}
// MSIN-4445 — Solo seulement ; une séquence existante n'empêche pas l'assignation libre.
$intEveId = (int)($_POST['eve_id'] ?? 0);
if ($intEveId > 0) {
$checkEpr = fxBibAssertSoloEpreuveForGlobalBatch($epr_id, $intEveId);
if (!$checkEpr['success']) {
echo json_encode($checkEpr);
exit;
}
}
$strSessionKey = fxBibBatchExecSessionKey($epr_id) . '_free';
if ($blnBatchReset) {
$_SESSION[$strSessionKey] = [];
fxBibSaveEpreuveBatchSort($epr_id, $strSort1, $strSort2);
} elseif (!isset($_SESSION[$strSessionKey]) || !is_array($_SESSION[$strSessionKey])) {
$_SESSION[$strSessionKey] = [];
}
$participants = fxGetParticipantsForBatchAssign($epr_id, $strSort1, $strSort2);
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 = fxBibBuildVirtualRangesForPlan($intStart, $intEnd);
$execChunk = fxProcessBatchAssignments($epr_id, $tabChunk, $tabRanges, 'execute');
$_SESSION[$strSessionKey] = array_merge($_SESSION[$strSessionKey], $execChunk);
$intRemaining = count(fxGetParticipantsForBatchAssign($epr_id, $strSort1, $strSort2));
$intProcessed = max(0, $intTotalInitial - $intRemaining);
$blnComplete = ($intRemaining === 0);
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,
par_date_bib = NULL,
par_maj = '" . fxGetDateTime() . "'
WHERE par_id = $par_id
AND is_cancelled = 0
";
$objDatabase->fxQuery($sql);
$count++;
}
// MSIN-4453 — Effacer aussi no_equipe (sinon le team# original reste et est réutilisé)
fxClearTeamNumbersAfterBibReset($epr_id, $participants);
$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;
}
// =====================
// RESET ALL ÉPREUVE
// =====================
if ($action == 'reset_all_apply') {
$epr_id = intval($_POST['epr_id'] ?? 0);
if ($epr_id <= 0) {
echo json_encode([
'success' => false,
'message' => fxBibMsg('bib_v4_ajax_params_invalid'),
]);
exit;
}
$lockCheck = fxBibAssertEpreuveNoLockedRanges($epr_id);
if (!$lockCheck['success']) {
echo json_encode([
'success' => false,
'message' => $lockCheck['message'] ?? fxBibMsg('bib_v4_js_reset_all_locked'),
]);
exit;
}
$participants = fxGetParticipantsForEpreuveResetAll($epr_id);
$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,
par_date_bib = NULL,
par_maj = '" . fxGetDateTime() . "'
WHERE par_id = $par_id
AND is_cancelled = 0
";
$objDatabase->fxQuery($sql);
$count++;
}
// MSIN-4453 — Effacer aussi no_equipe (aligné reset legacy)
fxClearTeamNumbersAfterBibReset($epr_id);
$html = renderBibView(
$epr_id,
0,
$strLangue,
'reset',
$participants,
fxBibTexte('bib_v4_ajax_title_reset_all_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);
$tabExtras = fxBibAssignRangesAjaxExtras($epr_id, $tabBibStats);
echo json_encode(array_merge([
'success' => true,
'locked' => !empty($result['locked']),
'html' => renderBibRanges($tabBibStats, $epr_id, 'assign')
], $tabExtras));
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);
$tabExtras = fxBibAssignRangesAjaxExtras($epr_id, $tabBibStats);
// Conserver le booléen métier de la config auto (prioritaire sur le recalcul).
$tabExtras['auto_active'] = !empty($result['auto_active']) ? 1 : 0;
$tabExtras['summary_html'] = fxBibAssignHeaderSummary(
(int)$tabExtras['seq_count'],
!empty($result['auto_active'])
);
echo json_encode(array_merge([
'success' => true,
'html' => renderBibRanges($tabBibStats, $epr_id, 'assign')
], $tabExtras));
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;
}
// MSIN-4424 — Sommaire des quantités (toutes épreuves)
if ($action == 'qty_summary') {
$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;
}
$tabSummary = fxBibCollectEventQtySummary($int_eve_id, $strLangue);
echo json_encode([
'success' => true,
'totals' => $tabSummary['totals'] ?? [],
'html' => renderBibQtySummaryPanel($int_eve_id, $strLangue, $tabSummary),
]);
exit;
}
// MSIN-4433 — Panneau impression PDF distribution dossards
if ($action == 'dist_print_panel') {
$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;
}
$tabPanel = fxBibCollectDistPrintPanelData($int_eve_id, $strLangue);
echo json_encode([
'success' => true,
'html' => renderBibDistPrintPanel($int_eve_id, $strLangue, $tabPanel),
'meta' => $tabPanel['meta'] ?? [],
]);
exit;
}
// =====================
// ACTION INVALIDE
// =====================
echo json_encode([
'success' => false,
'message' => fxBibMsg('bib_v4_ajax_action_invalid')
]);
exit;