441 lines
14 KiB
PHP
441 lines
14 KiB
PHP
<?php
|
||
/**
|
||
* MSIN-4512 — PDF distribution dossards : colonnes de base, check-in, orientation listes.
|
||
* Complète les fonctions DistPrint de php/inc_fx_promoteur.php.
|
||
*/
|
||
|
||
/** MSIN-4512 — Orientation listes : P portrait / L paysage. */
|
||
function fxBibDistPrintNormalizeOrient($strOrient) {
|
||
$strOrient = strtoupper(trim((string)$strOrient));
|
||
return ($strOrient === 'L') ? 'L' : 'P';
|
||
}
|
||
|
||
/** MSIN-4512 — Largeur utile page lettre (mm), hors marges 10+10. */
|
||
function fxBibDistPrintUsableWidthMm($strOrient) {
|
||
$strOrient = fxBibDistPrintNormalizeOrient($strOrient);
|
||
// Letter : 215.9 × 279.4 mm
|
||
return ($strOrient === 'L') ? 259.4 : 195.9;
|
||
}
|
||
|
||
/** MSIN-4512 — Réservé dossard + nom/prénom (mm). */
|
||
function fxBibDistPrintMandatoryWidthMm() {
|
||
return 22 + 55;
|
||
}
|
||
|
||
/** MSIN-4512 — Budget mm pour colonnes optionnelles. */
|
||
function fxBibDistPrintOptionalBudgetMm($strOrient) {
|
||
return max(20, (int)floor(fxBibDistPrintUsableWidthMm($strOrient) - fxBibDistPrintMandatoryWidthMm()));
|
||
}
|
||
|
||
/**
|
||
* MSIN-4512 — Largeur estimée d'une colonne optionnelle (mm).
|
||
*/
|
||
function fxBibDistPrintColEstWidthMm($strKey) {
|
||
$strKey = trim((string)$strKey);
|
||
if ($strKey === '') {
|
||
return 0;
|
||
}
|
||
if (preg_match('/^que:\d+$/', $strKey)) {
|
||
return 28;
|
||
}
|
||
static $tab = [
|
||
'no_bib' => 22,
|
||
'par_prenom_nom' => 55,
|
||
'col_check' => 10,
|
||
'no_bib_remis' => 14,
|
||
'par_sexe' => 12,
|
||
'par_age' => 12,
|
||
'par_naissance' => 26,
|
||
'par_ville' => 32,
|
||
'par_telephone1' => 30,
|
||
'par_telephone2' => 30,
|
||
'par_courriel' => 40,
|
||
'par_adresse' => 40,
|
||
'par_adresse2' => 36,
|
||
'par_codepostal' => 18,
|
||
'pay_nom' => 28,
|
||
'pay_id' => 28,
|
||
'pay_iso' => 14,
|
||
'nom_equipe' => 32,
|
||
'par_nom_equipe' => 32,
|
||
'par_contact_urgence_nom' => 32,
|
||
'par_contact_urgence_telephone' => 30,
|
||
];
|
||
if (isset($tab[$strKey])) {
|
||
return $tab[$strKey];
|
||
}
|
||
return 28;
|
||
}
|
||
|
||
/** MSIN-4512 — Somme des largeurs estimées. */
|
||
function fxBibDistPrintColsWidthSumMm(array $tabKeys) {
|
||
$flt = 0;
|
||
foreach ($tabKeys as $strKey) {
|
||
$flt += fxBibDistPrintColEstWidthMm($strKey);
|
||
}
|
||
return (int)ceil($flt);
|
||
}
|
||
|
||
/**
|
||
* MSIN-4512 — Parse CSV de clés colonnes (col_check,par_sexe,que:12,…).
|
||
* @return array<int, string>
|
||
*/
|
||
function fxBibParseDistPrintColKeys($strCsv) {
|
||
if ($strCsv === null || trim((string)$strCsv) === '') {
|
||
return [];
|
||
}
|
||
$out = [];
|
||
foreach (explode(',', (string)$strCsv) as $part) {
|
||
$str = trim($part);
|
||
if ($str === '') {
|
||
continue;
|
||
}
|
||
if (preg_match('/^que:(\d+)$/', $str, $m)) {
|
||
$str = 'que:' . (int)$m[1];
|
||
} elseif (preg_match('/^par_[a-z0-9_]+$/i', $str)) {
|
||
$str = strtolower($str);
|
||
} elseif (preg_match('/^(no_bib|col_check|no_bib_remis|pay_nom|pay_iso|pay_id|nom_equipe)$/i', $str)) {
|
||
$str = strtolower($str);
|
||
} else {
|
||
continue;
|
||
}
|
||
if (!in_array($str, $out, true)) {
|
||
$out[] = $str;
|
||
}
|
||
}
|
||
return $out;
|
||
}
|
||
|
||
/** MSIN-4512 — Sérialise les clés colonnes. */
|
||
function fxBibDistPrintColKeysToCsv(array $tabKeys) {
|
||
return implode(',', fxBibParseDistPrintColKeys(implode(',', $tabKeys)));
|
||
}
|
||
|
||
/**
|
||
* MSIN-4512 — Colonnes toujours présentes sur le PDF (ordre choisissable, non retirables).
|
||
* @return array<int, string>
|
||
*/
|
||
function fxBibDistPrintMandatoryKeys() {
|
||
// Défaut historique PDF : Nom/Prénom puis Dossard.
|
||
return ['par_prenom_nom', 'no_bib'];
|
||
}
|
||
|
||
/** MSIN-4512 — true si clé obligatoire (budget optionnel non consommé). */
|
||
function fxBibDistPrintIsMandatoryKey($strKey) {
|
||
return in_array(trim((string)$strKey), fxBibDistPrintMandatoryKeys(), true);
|
||
}
|
||
|
||
/**
|
||
* MSIN-4512 — Garantit les clés obligatoires dans l’ordre (insère le défaut si absentes).
|
||
* @param array<int, string> $tabKeys
|
||
* @return array<int, string>
|
||
*/
|
||
function fxBibDistPrintEnsureMandatoryInOrder(array $tabKeys) {
|
||
$tabOut = [];
|
||
foreach ($tabKeys as $strKey) {
|
||
$strKey = trim((string)$strKey);
|
||
if ($strKey === '' || in_array($strKey, $tabOut, true)) {
|
||
continue;
|
||
}
|
||
$tabOut[] = $strKey;
|
||
}
|
||
$tabMissing = [];
|
||
foreach (fxBibDistPrintMandatoryKeys() as $strMust) {
|
||
if (!in_array($strMust, $tabOut, true)) {
|
||
$tabMissing[] = $strMust;
|
||
}
|
||
}
|
||
if (!empty($tabMissing)) {
|
||
$tabOut = array_merge($tabMissing, $tabOut);
|
||
}
|
||
return $tabOut;
|
||
}
|
||
|
||
/**
|
||
* MSIN-4512 — Catalogue colonnes de base proposables (hors questions).
|
||
* Même source fiche / Production Excel + case OK + check-in.
|
||
*
|
||
* @return array<int, array{key:string,label:string,width:int}>
|
||
*/
|
||
function fxBibDistPrintCollectBaseFieldOptions($int_eve_id, $strLangue = 'fr') {
|
||
$int_eve_id = (int)$int_eve_id;
|
||
$strLangue = ($strLangue === 'en') ? 'en' : 'fr';
|
||
$tabOut = [];
|
||
$tabSeen = [];
|
||
|
||
$fnAdd = function ($strKey, $strLabel) use (&$tabOut, &$tabSeen) {
|
||
$strKey = trim((string)$strKey);
|
||
$strLabel = trim((string)$strLabel);
|
||
if ($strKey === '' || isset($tabSeen[$strKey])) {
|
||
return;
|
||
}
|
||
// Obligatoires PDF : pas dans les chips Disponibles (déjà dans l’ordre).
|
||
if (in_array($strKey, ['par_nom', 'par_prenom', 'par_prenom_nom', 'no_bib'], true)) {
|
||
return;
|
||
}
|
||
$tabSeen[$strKey] = true;
|
||
$tabOut[] = [
|
||
'key' => $strKey,
|
||
'label' => $strLabel !== '' ? $strLabel : $strKey,
|
||
'width' => fxBibDistPrintColEstWidthMm($strKey),
|
||
];
|
||
};
|
||
|
||
$fnAdd('col_check', fxBibTexte('bib_v4_dist_print_col_check_opt', 0));
|
||
$fnAdd('no_bib_remis', fxBibTexte('bib_v4_dist_print_col_checkin', 0));
|
||
|
||
if (!function_exists('fxBibProductionGetEventInfoFields')) {
|
||
$strProd = __DIR__ . '/inc_fx_bib_production.php';
|
||
if (is_file($strProd)) {
|
||
require_once $strProd;
|
||
}
|
||
}
|
||
|
||
$blnHasSexe = false;
|
||
if ($int_eve_id > 0 && function_exists('fxBibProductionGetEventInfoFields')) {
|
||
foreach (fxBibProductionGetEventInfoFields($int_eve_id) as $tabInfo) {
|
||
$strCha = trim((string)($tabInfo['cha_nom'] ?? ''));
|
||
if ($strCha === '') {
|
||
continue;
|
||
}
|
||
if ($strCha === 'par_sexe') {
|
||
$blnHasSexe = true;
|
||
}
|
||
$strLabel = trim((string)($tabInfo['cha_label_' . $strLangue] ?? ''));
|
||
if ($strLabel === '') {
|
||
$strLabel = trim((string)($tabInfo['cha_label_fr'] ?? ''));
|
||
}
|
||
$fnAdd($strCha, $strLabel);
|
||
}
|
||
}
|
||
|
||
if (!$blnHasSexe) {
|
||
$fnAdd('par_sexe', fxBibTexte('bib_v4_dist_print_col_sexe', 0));
|
||
}
|
||
|
||
// Pays : libellé (pas l'id brut) — comme Production Excel.
|
||
$fnAdd('pay_nom', fxBibTexte('bib_v5_production_field_pay_nom', 0) ?: (($strLangue === 'en') ? 'Country' : 'Pays'));
|
||
$fnAdd('nom_equipe', fxBibTexte('bib_v5_production_field_nom_equipe', 0) ?: (($strLangue === 'en') ? 'Team name' : 'Nom d\'équipe'));
|
||
|
||
return $tabOut;
|
||
}
|
||
|
||
/**
|
||
* MSIN-4512 — Sélection mémorisée (ba_bib_dist_cols) avec repli legacy que_ids.
|
||
* @return array<int, string>
|
||
*/
|
||
function fxBibDistPrintResolveSelectedCols(array $eprRow, array $tabValidKeys) {
|
||
$tabValid = [];
|
||
foreach ($tabValidKeys as $strKey) {
|
||
$tabValid[$strKey] = true;
|
||
}
|
||
// Obligatoires toujours valides (ordre PDF).
|
||
foreach (fxBibDistPrintMandatoryKeys() as $strMust) {
|
||
$tabValid[$strMust] = true;
|
||
}
|
||
|
||
$strCols = trim((string)($eprRow['ba_bib_dist_cols'] ?? ''));
|
||
if ($strCols !== '') {
|
||
$tabSel = fxBibParseDistPrintColKeys($strCols);
|
||
} else {
|
||
// Legacy : case OK + sexe (comportement PDF d’avant) + questions CSV.
|
||
$tabSel = array_merge(fxBibDistPrintMandatoryKeys(), ['col_check', 'par_sexe']);
|
||
foreach (fxBibParseDistPrintQueIds($eprRow['ba_bib_dist_que_ids'] ?? '') as $intQueId) {
|
||
$tabSel[] = 'que:' . (int)$intQueId;
|
||
}
|
||
}
|
||
|
||
$tabOut = [];
|
||
foreach ($tabSel as $strKey) {
|
||
if (isset($tabValid[$strKey]) && !in_array($strKey, $tabOut, true)) {
|
||
$tabOut[] = $strKey;
|
||
}
|
||
}
|
||
return fxBibDistPrintEnsureMandatoryInOrder($tabOut);
|
||
}
|
||
|
||
/** MSIN-4512 — Orientation mémorisée sur l’événement. */
|
||
function fxBibGetDistPrintOrient($int_eve_id) {
|
||
global $objDatabase;
|
||
|
||
$int_eve_id = (int)$int_eve_id;
|
||
if ($int_eve_id <= 0) {
|
||
return 'P';
|
||
}
|
||
// Colonne absente tant que sql/MSIN-4512 n’est pas passé → portrait.
|
||
static $blnHasCol = null;
|
||
if ($blnHasCol === null) {
|
||
$blnHasCol = ((int)$objDatabase->fxGetVar(
|
||
"SELECT COUNT(*) FROM information_schema.COLUMNS
|
||
WHERE TABLE_SCHEMA = DATABASE()
|
||
AND TABLE_NAME = 'inscriptions_evenements'
|
||
AND COLUMN_NAME = 'eve_bib_dist_print_orient'"
|
||
) > 0);
|
||
}
|
||
if (!$blnHasCol) {
|
||
return 'P';
|
||
}
|
||
$str = $objDatabase->fxGetVar(
|
||
"SELECT eve_bib_dist_print_orient FROM inscriptions_evenements WHERE eve_id = $int_eve_id LIMIT 1"
|
||
);
|
||
return fxBibDistPrintNormalizeOrient($str);
|
||
}
|
||
|
||
/** MSIN-4512 — Persiste orientation listes. */
|
||
function fxBibSaveDistPrintOrient($int_eve_id, $strOrient) {
|
||
global $objDatabase;
|
||
|
||
$int_eve_id = (int)$int_eve_id;
|
||
if ($int_eve_id <= 0) {
|
||
return;
|
||
}
|
||
$strOrient = fxBibDistPrintNormalizeOrient($strOrient);
|
||
static $blnHasCol = null;
|
||
if ($blnHasCol === null) {
|
||
$blnHasCol = ((int)$objDatabase->fxGetVar(
|
||
"SELECT COUNT(*) FROM information_schema.COLUMNS
|
||
WHERE TABLE_SCHEMA = DATABASE()
|
||
AND TABLE_NAME = 'inscriptions_evenements'
|
||
AND COLUMN_NAME = 'eve_bib_dist_print_orient'"
|
||
) > 0);
|
||
}
|
||
if (!$blnHasCol) {
|
||
return;
|
||
}
|
||
$objDatabase->fxQuery(
|
||
"UPDATE inscriptions_evenements
|
||
SET eve_bib_dist_print_orient = '" . $objDatabase->fxEscape($strOrient) . "'
|
||
WHERE eve_id = $int_eve_id
|
||
LIMIT 1"
|
||
);
|
||
}
|
||
|
||
/**
|
||
* MSIN-4512 — Valeur cellule PDF pour une clé optionnelle.
|
||
* MSIN-4554 — nom_equipe : pec (comme assignation) ; que: déjà enrichies en équipe via fxBibGetDistPrintQuestionAnswers.
|
||
*/
|
||
function fxBibDistPrintResolveColValue($strKey, array $row, array $tabAnswersByQue, $strLangue = 'fr') {
|
||
$strKey = trim((string)$strKey);
|
||
$strLangue = ($strLangue === 'en') ? 'en' : 'fr';
|
||
|
||
if ($strKey === 'col_check') {
|
||
return '';
|
||
}
|
||
if ($strKey === 'no_bib_remis') {
|
||
return ((int)($row['no_bib_remis'] ?? 0) === 1)
|
||
? fxBibTexte('bib_v4_dist_print_checkin_yes', 0)
|
||
: '';
|
||
}
|
||
if ($strKey === 'par_sexe') {
|
||
return fxBibDistPrintSexeLabel($row['par_sexe'] ?? '', $strLangue);
|
||
}
|
||
// MSIN-4554 — Champ cœur : même priorité que l'écran d'assignation.
|
||
if ($strKey === 'nom_equipe') {
|
||
$strPec = trim((string)($row['pec_nom_equipe'] ?? ''));
|
||
if ($strPec !== '') {
|
||
return $strPec;
|
||
}
|
||
return trim((string)($row['par_nom_equipe'] ?? ''));
|
||
}
|
||
if (preg_match('/^que:(\d+)$/', $strKey, $m)) {
|
||
// MSIN-4554 — Sur épreuve équipe les réponses (dont nom d'équipe) sont
|
||
// déjà enrichies dans fxBibGetDistPrintQuestionAnswers.
|
||
return trim((string)($tabAnswersByQue[(int)$m[1]] ?? ''));
|
||
}
|
||
|
||
if (!function_exists('fxBibProductionResolveCellValue')) {
|
||
$strProd = __DIR__ . '/inc_fx_bib_production.php';
|
||
if (is_file($strProd)) {
|
||
require_once $strProd;
|
||
}
|
||
}
|
||
if (function_exists('fxBibProductionResolveCellValue')) {
|
||
$str = fxBibProductionResolveCellValue(
|
||
$strKey,
|
||
(int)($row['no_bib'] ?? 0),
|
||
'',
|
||
$row,
|
||
$tabAnswersByQue,
|
||
$strLangue
|
||
);
|
||
if ($strKey === 'par_naissance' && $str !== '' && $str !== '0000-00-00') {
|
||
$ts = strtotime($str);
|
||
if ($ts !== false) {
|
||
return date(($strLangue === 'en') ? 'Y-m-d' : 'Y-m-d', $ts);
|
||
}
|
||
}
|
||
return $str;
|
||
}
|
||
|
||
return trim((string)($row[$strKey] ?? ''));
|
||
}
|
||
|
||
/**
|
||
* MSIN-4512 — Calcule largeurs PDF pour la liste ordonnée (fixes + options).
|
||
* Dossard fixe ~22 mm ; Nom/Prénom flexible ; options compressées si besoin.
|
||
*
|
||
* @param array<int, array{key:string,label:string,width?:int}> $tabCols
|
||
* @return array{widths:array<string,float>,page:float}
|
||
*/
|
||
function fxBibDistPrintComputeLayoutWidths(array $tabCols, $strOrient) {
|
||
$fltPage = fxBibDistPrintUsableWidthMm($strOrient);
|
||
$fltBib = 22.0;
|
||
$fltNameMin = 50.0;
|
||
$fltOptNeed = 0.0;
|
||
$blnHasName = false;
|
||
$blnHasBib = false;
|
||
|
||
foreach ($tabCols as $col) {
|
||
$strKey = (string)($col['key'] ?? '');
|
||
if ($strKey === 'par_prenom_nom') {
|
||
$blnHasName = true;
|
||
continue;
|
||
}
|
||
if ($strKey === 'no_bib') {
|
||
$blnHasBib = true;
|
||
continue;
|
||
}
|
||
$fltOptNeed += (float)($col['width'] ?? fxBibDistPrintColEstWidthMm($strKey));
|
||
}
|
||
|
||
$fltReserved = ($blnHasBib ? $fltBib : 0.0);
|
||
$fltRemain = max(10.0, $fltPage - $fltReserved);
|
||
|
||
$fltName = $blnHasName ? $fltNameMin : 0.0;
|
||
$fltForOpts = max(0.0, $fltRemain - $fltName);
|
||
$fltScale = ($fltOptNeed > 0 && $fltOptNeed > $fltForOpts) ? ($fltForOpts / $fltOptNeed) : 1.0;
|
||
if ($blnHasName && $fltScale >= 1.0) {
|
||
$fltName = max($fltNameMin, $fltRemain - $fltOptNeed);
|
||
$fltScale = 1.0;
|
||
}
|
||
|
||
$tabW = [];
|
||
foreach ($tabCols as $col) {
|
||
$strKey = (string)($col['key'] ?? '');
|
||
if ($strKey === '') {
|
||
continue;
|
||
}
|
||
if ($strKey === 'no_bib') {
|
||
$tabW[$strKey] = $fltBib;
|
||
continue;
|
||
}
|
||
if ($strKey === 'par_prenom_nom') {
|
||
$tabW[$strKey] = max($fltNameMin, $fltName);
|
||
continue;
|
||
}
|
||
$fltW = (float)($col['width'] ?? fxBibDistPrintColEstWidthMm($strKey)) * $fltScale;
|
||
$tabW[$strKey] = max(8.0, $fltW);
|
||
}
|
||
|
||
return [
|
||
'widths' => $tabW,
|
||
'page' => $fltPage,
|
||
// Compat anciens appelants éventuels.
|
||
'name' => $tabW['par_prenom_nom'] ?? $fltName,
|
||
'bib' => $tabW['no_bib'] ?? $fltBib,
|
||
'opts' => array_filter($tabW, function ($k) {
|
||
return !fxBibDistPrintIsMandatoryKey($k);
|
||
}, ARRAY_FILTER_USE_KEY),
|
||
];
|
||
}
|