Files
ms1inscription-v5/php/inc_fx_bib_global_generated.php
stephan 9e8055bfee Refactor global simulation view handling, enhance styles, and increment version code to 4.72.783
This commit refines the rendering of the global simulation view by introducing a new wrapper function for better structure and maintainability. The CSS is updated to improve the layout and appearance of the simulation toolbar and title. Additionally, JavaScript functions are added to manage the insertion and removal of global simulation views. The version code is incremented to 4.72.783 to reflect these changes.
2026-07-09 12:30:17 -04:00

434 lines
12 KiB
PHP

<?php
/**
* MSIN-4443 — Assignation globale solo sans séquence existante.
* Création automatique de plages de dossards puis assignation via le moteur batch existant.
*/
/** True si l'épreuve a au moins une séquence enregistrée. */
function fxBibEpreuveHasAnySequence($epr_id) {
global $objDatabase;
$epr_id = (int)$epr_id;
if ($epr_id <= 0) {
return false;
}
$intCount = (int)$objDatabase->fxGetVar("
SELECT COUNT(*)
FROM inscriptions_epreuves_bib
WHERE epr_id = $epr_id
AND epr_bib_start IS NOT NULL
AND epr_bib_finish IS NOT NULL
");
return $intCount > 0;
}
/**
* Épreuves solo sans aucune séquence, triées par epr_id (ordre page).
*
* @return array[] lignes inscriptions_epreuves
*/
function fxBibGetSoloEpreuvesWithoutSequence($int_eve_id, $tabEpreuves = null) {
global $objDatabase;
$int_eve_id = (int)$int_eve_id;
if ($int_eve_id <= 0) {
return [];
}
if (!is_array($tabEpreuves)) {
$tabEpreuves = $objDatabase->fxGetResults(
"SELECT * FROM inscriptions_epreuves e"
. " WHERE e.eve_id = $int_eve_id AND epr_actif = 1"
. " ORDER BY epr_id"
);
}
$out = [];
foreach ($tabEpreuves as $epr) {
if ((int)($epr['epr_equipe'] ?? 0) === 1) {
continue;
}
if (fxBibEpreuveHasAnySequence((int)$epr['epr_id'])) {
continue;
}
$out[] = $epr;
}
return $out;
}
/** Bloc 1 disponible : au moins 2 épreuves solo avec séquences sélectionnables. */
function fxBibGlobalBatchHasExistingBlock($int_eve_id, $tabEpreuves) {
$int_eve_id = (int)$int_eve_id;
$intCount = 0;
foreach ($tabEpreuves as $epr) {
if ((int)($epr['epr_equipe'] ?? 0) === 1) {
continue;
}
if (empty(fxBibGetSelectableRangesForGlobalBatch((int)$epr['epr_id']))) {
continue;
}
$intCount++;
if ($intCount >= 2) {
return true;
}
}
return false;
}
/** Bloc 2 disponible : au moins 1 épreuve solo sans séquence. */
function fxBibGlobalBatchHasGeneratedBlock($int_eve_id, $tabEpreuves) {
return count(fxBibGetSoloEpreuvesWithoutSequence($int_eve_id, $tabEpreuves)) >= 1;
}
/** Tuile / coquille assignation globale visible si l'un des deux blocs est utilisable. */
function fxBibGlobalBatchToolIsAvailable($int_eve_id, $tabEpreuves) {
return fxBibGlobalBatchHasExistingBlock($int_eve_id, $tabEpreuves)
|| fxBibGlobalBatchHasGeneratedBlock($int_eve_id, $tabEpreuves);
}
/**
* Valide et ordonne les epr_id du bloc généré (solo, sans séquence, ordre epr_id).
*
* @return int[]
*/
function fxBibParseGlobalBatchGeneratedEprIds($eprIds, $int_eve_id) {
if (!is_array($eprIds)) {
return [];
}
$int_eve_id = (int)$int_eve_id;
$tabWanted = [];
foreach ($eprIds as $epr_id) {
$epr_id = (int)$epr_id;
if ($epr_id <= 0) {
continue;
}
$check = fxBibAssertSoloEpreuveForGlobalBatch($epr_id, $int_eve_id);
if (!$check['success']) {
continue;
}
if (fxBibEpreuveHasAnySequence($epr_id)) {
continue;
}
$tabWanted[$epr_id] = true;
}
if (empty($tabWanted)) {
return [];
}
$tabIds = array_keys($tabWanted);
sort($tabIds, SORT_NUMERIC);
return $tabIds;
}
/**
* Plan de plages générées : taille = a_assigner (sans marge), enchaînement par epr_id.
*
* @return array[] [{epr_id, start, end, nb_pending, skip}]
*/
function fxBibBuildGeneratedRangePlan($int_eve_id, $tabEprIds, $intStartBib, $strSort1, $strSort2) {
$int_eve_id = (int)$int_eve_id;
$intStartBib = (int)$intStartBib;
$tabEprIds = fxBibParseGlobalBatchGeneratedEprIds($tabEprIds, $int_eve_id);
$intCursor = $intStartBib;
$tabPlan = [];
foreach ($tabEprIds as $epr_id) {
$participants = fxGetParticipantsForBatchAssign($epr_id, $strSort1, $strSort2);
$intPending = count($participants);
if ($intPending <= 0) {
$tabPlan[] = [
'epr_id' => $epr_id,
'start' => 0,
'end' => 0,
'nb_pending' => 0,
'skip' => true,
];
continue;
}
$intStart = $intCursor;
$intEnd = $intCursor + $intPending - 1;
$tabPlan[] = [
'epr_id' => $epr_id,
'start' => $intStart,
'end' => $intEnd,
'nb_pending' => $intPending,
'skip' => false,
];
$intCursor = $intEnd + 1;
}
return $tabPlan;
}
/** Plages virtuelles pour simulation (même format que fxGetRangesForBatchAssign). */
function fxBibBuildVirtualRangesForPlan($intStart, $intEnd) {
$intStart = (int)$intStart;
$intEnd = (int)$intEnd;
if ($intStart <= 0 || $intEnd < $intStart) {
return [];
}
return [[
'id' => 0,
'start' => $intStart,
'end' => $intEnd,
]];
}
/**
* Valide le plan généré (chevauchements, épreuves sans séquence, au moins un à assigner).
*/
function fxBibValidateGeneratedRangePlan($int_eve_id, $tabPlan, $strLangue = 'fr') {
$int_eve_id = (int)$int_eve_id;
if (!is_array($tabPlan) || empty($tabPlan)) {
return [
'success' => false,
'message' => fxBibMsg('bib_v4_global_batch_gen_plan_invalid'),
];
}
$blnHasAssignable = false;
foreach ($tabPlan as $item) {
if (!empty($item['skip'])) {
continue;
}
$blnHasAssignable = true;
$epr_id = (int)($item['epr_id'] ?? 0);
$intStart = (int)($item['start'] ?? 0);
$intEnd = (int)($item['end'] ?? 0);
if ($epr_id <= 0 || $intStart <= 0 || $intEnd < $intStart) {
return [
'success' => false,
'message' => fxBibMsg('bib_v4_global_batch_gen_plan_invalid'),
];
}
if (fxBibEpreuveHasAnySequence($epr_id)) {
return [
'success' => false,
'message' => fxBibMsg('bib_v4_global_batch_gen_has_seq'),
];
}
$tabOverlaps = fxBibGetOverlappingRanges($int_eve_id, $intStart, $intEnd, 0, $epr_id);
if (!empty($tabOverlaps)) {
return [
'success' => false,
'message' => fxBibFormatOverlapConflictMessage($tabOverlaps, $strLangue),
];
}
}
if (!$blnHasAssignable) {
return [
'success' => false,
'message' => fxBibMsg('bib_v4_global_batch_gen_no_pending'),
];
}
return ['success' => true];
}
/**
* Crée les séquences en BD pour le plan validé.
*
* @return array{success:bool, message?:string, plans?:array}
*/
function fxBibCreateGeneratedRanges($int_eve_id, $tabPlan, $strLangue = 'fr') {
global $objDatabase;
$int_eve_id = (int)$int_eve_id;
$tabCreated = [];
foreach ($tabPlan as $item) {
if (!empty($item['skip'])) {
continue;
}
$epr_id = (int)($item['epr_id'] ?? 0);
$intStart = (int)($item['start'] ?? 0);
$intEnd = (int)($item['end'] ?? 0);
if ($epr_id <= 0 || $intStart <= 0 || $intEnd < $intStart) {
return [
'success' => false,
'message' => fxBibMsg('bib_v4_global_batch_gen_plan_invalid'),
];
}
if (fxBibEpreuveHasAnySequence($epr_id)) {
return [
'success' => false,
'message' => fxBibMsg('bib_v4_global_batch_gen_has_seq'),
];
}
$tabOverlaps = fxBibGetOverlappingRanges($int_eve_id, $intStart, $intEnd, 0, $epr_id);
if (!empty($tabOverlaps)) {
return [
'success' => false,
'message' => fxBibFormatOverlapConflictMessage($tabOverlaps, $strLangue),
];
}
$objDatabase->fxQuery("
INSERT INTO inscriptions_epreuves_bib
(epr_id, epr_bib_start, epr_bib_finish, update_date, update_qui, eve_id)
VALUES
($epr_id, $intStart, $intEnd, NOW(), 'admin', $int_eve_id)
");
$intBibId = (int)($objDatabase->last_id ?? 0);
if ($intBibId <= 0) {
return [
'success' => false,
'message' => fxBibMsg('bib_v4_ajax_error'),
];
}
$tabCreated[] = [
'epr_id' => $epr_id,
'ranges' => [$intBibId],
'start' => $intStart,
'end' => $intEnd,
];
}
return [
'success' => true,
'plans' => $tabCreated,
];
}
/** Statistiques agrégées pour l'analyse du bloc généré. */
function fxBibBuildGlobalBatchGeneratedAnalysisStats($tabPlan) {
$intEpreuves = 0;
$intPending = 0;
$intBibs = 0;
foreach ($tabPlan as $item) {
if (!empty($item['skip'])) {
continue;
}
$intEpreuves++;
$intPending += (int)($item['nb_pending'] ?? 0);
$intStart = (int)($item['start'] ?? 0);
$intEnd = (int)($item['end'] ?? 0);
if ($intEnd >= $intStart && $intStart > 0) {
$intBibs += ($intEnd - $intStart + 1);
}
}
return [
'nb_epreuves' => $intEpreuves,
'nb_participants' => $intPending,
'nb_bibs' => $intBibs,
];
}
/**
* Simulation globale bloc généré (plages virtuelles, aucune écriture BD).
*
* @return array{success:bool, html?:string, info?:string, message?:string}
*/
function fxBibSimulateGlobalBatchGenerated($int_eve_id, $tabPlan, $strSort1, $strSort2, $strLangue) {
$int_eve_id = (int)$int_eve_id;
$check = fxBibValidateGeneratedRangePlan($int_eve_id, $tabPlan, $strLangue);
if (!$check['success']) {
return $check;
}
$htmlParts = [];
$intParticipants = 0;
$intBibs = 0;
foreach ($tabPlan as $item) {
if (!empty($item['skip'])) {
continue;
}
$epr_id = (int)$item['epr_id'];
$intStart = (int)$item['start'];
$intEnd = (int)$item['end'];
$participants = fxGetParticipantsForBatchAssign($epr_id, $strSort1, $strSort2);
$tabRanges = fxBibBuildVirtualRangesForPlan($intStart, $intEnd);
$simu = fxProcessBatchAssignments($epr_id, $participants, $tabRanges, 'simulation');
$intNbPending = count($participants);
$intParticipants += $intNbPending;
$intBibs += max(0, $intEnd - $intStart + 1);
$tabEpreuve = fxGetEpreuve($epr_id);
$strEprLabel = $tabEpreuve ? fxBibEpreuveDisplayName($tabEpreuve, $strLangue) : ('#' . $epr_id);
$strRangeLabel = fxBibMsg(
'bib_v4_global_batch_gen_range_preview',
$intStart,
$intEnd,
$intNbPending
);
$htmlParts[] = '<div class="bib-global-sim-section">'
. '<h4 class="bib-global-sim-section__title">' . fxBibEsc($strEprLabel) . '</h4>'
. '<p class="bib-global-sim-section__range text-muted small">' . fxBibEsc($strRangeLabel) . '</p>'
. renderBibView(
$epr_id,
0,
$strLangue,
'simulation',
$simu,
fxBibTexte('bib_v4_ajax_title_simulation'),
fxBibMsg(
'bib_v4_ajax_sim_summary',
max(0, $intEnd - $intStart + 1),
$intNbPending,
max(0, $intNbPending - count($simu))
)
)
. '</div>';
}
$info = fxBibMsg(
'bib_v4_ajax_global_gen_sim_summary',
fxBibBuildGlobalBatchGeneratedAnalysisStats($tabPlan)['nb_epreuves'],
$intBibs,
$intParticipants
);
$html = fxBibWrapGlobalSimView($info, implode('', $htmlParts), 'bib-global-sim-view--generated');
return [
'success' => true,
'html' => $html,
'info' => $info,
];
}