From 2d706fb62803322a0f0dca916ac25a9b523efcfd Mon Sep 17 00:00:00 2001 From: stephan Date: Wed, 22 Jul 2026 07:43:03 -0400 Subject: [PATCH] =?UTF-8?q?MSIN-4471=20=E2=80=94=20Enhance=20bib=20distrib?= =?UTF-8?q?ution=20document=20handling=20by=20adding=20new=20columns=20for?= =?UTF-8?q?=20participant=20names=20and=20checkboxes.=20Adjust=20maximum?= =?UTF-8?q?=20question=20display=20for=20improved=20readability.=20Update?= =?UTF-8?q?=20version=20code=20and=20documentation=20content=20for=20clari?= =?UTF-8?q?ty=20and=20usability.=20Increment=20version=20code=20to=204.72.?= =?UTF-8?q?861.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- php/inc_fx_promoteur.php | 136 ++++++++++++++++-------- php/inc_settings.php | 4 +- sql/MSIN-4471-bib-dist-print-doc.sql | 8 +- sql/MSIN-4471-bib-dist-print-layout.sql | 31 ++++++ 4 files changed, 127 insertions(+), 52 deletions(-) create mode 100644 sql/MSIN-4471-bib-dist-print-layout.sql diff --git a/php/inc_fx_promoteur.php b/php/inc_fx_promoteur.php index ef4ae8c..392f0e3 100644 --- a/php/inc_fx_promoteur.php +++ b/php/inc_fx_promoteur.php @@ -3706,6 +3706,9 @@ function fxBibStaticFallback($clef) { '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'], + // MSIN-4471 — Feuille bénévole : nom+prénom + case remise. + 'bib_v4_dist_print_col_nom_prenom' => ['fr' => 'Nom, Prénom', 'en' => 'Last name, First name'], + 'bib_v4_dist_print_col_check' => ['fr' => '', 'en' => ''], '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'], @@ -9049,9 +9052,9 @@ function fxResolveTeamBib( // MSIN-4433 — Impression PDF distribution des dossards // ============================================================================= -/** MSIN-4433 — Limite de colonnes questions (format lettre 8½ × 11). */ +/** MSIN-4433 / MSIN-4471 — Limite de colonnes questions (feuille bénévole lettre). */ function fxBibDistPrintMaxQuestions() { - return 5; + return 3; } /** MSIN-4433 — Nom affiché du compte connecté (promoteur ou superadmin). */ @@ -9323,10 +9326,8 @@ function fxBibSaveDistPrintSelections($int_eve_id, array $tabSelections, $strLan } if (count($tabFiltered) > $intMax) { - return [ - 'success' => false, - 'message' => fxBibMsg('bib_v4_dist_print_max_questions', $intMax), - ]; + // MSIN-4471 — Max abaissé à 3 : garder les N premières plutôt que bloquer. + $tabFiltered = array_slice($tabFiltered, 0, $intMax); } $strCsv = fxBibDistPrintQueIdsToCsv($tabFiltered); @@ -9344,7 +9345,9 @@ function fxBibSaveDistPrintSelections($int_eve_id, array $tabSelections, $strLan } /** - * MSIN-4433 — Participants avec dossard (une ligne par no_bib), tri batch. + * MSIN-4433 / MSIN-4471 — Participants avec dossard (une ligne par no_bib). + * Tri fixe nom + prénom : les bénévoles cherchent par nom à la table. + * ($sort1 / $sort2 conservés pour compat signature ; ignorés.) * @return array> */ function fxBibGetParticipantsForDistPrint($epr_id, $sort1, $sort2 = '', $strLangue = 'fr') { @@ -9355,17 +9358,9 @@ function fxBibGetParticipantsForDistPrint($epr_id, $sort1, $sort2 = '', $strLang 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); + // MSIN-4471 — Toujours alphabétique nom / prénom (pas le tri batch d'assignation). + $orderBy = 'TRIM(p.par_nom), TRIM(p.par_prenom), p.par_id ASC'; $sql = " SELECT @@ -9886,8 +9881,15 @@ function fxBibOutputDistPrintPdf($int_eve_id, $strLangue = 'fr', $strPrintedBy = // MSIN-4471 — Pages d'explication (doc) avant les tableaux d'épreuves. fxBibOutputDistPrintIntroPages($pdf, $int_eve_id, $strLangue); + // MSIN-4471 — Feuille bénévole : case remise + dossard gros + nom/prénom + ~20–25 lignes/page. $fltPageW = $pdf->GetPageWidth() - 20; - $fltFixedW = 18 + 42 + 42 + 12; + $fltColCheck = 9; + $fltColBib = 28; + $fltColSexe = 12; + $fltFixedW = $fltColCheck + $fltColBib + $fltColSexe; + $fltRowH = 8; + $fltHeadH = 8; + $fltBottom = method_exists($pdf, 'GetBreakMargin') ? $pdf->GetBreakMargin() : 14; $blnHasPrintedEpreuve = false; foreach ($tabPanel['epreuves'] as $tabEpr) { @@ -9901,8 +9903,8 @@ function fxBibOutputDistPrintPdf($int_eve_id, $strLangue = 'fr', $strPrintedBy = continue; } - list($strSort1, $strSort2) = fxBibGetEpreuveDefaultSortKeys($tabEprRow); - $tabParticipants = fxBibGetParticipantsForDistPrint($epr_id, $strSort1, $strSort2, $strLangue); + // MSIN-4471 — Tri nom/prénom forcé dans fxBibGetParticipantsForDistPrint (args ignorés). + $tabParticipants = fxBibGetParticipantsForDistPrint($epr_id, 'ba:2', '', $strLangue); // MSIN-4433 — Épreuve sans dossard assigné : ne pas imprimer la section. if (empty($tabParticipants)) { continue; @@ -9921,51 +9923,93 @@ function fxBibOutputDistPrintPdf($int_eve_id, $strLangue = 'fr', $strPrintedBy = } } } + // MSIN-4471 — Max 3 questions affichées (lisibilité feuille bénévole). + if (count($tabQueCols) > fxBibDistPrintMaxQuestions()) { + $tabQueCols = array_slice($tabQueCols, 0, fxBibDistPrintMaxQuestions()); + } $intNbQ = count($tabQueCols); - $fltQTotal = max(0, $fltPageW - $fltFixedW); - $fltQEach = ($intNbQ > 0) ? ($fltQTotal / $intNbQ) : 0; + $fltNameW = 78; + $fltQTotal = max(0, $fltPageW - $fltFixedW - $fltNameW); + if ($intNbQ === 0) { + $fltNameW = $fltPageW - $fltFixedW; + $fltQEach = 0; + } else { + $fltQEach = $fltQTotal / $intNbQ; + } - // MSIN-4470 — Chaque épreuve commence sur une page neuve; Header répète les titres. $pdf->eprTitle = fxBibDistPrintTruncate($tabEpr['epr_label'] ?? '', 90); $pdf->AddPage(); $blnHasPrintedEpreuve = true; $tabAnswers = fxBibGetDistPrintQuestionAnswers($epr_id, $tabSelectedQue, $tabParticipants, $strLangue); - $pdf->SetFont('Arial', 'B', 8); - $pdf->SetFillColor(230, 230, 230); - $pdf->Cell(18, 6, fxBibDistPrintPdfText(fxBibTexte('bib_v4_dist_print_col_bib', 0)), 1, 0, 'C', true); - $pdf->Cell(42, 6, fxBibDistPrintPdfText(fxBibTexte('bib_v4_dist_print_col_nom', 0)), 1, 0, 'L', true); - $pdf->Cell(42, 6, fxBibDistPrintPdfText(fxBibTexte('bib_v4_dist_print_col_prenom', 0)), 1, 0, 'L', true); - $pdf->Cell(12, 6, fxBibDistPrintPdfText(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, fxBibDistPrintPdfText(fxBibDistPrintTruncate($col['label'], 22)), 1, $blnLast ? 1 : 0, 'L', true); - } - if ($intNbQ === 0) { - $pdf->Ln(); - } + $fnDrawTableHeader = function () use ($pdf, $fltColCheck, $fltColBib, $fltNameW, $fltColSexe, $fltQEach, $tabQueCols, $intNbQ, $fltHeadH) { + $pdf->SetFont('Arial', 'B', 10); + $pdf->SetFillColor(220, 220, 220); + $pdf->Cell($fltColCheck, $fltHeadH, fxBibDistPrintPdfText(fxBibTexte('bib_v4_dist_print_col_check', 0)), 1, 0, 'C', true); + $pdf->Cell($fltColBib, $fltHeadH, fxBibDistPrintPdfText(fxBibTexte('bib_v4_dist_print_col_bib', 0)), 1, 0, 'C', true); + $pdf->Cell($fltNameW, $fltHeadH, fxBibDistPrintPdfText(fxBibTexte('bib_v4_dist_print_col_nom_prenom', 0)), 1, 0, 'L', true); + $pdf->Cell($fltColSexe, $fltHeadH, fxBibDistPrintPdfText(fxBibTexte('bib_v4_dist_print_col_sexe', 0)), 1, $intNbQ === 0 ? 1 : 0, 'C', true); + foreach ($tabQueCols as $i => $col) { + $blnLast = ($i === $intNbQ - 1); + $pdf->Cell($fltQEach, $fltHeadH, fxBibDistPrintPdfText(fxBibDistPrintTruncate($col['label'], 18)), 1, $blnLast ? 1 : 0, 'L', true); + } + }; - $pdf->SetFont('Arial', '', 8); + $fnDrawTableHeader(); + + $intRow = 0; foreach ($tabParticipants as $row) { + // Nouvelle page + répéter l'en-tête de colonnes (feuille bénévole). + if ($pdf->GetY() + $fltRowH > $pdf->GetPageHeight() - $fltBottom) { + $pdf->AddPage(); + $fnDrawTableHeader(); + } + $strKey = fxBibDistPrintAnswerKey($row); - $pdf->Cell(18, 5, fxBibDistPrintPdfText((string)(int)($row['no_bib'] ?? 0)), 1, 0, 'C'); - $pdf->Cell(42, 5, fxBibDistPrintPdfText(fxBibDistPrintTruncate($row['par_nom'] ?? '', 24)), 1, 0, 'L'); - $pdf->Cell(42, 5, fxBibDistPrintPdfText(fxBibDistPrintTruncate($row['par_prenom'] ?? '', 24)), 1, 0, 'L'); - $pdf->Cell(12, 5, fxBibDistPrintPdfText(fxBibDistPrintSexeLabel($row['par_sexe'] ?? '', $strLangue)), 1, 0, 'C'); + $strNom = trim((string)($row['par_nom'] ?? '')); + $strPrenom = trim((string)($row['par_prenom'] ?? '')); + $strNomPrenom = $strNom; + if ($strPrenom !== '') { + $strNomPrenom .= ($strNom !== '' ? ', ' : '') . $strPrenom; + } + + $blnAlt = ($intRow % 2) === 1; + if ($blnAlt) { + $pdf->SetFillColor(245, 245, 245); + } else { + $pdf->SetFillColor(255, 255, 255); + } + $blnFill = true; + + // Case à cocher (vide) pour marquer la remise. + $pdf->SetFont('Arial', '', 11); + $pdf->Cell($fltColCheck, $fltRowH, '', 1, 0, 'C', $blnFill); + + // Dossard — signal principal. + $pdf->SetFont('Arial', 'B', 14); + $pdf->Cell($fltColBib, $fltRowH, fxBibDistPrintPdfText((string)(int)($row['no_bib'] ?? 0)), 1, 0, 'C', $blnFill); + + // Nom, Prénom. + $pdf->SetFont('Arial', '', 11); + $pdf->Cell($fltNameW, $fltRowH, fxBibDistPrintPdfText(fxBibDistPrintTruncate($strNomPrenom, 42)), 1, 0, 'L', $blnFill); + + $pdf->SetFont('Arial', '', 11); + $pdf->Cell($fltColSexe, $fltRowH, fxBibDistPrintPdfText(fxBibDistPrintSexeLabel($row['par_sexe'] ?? '', $strLangue)), 1, $intNbQ === 0 ? 1 : 0, 'C', $blnFill); + foreach ($tabQueCols as $i => $col) { $intQueId = (int)$col['que_id']; $strAns = $tabAnswers[$strKey][$intQueId] ?? ''; $blnLast = ($i === $intNbQ - 1); - $pdf->Cell($fltQEach, 5, fxBibDistPrintPdfText(fxBibDistPrintTruncate($strAns, 24)), 1, $blnLast ? 1 : 0, 'L'); - } - if ($intNbQ === 0) { - $pdf->Ln(); + $pdf->SetFont('Arial', '', 10); + $pdf->Cell($fltQEach, $fltRowH, fxBibDistPrintPdfText(fxBibDistPrintTruncate($strAns, 20)), 1, $blnLast ? 1 : 0, 'L', $blnFill); } + + $intRow++; } - $pdf->Ln(5); + $pdf->Ln(4); } if (!$blnHasPrintedEpreuve && $pdf->PageNo() < 1) { diff --git a/php/inc_settings.php b/php/inc_settings.php index 10defe6..0566e05 100644 --- a/php/inc_settings.php +++ b/php/inc_settings.php @@ -7,8 +7,8 @@ * Constantes * * **************/ -define('_VERSION_CODE', '4.72.860'); // MSIN-4470 -define('_DATE_CODE', '2026-07-21'); +define('_VERSION_CODE', '4.72.861'); // MSIN-4471 +define('_DATE_CODE', '2026-07-22'); //MSIN-4290 define('QR_SECRET_KEY', 'ms1_qr_2026_cle_secrete_longue_et_fixe'); diff --git a/sql/MSIN-4471-bib-dist-print-doc.sql b/sql/MSIN-4471-bib-dist-print-doc.sql index 761870b..a36dcdd 100644 --- a/sql/MSIN-4471-bib-dist-print-doc.sql +++ b/sql/MSIN-4471-bib-dist-print-doc.sql @@ -19,8 +19,8 @@ VALUES ('bib_v4_dist_print_doc', 'bib_dist_print', 'compte.php', 1, NOW()); -- FR -- --------------------------------------------------------------------------- INSERT INTO `doc_page` (`doc_mod_clef`, `doc_page_clef`, `doc_langue`, `doc_titre`, `doc_sous_titre`, `doc_html`, `doc_prg`, `doc_tri`, `doc_actif`, `doc_creation`) VALUES -('bib_dist_print', 'bib_dist_print_reco', 'fr', 'Recommandations — distribution des dossards', 'À lire avant de commencer', -'

Texte provisoire (MSIN-4471) — Remplacez ce contenu via l''éditeur de documentation.

Règles de base

  1. Vérifiez l''identité du participant avant de remettre le dossard.
  2. Cochez ou rayez le numéro sur la liste imprimée après chaque remise.
  3. Gardez les dossards non remis dans un bac séparé clairement étiqueté.

Organisation de la table

  • Une personne pour la liste, une pour les dossards.
  • Préparez les épreuves dans l''ordre d''arrivée des files.
Astuce images : déposez vos fichiers dans images/doc/ puis utilisez <img src="/images/doc/nom-fichier.png">.
', +('bib_dist_print', 'bib_dist_print_reco', 'fr', 'Conseils à la distribution', 'Pour une remise de dossards sans erreur', +'

À lire avant de commencer. Quelques secondes à chaque remise évitent les échanges, les erreurs de chronométrage et les résultats incorrects.

1. Familles et groupes — plusieurs dossards

Lorsque vous remettez plusieurs dossards à une même famille ou à un groupe, assurez-vous que le prénom figure sur chaque dossard. S''il n''y est pas déjà, écrivez-le au verso. Ainsi, chacun part avec le bon numéro : un échange entre proches fausse le chronométrage et les résultats.

2. Ne pas céder son dossard

Un dossard inscrit à un nom ne doit pas être remis à une autre personne. Si un échange a déjà eu lieu, avisez immédiatement les organisateurs afin qu''ils corrigent la base de données avant la course.

3. Dossard bien visible à l''avant

Rappelez au participant d''épingler son dossard bien visible à l''avant du torse. C''est essentiel pour un chronométrage fiable et un bon temps officiel.

4. Vérifier ses infos avant la course (code QR)

Invitez les participants à scanner le code QR de l''événement pour consulter les résultats en ligne avant le départ. Ils pourront confirmer que leur nom, leur âge et leur numéro de dossard sont correctement associés à leur inscription.

', 'compte.php', 10, 1, NOW()), ('bib_dist_print', 'bib_dist_print_table', 'fr', 'Affiche table — participants', 'À placer sur la table de distribution', '

Texte provisoire — Page destinée à être lue par les participants à la table.

À faire avant de vous présenter

  • Ayez votre confirmation d''inscription ou une pièce d''identité.
  • Connaissez le nom de votre épreuve (ex. 10 km, demi-marathon).
  • Si vous êtes en équipe, présentez-vous ensemble si possible.

À la table

  1. Donnez votre nom de famille clairement.
  2. Vérifiez le numéro de dossard avant de quitter la table.
  3. Épinglez le dossard bien visible à l''avant.

Vous pouvez ajouter une image d''exemple ici, par ex. :

Exemple d''affichage

', @@ -33,8 +33,8 @@ INSERT INTO `doc_page` (`doc_mod_clef`, `doc_page_clef`, `doc_langue`, `doc_titr -- EN -- --------------------------------------------------------------------------- INSERT INTO `doc_page` (`doc_mod_clef`, `doc_page_clef`, `doc_langue`, `doc_titre`, `doc_sous_titre`, `doc_html`, `doc_prg`, `doc_tri`, `doc_actif`, `doc_creation`) VALUES -('bib_dist_print', 'bib_dist_print_reco', 'en', 'Recommendations — bib distribution', 'Read before you start', -'

Placeholder text (MSIN-4471) — Replace this content in the documentation editor.

Basic rules

  1. Verify the participant''s identity before handing out the bib.
  2. Check or cross off the number on the printed list after each handout.
  3. Keep unclaimed bibs in a clearly labeled separate bin.

Table setup

  • One person on the list, one on the bibs.
  • Prepare races in the order of the queues.
Image tip: place files under images/doc/ then use <img src="/images/doc/filename.png">.
', +('bib_dist_print', 'bib_dist_print_reco', 'en', 'Distribution tips', 'For a smooth, error-free bib handout', +'

Read before you start. A few seconds at each handout prevent swaps, timing errors, and incorrect results.

1. Families and groups — multiple bibs

When handing out several bibs to the same family or group, make sure each bib shows a first name. If it is not already there, write it on the back. This way everyone leaves with the right number: a swap between relatives will skew timing and results.

2. Do not give away your bib

A bib registered under one name must not be given to someone else. If a swap has already happened, notify the organizers immediately so they can correct the database before the race.

3. Bib clearly visible on the front

Remind the participant to pin the bib clearly visible on the front of the torso. This is essential for reliable timing and an accurate official time.

4. Check details before the race (QR code)

Invite participants to scan the event QR code and view results online before the start. They can confirm that their name, age, and bib number are correctly linked to their registration.

', 'compte.php', 10, 1, NOW()), ('bib_dist_print', 'bib_dist_print_table', 'en', 'Table sign — participants', 'Place on the distribution table', '

Placeholder — Page meant to be read by participants at the table.

Before you come up

  • Have your registration confirmation or ID ready.
  • Know your race name (e.g. 10K, half marathon).
  • If you are a team, come together if possible.

At the table

  1. Say your last name clearly.
  2. Check the bib number before leaving the table.
  3. Pin the bib clearly on the front.

You can add a sample image here, e.g.:

Sample display

', diff --git a/sql/MSIN-4471-bib-dist-print-layout.sql b/sql/MSIN-4471-bib-dist-print-layout.sql new file mode 100644 index 0000000..4191867 --- /dev/null +++ b/sql/MSIN-4471-bib-dist-print-layout.sql @@ -0,0 +1,31 @@ +-- MSIN-4471 — Libellés feuille bénévole PDF distribution (nom+prénom, case remise) +-- Prérequis : clés bib_v4_dist_print_* (MSIN-4433) +-- Idempotent : INSERT si absent ; UPDATE libellé max questions si présent + +SET NAMES utf8mb4; + +INSERT INTO info (info_clef, info_langue, info_texte, info_aide, info_prg, info_section, info_tri, info_actif, info_type, info_css, info_js, info_creation) +SELECT 'bib_v4_dist_print_col_nom_prenom', 'fr', 'Nom, Prénom', '', 'compte.php', '', 0, 1, '', '', '', NOW() +FROM DUAL WHERE NOT EXISTS ( + SELECT 1 FROM info WHERE info_clef = 'bib_v4_dist_print_col_nom_prenom' AND info_langue = 'fr' AND info_prg = 'compte.php' +); + +INSERT INTO info (info_clef, info_langue, info_texte, info_aide, info_prg, info_section, info_tri, info_actif, info_type, info_css, info_js, info_creation) +SELECT 'bib_v4_dist_print_col_nom_prenom', 'en', 'Last name, First name', '', 'compte.php', '', 0, 1, '', '', '', NOW() +FROM DUAL WHERE NOT EXISTS ( + SELECT 1 FROM info WHERE info_clef = 'bib_v4_dist_print_col_nom_prenom' AND info_langue = 'en' AND info_prg = 'compte.php' +); + +INSERT INTO info (info_clef, info_langue, info_texte, info_aide, info_prg, info_section, info_tri, info_actif, info_type, info_css, info_js, info_creation) +SELECT 'bib_v4_dist_print_col_check', 'fr', '', '', 'compte.php', '', 0, 1, '', '', '', NOW() +FROM DUAL WHERE NOT EXISTS ( + SELECT 1 FROM info WHERE info_clef = 'bib_v4_dist_print_col_check' AND info_langue = 'fr' AND info_prg = 'compte.php' +); + +INSERT INTO info (info_clef, info_langue, info_texte, info_aide, info_prg, info_section, info_tri, info_actif, info_type, info_css, info_js, info_creation) +SELECT 'bib_v4_dist_print_col_check', 'en', '', '', 'compte.php', '', 0, 1, '', '', '', NOW() +FROM DUAL WHERE NOT EXISTS ( + SELECT 1 FROM info WHERE info_clef = 'bib_v4_dist_print_col_check' AND info_langue = 'en' AND info_prg = 'compte.php' +); + +-- Le message max questions utilise %d via PHP (fxBibDistPrintMaxQuestions = 3) — pas de texte figé ici.