diff --git a/inc_footer_scripts.php b/inc_footer_scripts.php index df8786a..4936e92 100644 --- a/inc_footer_scripts.php +++ b/inc_footer_scripts.php @@ -359,7 +359,7 @@ if ($strLangue == 'fr') { $link.html(''); } } else { - alert($result.state); + alert(($result && ($result.message || $result.state)) || 'Erreur'); } $preloader.fadeOut(); diff --git a/js/v2/bib-assignments.js b/js/v2/bib-assignments.js index f670751..4816fa3 100644 --- a/js/v2/bib-assignments.js +++ b/js/v2/bib-assignments.js @@ -1080,13 +1080,15 @@ }) .then(function (res) { if (!res.ok) { - throw new Error(bibJs('jsErrGeneric')); + throw new Error(bibJs('jsErrGeneric') || 'Erreur'); } - return res.json(); + return res.json().catch(function () { + throw new Error(bibJs('jsServerError') || bibJs('jsErrGeneric') || 'Erreur serveur'); + }); }) .then(function (createData) { - if (!createData || !createData.success || !createData.plans) { - throw new Error((createData && createData.message) || bibJs('jsErrGeneric')); + if (!createData || !createData.success || !Array.isArray(createData.plans) || createData.plans.length === 0) { + throw new Error((createData && createData.message) || bibJs('jsErrGeneric') || 'Erreur'); } var plans = createData.plans; @@ -1104,7 +1106,11 @@ chain = chain.then(function () { var row = moduleBib.querySelector('.epr-row[data-epr-id="' + plan.epr_id + '"]'); if (!row) { - throw new Error(bibJs('jsErrGeneric')); + throw new Error( + bibJs('jsGlobalGenEprMissing') + || bibJs('jsErrGeneric') + || 'Épreuve introuvable — rechargez la page.' + ); } var pendingBefore = bibRowPendingBibs(row); diff --git a/php/inc_fx_bib_global_generated.php b/php/inc_fx_bib_global_generated.php index a7533bc..84d8a9e 100644 --- a/php/inc_fx_bib_global_generated.php +++ b/php/inc_fx_bib_global_generated.php @@ -297,18 +297,46 @@ function fxBibCreateGeneratedRanges($int_eve_id, $tabPlan, $strLangue = 'fr') { ]; } - $objDatabase->fxQuery(" + // 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 + "); + } - $intBibId = (int)($objDatabase->last_id ?? 0); if ($intBibId <= 0) { return [ 'success' => false, - 'message' => fxBibMsg('bib_v4_ajax_error'), + 'message' => fxBibMsg('bib_v4_global_batch_gen_insert_failed'), ]; } diff --git a/php/inc_fx_promoteur.php b/php/inc_fx_promoteur.php index 59eb0d4..42e2da6 100644 --- a/php/inc_fx_promoteur.php +++ b/php/inc_fx_promoteur.php @@ -3331,6 +3331,9 @@ function fxBibStaticFallback($clef) { 'bib_v4_global_batch_gen_no_pending' => ['fr' => 'Aucune inscription à assigner dans les épreuves sélectionnées.', 'en' => 'No registrations to assign in the selected races.'], 'bib_v4_global_batch_gen_has_seq' => ['fr' => 'Une épreuve sélectionnée a déjà une séquence — rechargez le panneau.', 'en' => 'A selected race already has a sequence — reload the panel.'], 'bib_v4_global_batch_gen_no_start' => ['fr' => 'Indiquez le numéro du premier dossard.', 'en' => 'Enter the first bib number.'], + 'bib_v4_global_batch_gen_insert_failed' => ['fr' => 'Impossible de créer la séquence de dossards. Réessayez ou rechargez la page.', 'en' => 'Could not create the bib sequence. Try again or reload the page.'], + 'bib_v4_js_global_gen_epr_missing' => ['fr' => 'Épreuve introuvable sur la page après création des séquences. Rechargez la page puis réessayez.', 'en' => 'Race not found on the page after creating sequences. Reload the page and try again.'], + 'bib_v4_ajax_error' => ['fr' => 'Erreur', 'en' => 'Error'], 'bib_v4_global_batch_unavailable' => ['fr' => 'Aucune assignation globale disponible pour cet événement.', 'en' => 'No global assignment available for this event.'], 'bib_v4_ajax_global_gen_sim_summary' => ['fr' => '%d épreuve(s) · %d dossards · %d à assigner', 'en' => '%d race(s) · %d bibs · %d to assign'], 'bib_v4_ajax_global_gen_analysis' => ['fr' => '%d épreuve(s) · plage %d → %d · %d à assigner', 'en' => '%d race(s) · range %d → %d · %d to assign'], @@ -3589,6 +3592,7 @@ function fxBibModuleJsDataAttrs() { 'global-no-epr' => 'bib_v4_js_global_no_epr', 'global-no-seq' => 'bib_v4_js_global_no_seq', 'global-gen-no-start' => 'bib_v4_global_batch_gen_no_start', + 'global-gen-epr-missing' => 'bib_v4_js_global_gen_epr_missing', 'global-gen-no-epr' => 'bib_v4_js_global_no_epr', 'global-batch-finalizing' => 'bib_v4_js_global_batch_finalizing', 'global-batch-complete-title' => 'bib_v4_global_batch_go_complete_title', @@ -4775,6 +4779,7 @@ function renderBibGlobalBatchPanelGenerated($int_eve_id, $tabEpreuves, $strLangu class="form-control form-control-sm bib-global-gen-start-bib" min="1" step="1" + value="1" inputmode="numeric" placeholder="1"> diff --git a/php/inc_settings.php b/php/inc_settings.php index b0d1a1d..2a6aac1 100644 --- a/php/inc_settings.php +++ b/php/inc_settings.php @@ -7,7 +7,7 @@ * Constantes * * **************/ -define('_VERSION_CODE', '4.72.819'); +define('_VERSION_CODE', '4.72.821'); define('_DATE_CODE', '2026-07-13'); //MSIN-4290 define('QR_SECRET_KEY', 'ms1_qr_2026_cle_secrete_longue_et_fixe'); diff --git a/sql/MSIN-4443-bib-gen-create-error-msg.sql b/sql/MSIN-4443-bib-gen-create-error-msg.sql new file mode 100644 index 0000000..80f6689 --- /dev/null +++ b/sql/MSIN-4443-bib-gen-create-error-msg.sql @@ -0,0 +1,15 @@ +-- MSIN-4443 — Message d'erreur création séquences (assignation globale sans séquence) +-- Contexte : remplace le message trop vague « Erreur » (bib_v4_ajax_error) sur échec INSERT +-- Notes prod : idempotent + +DELETE FROM info +WHERE info_clef IN ( + 'bib_v4_global_batch_gen_insert_failed', + 'bib_v4_js_global_gen_epr_missing' +) AND info_prg = 'compte.php'; + +INSERT INTO info (info_clef, info_langue, info_texte, info_aide, info_prg, info_description, info_trie, info_actif, info_option1, info_option2, info_option3, info_creation) VALUES +('bib_v4_global_batch_gen_insert_failed', 'fr', 'Impossible de créer la séquence de dossards. Réessayez ou rechargez la page.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('bib_v4_global_batch_gen_insert_failed', 'en', 'Could not create the bib sequence. Try again or reload the page.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('bib_v4_js_global_gen_epr_missing', 'fr', 'Épreuve introuvable sur la page après création des séquences. Rechargez la page puis réessayez.', '', 'compte.php', '', 0, 1, '', '', '', NOW()), +('bib_v4_js_global_gen_epr_missing', 'en', 'Race not found on the page after creating sequences. Reload the page and try again.', '', 'compte.php', '', 0, 1, '', '', '', NOW()); diff --git a/superadm/enregistrer.php b/superadm/enregistrer.php index 1e19f76..544fec3 100644 --- a/superadm/enregistrer.php +++ b/superadm/enregistrer.php @@ -13,24 +13,95 @@ elseif (isset($_POST['action'])) else $action = 'Aucune action définie'; -// MSIN-4401 — Préférences rapport chrono (colonnes / ordre / tri) : mêmes droits -// que rapport_new.php (session promoteur com_info). Les autres actions restent -// réservées au canal superadm (usa_id). +// MSIN-4401 — Actions autorisées en session compte promoteur (com_info), sans usa_id : +// - prefs rapport chrono (chksave / trie / groupe) +// - bascule « dossard récupéré » (remis) depuis le site promoteur / gestion V2 +// Les autres actions restent réservées au canal superadm (usa_id). if (session_status() !== PHP_SESSION_ACTIVE) { session_start(); } -$tabRapportCompteActions = array('chksave', 'triechange', 'triecheckall', 'groupechange'); -$blnRapportCompte = in_array($action, $tabRapportCompteActions, true) - && !empty($_SESSION['com_info']['com_id']) + +$intComCompte = intval($_SESSION['com_info']['com_id'] ?? ($_SESSION['com_id'] ?? 0)); +$tabCompteActions = array('chksave', 'triechange', 'triecheckall', 'groupechange', 'remis'); +$blnComptePromoteur = in_array($action, $tabCompteActions, true) + && $intComCompte > 0 && empty($_SESSION['usa_id']); -if ($blnRapportCompte) { - // Ne jamais laisser le promoteur écrire les prefs d'un autre com_id - $_REQUEST['key'] = intval($_SESSION['com_info']['com_id']); - if (isset($_POST['key'])) { - $_POST['key'] = $_REQUEST['key']; - } +if ($blnComptePromoteur) { header('Content-Type: application/json; charset=utf-8'); + + if ($action === 'remis') { + // Forcer l'identité + cibles (évite spoofing POST). + $strParNom = trim( + (string)($_SESSION['com_info']['com_prenom'] ?? '') + . ' ' + . (string)($_SESSION['com_info']['com_nom'] ?? '') + ); + if ($strParNom === '') { + $strParNom = 'compte#' . $intComCompte; + } + $_REQUEST['par_id'] = $intComCompte; + $_REQUEST['par'] = $strParNom; + $_REQUEST['table'] = urlencode(base64_encode('resultats_participants')); + $_REQUEST['field'] = 'no_bib_remis'; + $_REQUEST['key'] = 'par_id'; + if (isset($_POST['par_id'])) { + $_POST['par_id'] = $_REQUEST['par_id']; + } + if (isset($_POST['par'])) { + $_POST['par'] = $_REQUEST['par']; + } + if (isset($_POST['table'])) { + $_POST['table'] = $_REQUEST['table']; + } + if (isset($_POST['field'])) { + $_POST['field'] = $_REQUEST['field']; + } + if (isset($_POST['key'])) { + $_POST['key'] = $_REQUEST['key']; + } + + // Droit sur l'événement du participant (Legacy com_eve_promoteur OU V2 bib_remis). + $intParCible = intval($_REQUEST['id'] ?? 0); + $blnOkRemis = false; + if ($intParCible > 0) { + $intEveCible = intval($db->fxGetVar( + 'SELECT eve_id FROM resultats_participants WHERE par_id = ' . $intParCible . ' LIMIT 1' + )); + if ($intEveCible > 0) { + $strEvePromoteur = (string)$db->fxGetVar( + 'SELECT com_eve_promoteur FROM inscriptions_comptes WHERE com_id = ' . $intComCompte . ' LIMIT 1' + ); + $arrEvePromo = array_filter(array_map('intval', explode(',', $strEvePromoteur))); + if (in_array($intEveCible, $arrEvePromo, true)) { + $blnOkRemis = true; + } else { + if (!function_exists('fxEveAccesHasPermission')) { + require_once dirname(__DIR__) . '/php/inc_fx_eve_acces.php'; + } + $blnOkRemis = fxEveAccesHasPermission( + $intComCompte, + $intEveCible, + 'inscriptions_gestion.bib_remis' + ); + } + } + } + if (!$blnOkRemis) { + echo json_encode(array( + 'state' => 'error', + 'message' => 'Accès refusé pour ce dossard récupéré.', + 'code' => 'forbidden', + )); + exit; + } + } else { + // Prefs rapport : ne jamais laisser le promoteur écrire un autre com_id + $_REQUEST['key'] = $intComCompte; + if (isset($_POST['key'])) { + $_POST['key'] = $_REQUEST['key']; + } + } } else { fxSuperadmAjaxAuthOrJsonExit(); }