diff --git a/ajax_bib_range.php b/ajax_bib_range.php
index af204f6..b63389a 100644
--- a/ajax_bib_range.php
+++ b/ajax_bib_range.php
@@ -43,6 +43,7 @@ $arrBibAjaxActions = [
'batch_global_analysis',
'batch_global_simulate',
'global_batch_panel',
+ 'dist_print_panel',
];
if ($action !== '' && in_array($action, $arrBibAjaxActions, true)) {
@@ -1153,6 +1154,29 @@ if ($action == 'qty_summary') {
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
// =====================
diff --git a/bib_dist_print.php b/bib_dist_print.php
new file mode 100644
index 0000000..fa4a876
--- /dev/null
+++ b/bib_dist_print.php
@@ -0,0 +1,46 @@
+ intMax) {
+ cb.checked = false;
+ }
+ });
+
+ let form = panel.querySelector('.bib-dist-print-form');
+ if (form) {
+ form.addEventListener('submit', function () {
+ window.setTimeout(function () {
+ refreshBibDistPrintPanel();
+ }, 1500);
+ });
+ }
+ }
+
/** MSIN-4424 — Rafraîchit le sommaire des quantités (si déjà chargé ou ouverture). */
function refreshBibQtySummaryPanel() {
let mount = document.getElementById('bib-qty-summary-mount');
diff --git a/php/inc_fx_promoteur.php b/php/inc_fx_promoteur.php
index 713cc07..926807d 100644
--- a/php/inc_fx_promoteur.php
+++ b/php/inc_fx_promoteur.php
@@ -3343,6 +3343,19 @@ function fxBibStaticFallback($clef) {
'bib_v4_qty_summary_by_epr' => ['fr' => 'Détail par épreuve', 'en' => 'Breakdown by race'],
'bib_v4_qty_summary_col_epr' => ['fr' => 'Épreuve', 'en' => 'Race'],
'bib_v4_ajax_global_sim_summary' => ['fr' => '%d épreuve(s) · %d dispo / %d à assigner → %d assignables (%d manque)', 'en' => '%d race(s) · %d avail. / %d to assign → %d assignable (%d short)'],
+ 'bib_v4_dist_print_title' => ['fr' => 'Impression PDF — distribution des dossards', 'en' => 'PDF print — bib distribution'],
+ 'bib_v4_dist_print_questions' => ['fr' => 'Colonnes optionnelles (questions)', 'en' => 'Optional columns (questions)'],
+ 'bib_v4_dist_print_generate' => ['fr' => 'Générer le PDF', 'en' => 'Generate PDF'],
+ 'bib_v4_dist_print_last_none' => ['fr' => 'Aucune impression enregistrée pour cet événement.', 'en' => 'No print recorded for this event yet.'],
+ 'bib_v4_dist_print_max_questions' => ['fr' => 'Maximum %d questions par épreuve pour le format lettre.', 'en' => 'Maximum %d questions per race for letter format.'],
+ 'bib_v4_dist_print_no_questions' => ['fr' => 'Aucune question éligible pour cette épreuve.', 'en' => 'No eligible question for this race.'],
+ 'bib_v4_dist_print_col_bib' => ['fr' => 'Dossard', 'en' => 'Bib'],
+ 'bib_v4_dist_print_col_nom' => ['fr' => 'Nom', 'en' => 'Last name'],
+ 'bib_v4_dist_print_col_prenom' => ['fr' => 'Prénom', 'en' => 'First name'],
+ 'bib_v4_dist_print_col_sexe' => ['fr' => 'Sexe', 'en' => 'Gender'],
+ 'bib_v4_dist_print_empty_epr' => ['fr' => 'Aucun dossard assigné pour cette épreuve.', 'en' => 'No bib assigned for this race.'],
+ 'bib_v4_dist_print_last' => ['fr' => 'Dernière impression : %s — %s', 'en' => 'Last print: %s — %s'],
+ 'bib_v4_dist_print_footer' => ['fr' => 'Imprimé le %s — %s', 'en' => 'Printed on %s — %s'],
];
return $tab[$clef][$lng] ?? '';
@@ -4609,6 +4622,10 @@ function fxShowBibTool4($str_code, $int_eve_id, $strLangue){
+
+
+
+
@@ -7723,4 +7740,744 @@ function fxResolveTeamBib(
}
return null;
+}
+
+// =============================================================================
+// MSIN-4433 — Impression PDF distribution des dossards
+// =============================================================================
+
+/** MSIN-4433 — Limite de colonnes questions (format lettre 8½ × 11). */
+function fxBibDistPrintMaxQuestions() {
+ return 5;
+}
+
+/** MSIN-4433 — Nom affiché du compte connecté (promoteur ou superadmin). */
+function fxBibGetCurrentPromoteurDisplayName() {
+ if (!empty($_SESSION['usa_id']) && !empty($_SESSION['usa_info']) && is_array($_SESSION['usa_info'])) {
+ $str = trim((string)($_SESSION['usa_info']['com_prenom'] ?? '') . ' ' . (string)($_SESSION['usa_info']['com_nom'] ?? ''));
+ if ($str !== '') {
+ return $str;
+ }
+ }
+ if (!empty($_SESSION['com_info']) && is_array($_SESSION['com_info'])) {
+ $str = trim((string)($_SESSION['com_info']['com_prenom'] ?? '') . ' ' . (string)($_SESSION['com_info']['com_nom'] ?? ''));
+ if ($str !== '') {
+ return $str;
+ }
+ }
+ return trim((string)($_SESSION['com_prenom'] ?? '') . ' ' . (string)($_SESSION['com_nom'] ?? ''));
+}
+
+/** MSIN-4433 — Accès génération PDF (session, permissions, CSRF). */
+function fxBibRequireDistPrintAccess() {
+ if (!fxBibRequirePromoteurSession()) {
+ http_response_code(403);
+ header('Content-Type: text/plain; charset=utf-8');
+ echo fxBibMsg('bib_v4_ajax_session_invalid');
+ exit;
+ }
+
+ if (empty($_SESSION['usa_id'])) {
+ require_once __DIR__ . '/inc_fx_eve_acces.php';
+ $intComId = (int)($_SESSION['com_id'] ?? 0);
+ $intEveId = (int)($_REQUEST['eve_id'] ?? $_POST['eve_id'] ?? 0);
+
+ if ($intComId <= 0 || $intEveId <= 0
+ || !fxEveAccesHasAnyPermission($intComId, $intEveId, array('dossards.manage', 'epreuves.edit_qte'))) {
+ http_response_code(403);
+ header('Content-Type: text/plain; charset=utf-8');
+ echo fxBibMsg('bib_v4_ajax_unauthorized');
+ exit;
+ }
+ }
+
+ $strToken = $_POST['csrf_token'] ?? $_REQUEST['csrf_token'] ?? '';
+ if (!fxBibValidateCsrf($strToken)) {
+ http_response_code(403);
+ header('Content-Type: text/plain; charset=utf-8');
+ echo fxBibMsg('bib_v4_ajax_unauthorized');
+ exit;
+ }
+}
+
+/** MSIN-4433 — Parse liste d'IDs questions depuis chaîne CSV. */
+function fxBibParseDistPrintQueIds($strCsv) {
+ if ($strCsv === null || $strCsv === '') {
+ return [];
+ }
+ $out = [];
+ foreach (explode(',', (string)$strCsv) as $part) {
+ $intId = (int)trim($part);
+ if ($intId > 0 && !in_array($intId, $out, true)) {
+ $out[] = $intId;
+ }
+ }
+ return $out;
+}
+
+/** MSIN-4433 — Sérialise les IDs questions pour stockage. */
+function fxBibDistPrintQueIdsToCsv(array $tabQueIds) {
+ $out = [];
+ foreach ($tabQueIds as $mixId) {
+ $intId = (int)$mixId;
+ if ($intId > 0 && !in_array($intId, $out, true)) {
+ $out[] = $intId;
+ }
+ }
+ return implode(',', $out);
+}
+
+/** MSIN-4433 — Libellé court sexe pour le PDF. */
+function fxBibDistPrintSexeLabel($strSexe, $strLangue = 'fr') {
+ $strSexe = strtolower(trim((string)$strSexe));
+ if ($strLangue === 'en') {
+ $tab = ['f' => 'F', 'h' => 'M', 'n' => 'X', 'a' => 'O'];
+ } else {
+ $tab = ['f' => 'F', 'h' => 'H', 'n' => 'X', 'a' => 'A'];
+ }
+ return $tab[$strSexe] ?? strtoupper($strSexe);
+}
+
+/** MSIN-4433 — Date/heure affichée (pied de page, panneau). */
+function fxBibFormatDistPrintDateTime($strWhen = '', $strLangue = 'fr') {
+ $ts = ($strWhen !== '') ? strtotime($strWhen) : time();
+ if ($ts === false) {
+ $ts = time();
+ }
+ if ($strLangue === 'en') {
+ return date('F j, Y, g:i a', $ts);
+ }
+ $tabMonths = [
+ 1 => 'janvier', 2 => 'février', 3 => 'mars', 4 => 'avril',
+ 5 => 'mai', 6 => 'juin', 7 => 'juillet', 8 => 'août',
+ 9 => 'septembre', 10 => 'octobre', 11 => 'novembre', 12 => 'décembre',
+ ];
+ $intDay = (int)date('j', $ts);
+ $intMonth = (int)date('n', $ts);
+ $intYear = (int)date('Y', $ts);
+ $strTime = date('H:i', $ts);
+ return $intDay . ' ' . ($tabMonths[$intMonth] ?? '') . ' ' . $intYear . ', ' . $strTime;
+}
+
+/** MSIN-4433 — Métadonnées dernière impression (événement). */
+function fxBibGetDistPrintMeta($int_eve_id) {
+ global $objDatabase;
+
+ $int_eve_id = (int)$int_eve_id;
+ if ($int_eve_id <= 0) {
+ return ['at' => '', 'by' => '', 'com_id' => 0];
+ }
+
+ $row = $objDatabase->fxGetRow("
+ SELECT eve_bib_dist_print_at, eve_bib_dist_print_by, eve_bib_dist_print_com_id
+ FROM inscriptions_evenements
+ WHERE eve_id = $int_eve_id
+ LIMIT 1
+ ");
+
+ if (!is_array($row)) {
+ return ['at' => '', 'by' => '', 'com_id' => 0];
+ }
+
+ return [
+ 'at' => trim((string)($row['eve_bib_dist_print_at'] ?? '')),
+ 'by' => trim((string)($row['eve_bib_dist_print_by'] ?? '')),
+ 'com_id' => (int)($row['eve_bib_dist_print_com_id'] ?? 0),
+ ];
+}
+
+/** MSIN-4433 — Persiste date / auteur de l'impression. */
+function fxBibSaveDistPrintMeta($int_eve_id, $int_com_id, $str_by) {
+ global $objDatabase;
+
+ $int_eve_id = (int)$int_eve_id;
+ $int_com_id = (int)$int_com_id;
+ $str_by = trim((string)$str_by);
+ if ($int_eve_id <= 0) {
+ return;
+ }
+
+ $sql = "
+ UPDATE inscriptions_evenements
+ SET eve_bib_dist_print_at = '" . $objDatabase->fxEscape(fxGetDateTime()) . "',
+ eve_bib_dist_print_by = '" . $objDatabase->fxEscape($str_by) . "',
+ eve_bib_dist_print_com_id = $int_com_id
+ WHERE eve_id = $int_eve_id
+ LIMIT 1
+ ";
+ $objDatabase->fxQuery($sql);
+}
+
+/**
+ * MSIN-4433 — Données panneau : épreuves, questions éligibles, sélection mémorisée.
+ * @return array{meta: array, epreuves: array>}
+ */
+function fxBibCollectDistPrintPanelData($int_eve_id, $strLangue = 'fr') {
+ global $objDatabase;
+
+ $int_eve_id = (int)$int_eve_id;
+ $tabOut = ['meta' => fxBibGetDistPrintMeta($int_eve_id), 'epreuves' => []];
+
+ if ($int_eve_id <= 0) {
+ return $tabOut;
+ }
+
+ $sqlEpreuves = "
+ SELECT *
+ FROM inscriptions_epreuves
+ WHERE eve_id = $int_eve_id
+ AND epr_actif = 1
+ ORDER BY epr_tri, epr_id
+ ";
+ $tabEpreuves = $objDatabase->fxGetResults($sqlEpreuves);
+ if (!is_array($tabEpreuves)) {
+ return $tabOut;
+ }
+
+ foreach ($tabEpreuves as $epr) {
+ $epr_id = (int)($epr['epr_id'] ?? 0);
+ if ($epr_id <= 0) {
+ continue;
+ }
+
+ $tabQuestionOpts = fxBibGetQuestionSortOptions($epr_id, $int_eve_id, $strLangue);
+ $tabSelected = fxBibParseDistPrintQueIds($epr['ba_bib_dist_que_ids'] ?? '');
+ $tabValidIds = [];
+ foreach ($tabQuestionOpts as $opt) {
+ if (preg_match('/^que:(\d+)$/', (string)($opt['key'] ?? ''), $m)) {
+ $tabValidIds[] = (int)$m[1];
+ }
+ }
+ $tabSelected = array_values(array_intersect($tabSelected, $tabValidIds));
+
+ $tabOut['epreuves'][] = [
+ 'epr_id' => $epr_id,
+ 'epr_label' => fxBibAnomalyEprLabel($epr, $strLangue),
+ 'questions' => $tabQuestionOpts,
+ 'selected' => $tabSelected,
+ 'avec_bib' => (int)(fxInfosBibEpreuve($epr_id)['avec_bib'] ?? 0),
+ ];
+ }
+
+ return $tabOut;
+}
+
+/** MSIN-4433 — Parse sélections POST dist_print[epr_id][] = que_id. */
+function fxBibParseDistPrintSelectionsFromPost(array $post) {
+ $tabRaw = $post['dist_print'] ?? [];
+ if (!is_array($tabRaw)) {
+ return [];
+ }
+
+ $out = [];
+ foreach ($tabRaw as $mixEprId => $mixQueIds) {
+ $epr_id = (int)$mixEprId;
+ if ($epr_id <= 0) {
+ continue;
+ }
+ if (!is_array($mixQueIds)) {
+ $mixQueIds = [$mixQueIds];
+ }
+ $tabIds = [];
+ foreach ($mixQueIds as $mixQueId) {
+ $intQueId = (int)$mixQueId;
+ if ($intQueId > 0 && !in_array($intQueId, $tabIds, true)) {
+ $tabIds[] = $intQueId;
+ }
+ }
+ $out[$epr_id] = $tabIds;
+ }
+
+ return $out;
+}
+
+/**
+ * MSIN-4433 — Sauvegarde les questions cochées par épreuve (à la génération PDF).
+ * @return array{success: bool, message?: string}
+ */
+function fxBibSaveDistPrintSelections($int_eve_id, array $tabSelections, $strLangue = 'fr') {
+ global $objDatabase;
+
+ $int_eve_id = (int)$int_eve_id;
+ if ($int_eve_id <= 0) {
+ return ['success' => false, 'message' => fxBibMsg('bib_v4_ajax_epr_invalid')];
+ }
+
+ $intMax = fxBibDistPrintMaxQuestions();
+ $tabPanel = fxBibCollectDistPrintPanelData($int_eve_id, $strLangue);
+
+ foreach ($tabPanel['epreuves'] as $tabEpr) {
+ $epr_id = (int)($tabEpr['epr_id'] ?? 0);
+ if ($epr_id <= 0) {
+ continue;
+ }
+
+ $tabValidIds = [];
+ foreach ($tabEpr['questions'] as $opt) {
+ if (preg_match('/^que:(\d+)$/', (string)($opt['key'] ?? ''), $m)) {
+ $tabValidIds[] = (int)$m[1];
+ }
+ }
+
+ $tabChosen = $tabSelections[$epr_id] ?? [];
+ if (!is_array($tabChosen)) {
+ $tabChosen = [];
+ }
+
+ $tabFiltered = [];
+ foreach ($tabChosen as $intQueId) {
+ $intQueId = (int)$intQueId;
+ if ($intQueId > 0 && in_array($intQueId, $tabValidIds, true)
+ && !in_array($intQueId, $tabFiltered, true)) {
+ $tabFiltered[] = $intQueId;
+ }
+ }
+
+ if (count($tabFiltered) > $intMax) {
+ return [
+ 'success' => false,
+ 'message' => fxBibMsg('bib_v4_dist_print_max_questions', $intMax),
+ ];
+ }
+
+ $strCsv = fxBibDistPrintQueIdsToCsv($tabFiltered);
+ $sql = "
+ UPDATE inscriptions_epreuves
+ SET ba_bib_dist_que_ids = '" . $objDatabase->fxEscape($strCsv) . "'
+ WHERE epr_id = $epr_id
+ AND eve_id = $int_eve_id
+ LIMIT 1
+ ";
+ $objDatabase->fxQuery($sql);
+ }
+
+ return ['success' => true];
+}
+
+/**
+ * MSIN-4433 — Participants avec dossard (une ligne par no_bib), tri batch.
+ * @return array>
+ */
+function fxBibGetParticipantsForDistPrint($epr_id, $sort1, $sort2 = '', $strLangue = 'fr') {
+ global $objDatabase;
+
+ $epr_id = (int)$epr_id;
+ if ($epr_id <= 0) {
+ return [];
+ }
+
+ $strSort1 = fxBibNormalizeSortKey($sort1);
+ $strSort2 = fxBibNormalizeSortKey($sort2);
+ if ($strSort1 === '') {
+ return [];
+ }
+ if ($strSort1 === $strSort2) {
+ $strSort2 = '';
+ }
+
+ $sqlBibNumP = fxBibSqlNoBibUnsigned('p.no_bib');
+ $orderBy = fxBibBuildBatchOrderBy($epr_id, $strSort1, $strSort2, $strLangue);
+
+ $sql = "
+ SELECT
+ p.par_id,
+ p.pec_id,
+ p.no_bib,
+ p.par_nom,
+ p.par_prenom,
+ p.par_sexe
+ FROM resultats_participants p
+ LEFT JOIN resultats_epreuves_commandees c
+ ON c.pec_id_original = p.pec_id
+ AND c.epr_id = p.epr_id
+ WHERE p.epr_id = $epr_id
+ AND p.is_cancelled = 0
+ AND $sqlBibNumP > 0
+ AND p.rol_id NOT IN (3, 4)
+ ORDER BY $orderBy
+ ";
+
+ $rows = $objDatabase->fxGetResults($sql);
+ if (!is_array($rows)) {
+ return [];
+ }
+
+ $tabSeen = [];
+ $tabOut = [];
+ foreach ($rows as $row) {
+ $strBibKey = (string)(int)($row['no_bib'] ?? 0);
+ if ($strBibKey === '0' || isset($tabSeen[$strBibKey])) {
+ continue;
+ }
+ $tabSeen[$strBibKey] = true;
+ $tabOut[] = $row;
+ }
+
+ return $tabOut;
+}
+
+/**
+ * MSIN-4433 — Réponses questions pour une liste de participants.
+ * @return array> clé "par_id:pec_id" => [que_id => réponse]
+ */
+function fxBibGetDistPrintQuestionAnswers($epr_id, array $tabQueIds, array $tabParticipants, $strLangue = 'fr') {
+ global $objDatabase;
+
+ $epr_id = (int)$epr_id;
+ $tabQueIds = array_values(array_filter(array_map('intval', $tabQueIds)));
+ if ($epr_id <= 0 || empty($tabQueIds) || empty($tabParticipants)) {
+ return [];
+ }
+
+ $tabParIds = [];
+ foreach ($tabParticipants as $row) {
+ $intParId = (int)($row['par_id'] ?? 0);
+ if ($intParId > 0) {
+ $tabParIds[] = $intParId;
+ }
+ }
+ $tabParIds = array_values(array_unique($tabParIds));
+ if (empty($tabParIds)) {
+ return [];
+ }
+
+ $strCol = ($strLangue === 'en') ? 'que_choix_en' : 'que_choix_fr';
+ $strQueIds = implode(',', $tabQueIds);
+ $strParIds = implode(',', $tabParIds);
+
+ $sql = "
+ SELECT par_id, pec_id, que_id, TRIM($strCol) AS ans
+ FROM resultats_questions
+ WHERE que_actif = 1
+ AND que_id IN ($strQueIds)
+ AND par_id IN ($strParIds)
+ ";
+ $rows = $objDatabase->fxGetResults($sql);
+ if (!is_array($rows)) {
+ return [];
+ }
+
+ $out = [];
+ foreach ($rows as $row) {
+ $strKey = (int)($row['par_id'] ?? 0) . ':' . (int)($row['pec_id'] ?? 0);
+ $intQueId = (int)($row['que_id'] ?? 0);
+ $out[$strKey][$intQueId] = trim((string)($row['ans'] ?? ''));
+ }
+
+ return $out;
+}
+
+/** MSIN-4433 — Tronque texte pour cellule PDF. */
+function fxBibDistPrintTruncate($str, $intMax = 28) {
+ $str = trim((string)$str);
+ if ($str === '') {
+ return '';
+ }
+ if (function_exists('mb_strlen') && function_exists('mb_substr')) {
+ if (mb_strlen($str) <= $intMax) {
+ return $str;
+ }
+ return mb_substr($str, 0, max(1, $intMax - 1)) . '…';
+ }
+ if (strlen($str) <= $intMax) {
+ return $str;
+ }
+ return substr($str, 0, max(1, $intMax - 1)) . '…';
+}
+
+/** MSIN-4433 — Génère et envoie le PDF (format lettre). */
+function fxBibOutputDistPrintPdf($int_eve_id, $strLangue = 'fr', $strPrintedBy = '') {
+ global $objDatabase;
+
+ $int_eve_id = (int)$int_eve_id;
+ if ($int_eve_id <= 0) {
+ http_response_code(400);
+ exit;
+ }
+
+ $tabEvent = $objDatabase->fxGetRow("SELECT * FROM inscriptions_evenements WHERE eve_id = $int_eve_id LIMIT 1");
+ if (!is_array($tabEvent)) {
+ http_response_code(404);
+ exit;
+ }
+
+ $strEventTitle = trim((string)($tabEvent['eve_nom_' . $strLangue] ?? ''));
+ if ($strEventTitle === '') {
+ $strEventTitle = trim((string)($tabEvent['eve_nom_fr'] ?? ''));
+ }
+
+ $strPrintedAt = fxBibFormatDistPrintDateTime('', $strLangue);
+ if (trim((string)$strPrintedBy) === '') {
+ $strPrintedBy = fxBibGetCurrentPromoteurDisplayName();
+ }
+ $strFooter = sprintf(
+ fxBibTexte('bib_v4_dist_print_footer', 0),
+ $strPrintedAt,
+ $strPrintedBy
+ );
+
+ $tabPanel = fxBibCollectDistPrintPanelData($int_eve_id, $strLangue);
+
+ $strDocRoot = $_SERVER['DOCUMENT_ROOT'] ?? dirname(__DIR__);
+ require_once($strDocRoot . '/fpdf182/fpdf.php');
+
+ if (!class_exists('BibDistPrintPdf', false)) {
+ class BibDistPrintPdf extends FPDF {
+ public $footerText = '';
+
+ function Footer() {
+ $this->SetY(-12);
+ $this->SetFont('Arial', 'I', 7);
+ $this->Cell(0, 4, utf8_decode($this->footerText), 0, 0, 'L');
+ $this->Cell(0, 4, utf8_decode('p. ' . $this->PageNo()), 0, 0, 'R');
+ }
+ }
+ }
+
+ $pdf = new BibDistPrintPdf('P', 'mm', 'letter');
+ $pdf->footerText = $strFooter;
+ $pdf->SetMargins(10, 12, 10);
+ $pdf->SetAutoPageBreak(true, 14);
+ $pdf->AddPage();
+ $pdf->SetFont('Arial', 'B', 14);
+ $pdf->Cell(0, 8, utf8_decode(fxBibDistPrintTruncate($strEventTitle, 80)), 0, 1, 'C');
+ $pdf->SetFont('Arial', '', 10);
+ $pdf->Cell(0, 6, utf8_decode(fxBibTexte('bib_v4_dist_print_title', 0)), 0, 1, 'C');
+ $pdf->Ln(4);
+
+ $fltPageW = $pdf->GetPageWidth() - 20;
+ $fltFixedW = 18 + 42 + 42 + 12;
+
+ foreach ($tabPanel['epreuves'] as $tabEpr) {
+ $epr_id = (int)($tabEpr['epr_id'] ?? 0);
+ if ($epr_id <= 0) {
+ continue;
+ }
+
+ $tabEprRow = $objDatabase->fxGetRow("SELECT * FROM inscriptions_epreuves WHERE epr_id = $epr_id LIMIT 1");
+ if (!is_array($tabEprRow)) {
+ continue;
+ }
+
+ list($strSort1, $strSort2) = fxBibGetEpreuveDefaultSortKeys($tabEprRow);
+ $tabSelectedQue = $tabEpr['selected'] ?? [];
+ $tabQueCols = [];
+ foreach ($tabEpr['questions'] as $opt) {
+ if (preg_match('/^que:(\d+)$/', (string)($opt['key'] ?? ''), $m)) {
+ $intQueId = (int)$m[1];
+ if (in_array($intQueId, $tabSelectedQue, true)) {
+ $tabQueCols[] = [
+ 'que_id' => $intQueId,
+ 'label' => (string)($opt['label'] ?? ('Q' . $intQueId)),
+ ];
+ }
+ }
+ }
+
+ $intNbQ = count($tabQueCols);
+ $fltQTotal = max(0, $fltPageW - $fltFixedW);
+ $fltQEach = ($intNbQ > 0) ? ($fltQTotal / $intNbQ) : 0;
+
+ $pdf->SetFont('Arial', 'B', 11);
+ $pdf->Cell(0, 7, utf8_decode(fxBibDistPrintTruncate($tabEpr['epr_label'] ?? '', 90)), 0, 1, 'L');
+ $pdf->Ln(1);
+
+ $tabParticipants = fxBibGetParticipantsForDistPrint($epr_id, $strSort1, $strSort2, $strLangue);
+ if (empty($tabParticipants)) {
+ $pdf->SetFont('Arial', 'I', 9);
+ $pdf->Cell(0, 5, utf8_decode(fxBibTexte('bib_v4_dist_print_empty_epr', 0)), 0, 1, 'L');
+ $pdf->Ln(3);
+ continue;
+ }
+
+ $tabAnswers = fxBibGetDistPrintQuestionAnswers($epr_id, $tabSelectedQue, $tabParticipants, $strLangue);
+
+ $pdf->SetFont('Arial', 'B', 8);
+ $pdf->SetFillColor(230, 230, 230);
+ $pdf->Cell(18, 6, utf8_decode(fxBibTexte('bib_v4_dist_print_col_bib', 0)), 1, 0, 'C', true);
+ $pdf->Cell(42, 6, utf8_decode(fxBibTexte('bib_v4_dist_print_col_nom', 0)), 1, 0, 'L', true);
+ $pdf->Cell(42, 6, utf8_decode(fxBibTexte('bib_v4_dist_print_col_prenom', 0)), 1, 0, 'L', true);
+ $pdf->Cell(12, 6, utf8_decode(fxBibTexte('bib_v4_dist_print_col_sexe', 0)), 1, 0, 'C', true);
+ foreach ($tabQueCols as $i => $col) {
+ $blnLast = ($i === $intNbQ - 1);
+ $pdf->Cell($fltQEach, 6, utf8_decode(fxBibDistPrintTruncate($col['label'], 22)), 1, $blnLast ? 1 : 0, 'L', true);
+ }
+ if ($intNbQ === 0) {
+ $pdf->Ln();
+ }
+
+ $pdf->SetFont('Arial', '', 8);
+ foreach ($tabParticipants as $row) {
+ $strKey = (int)($row['par_id'] ?? 0) . ':' . (int)($row['pec_id'] ?? 0);
+ $pdf->Cell(18, 5, utf8_decode((string)(int)($row['no_bib'] ?? 0)), 1, 0, 'C');
+ $pdf->Cell(42, 5, utf8_decode(fxBibDistPrintTruncate($row['par_nom'] ?? '', 24)), 1, 0, 'L');
+ $pdf->Cell(42, 5, utf8_decode(fxBibDistPrintTruncate($row['par_prenom'] ?? '', 24)), 1, 0, 'L');
+ $pdf->Cell(12, 5, utf8_decode(fxBibDistPrintSexeLabel($row['par_sexe'] ?? '', $strLangue)), 1, 0, 'C');
+ foreach ($tabQueCols as $i => $col) {
+ $intQueId = (int)$col['que_id'];
+ $strAns = $tabAnswers[$strKey][$intQueId] ?? '';
+ $blnLast = ($i === $intNbQ - 1);
+ $pdf->Cell($fltQEach, 5, utf8_decode(fxBibDistPrintTruncate($strAns, 24)), 1, $blnLast ? 1 : 0, 'L');
+ }
+ if ($intNbQ === 0) {
+ $pdf->Ln();
+ }
+ }
+
+ $pdf->Ln(5);
+ }
+
+ $strFilename = 'distribution-dossards-' . $int_eve_id . '.pdf';
+ $pdf->Output('I', $strFilename);
+}
+
+/** MSIN-4433 — Coquille panneau impression (chargement AJAX). */
+function renderBibDistPrintPanelShell($int_eve_id, $strLangue = 'fr') {
+ global $vDomaine;
+
+ $int_eve_id = (int)$int_eve_id;
+ $strWait = fxBibTexte('bib_v4_js_loader_wait', 0);
+ if ($strWait === '' || $strWait === 'bib_v4_js_loader_wait') {
+ $strWait = ($strLangue === 'en') ? 'Loading...' : 'Chargement...';
+ }
+ $strRunnerSrc = $vDomaine . '/images/ms1-runner-loader.gif?v=' . _VERSION_CODE;
+
+ ob_start();
+ ?>
+
+
+
+
+
; ?>)
+
+
+
+
+
+
+