Files
ms1inscription-v5/ajax_bib_range.php

1547 lines
45 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?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'] ?? '';
$arrBibAjaxActions = [
'create',
'delete',
'save',
'view',
'batch_analysis',
'refresh_ranges',
'refresh_ranges_data',
'batch_go',
'batch_apply',
'batch_apply_chunk',
'reset_preview',
'reset_apply',
'reset_all_apply',
'save_allow_inter_epr',
'save_team_mode',
'toggle_lock',
'save_auto_config',
'anomalies',
'batch_global_analysis',
'batch_global_simulate',
'batch_global_gen_analysis',
'batch_global_gen_simulate',
'batch_global_gen_create',
'batch_global_free_analysis',
'batch_global_free_simulate',
'batch_global_free_plan',
'batch_apply_free_chunk',
'global_batch_panel',
'dist_print_panel',
];
if ($action !== '' && in_array($action, $arrBibAjaxActions, true)) {
fxBibRequireAjaxAccess();
}
// =====================
// 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[] = '<div class="bib-global-sim-section">'
. '<h4 class="bib-global-sim-section__title">' . fxBibEsc($strEprLabel) . '</h4>'
. 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']
)
)
. '</div>';
}
$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 = fxBibParseGlobalBatchGeneratedEprIds(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 = fxBibParseGlobalBatchGeneratedEprIds(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 = fxBibParseGlobalBatchGeneratedEprIds(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;
}
// Même filtre que le panneau : solo sans séquence.
$intEveId = (int)($_POST['eve_id'] ?? 0);
if ($intEveId > 0) {
$checkEpr = fxBibAssertSoloEpreuveForGlobalBatch($epr_id, $intEveId);
if (!$checkEpr['success']) {
echo json_encode($checkEpr);
exit;
}
if (fxBibEpreuveHasAnySequence($epr_id)) {
echo json_encode([
'success' => false,
'message' => fxBibMsg('bib_v4_global_batch_gen_has_seq'),
]);
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
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
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 67 (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;