Files
ms1inscription-v5/php/inc_fx_bib_global_generated.php

469 lines
13 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 = [];
if (fxBibProductionEventIsLocked($int_eve_id)) {
return [
'success' => false,
'message' => fxBibMsg('bib_v5_production_locked_error'),
];
}
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),
];
}
// MSIN-4443 — Création séquence : vérifier INSERT + last_id (évite le message générique « Erreur »).
$sqlInsert = "
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)
";
$qryInsert = $objDatabase->fxQuery($sqlInsert);
if ($qryInsert === false) {
return [
'success' => false,
'message' => fxBibMsg('bib_v4_global_batch_gen_insert_failed'),
];
}
$intBibId = 0;
if (method_exists($objDatabase, 'fxGetLastId')) {
$intBibId = (int)$objDatabase->fxGetLastId();
} elseif (isset($objDatabase->last_id)) {
$intBibId = (int)$objDatabase->last_id;
}
// Repli : si insert_id absent, retrouver la ligne fraîchement créée.
if ($intBibId <= 0) {
$intBibId = (int)$objDatabase->fxGetVar("
SELECT epr_bib_id
FROM inscriptions_epreuves_bib
WHERE epr_id = $epr_id
AND epr_bib_start = $intStart
AND epr_bib_finish = $intEnd
AND eve_id = $int_eve_id
ORDER BY epr_bib_id DESC
LIMIT 1
");
}
if ($intBibId <= 0) {
return [
'success' => false,
'message' => fxBibMsg('bib_v4_global_batch_gen_insert_failed'),
];
}
$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,
];
}