From 0551b13a868966540fa769799d0e704c0185e355 Mon Sep 17 00:00:00 2001 From: Clemens Creutzburg Date: Sun, 19 Jul 2026 23:24:36 +0200 Subject: [PATCH] pdf erstellung aufhrbung der 50 Personen Grenze --- app/saas-auth.php | 16 +++++-- .../migrations/0016_saas_pdf_row_height.sql | 2 + exportKaffeeliste.php | 46 ++++++++++++------- mandant-einstellungen.php | 5 ++ 4 files changed, 49 insertions(+), 20 deletions(-) create mode 100644 database/migrations/0016_saas_pdf_row_height.sql diff --git a/app/saas-auth.php b/app/saas-auth.php index a08ff54..682ea90 100644 --- a/app/saas-auth.php +++ b/app/saas-auth.php @@ -275,7 +275,8 @@ function saas_fetch_tenant_settings(PDO $pdo, int $tenantId): ?array ts.sheet_window_days, ts.negative_warning_cents, ts.pdf_show_empty_rows, - ts.pdf_split_mode + ts.pdf_split_mode, + ts.pdf_row_height_px FROM tenants t LEFT JOIN tenant_settings ts ON ts.tenant_id = t.id WHERE t.id = ? @@ -310,6 +311,7 @@ function saas_fetch_tenant_settings(PDO $pdo, int $tenantId): ?array 'negative_warning_cents' => (int)$settings['negative_warning_cents'], 'pdf_show_empty_rows' => (int)$settings['pdf_show_empty_rows'], 'pdf_split_mode' => (string)$settings['pdf_split_mode'], + 'pdf_row_height_px' => (int)$settings['pdf_row_height_px'], ]; } @@ -324,6 +326,7 @@ function saas_update_tenant_settings(PDO $pdo, int $tenantId, array $input): arr $negativeWarningCents = saas_parse_money_cents($input['negative_warning'] ?? ''); $paypalUrlTemplate = trim((string)($input['paypal_url_template'] ?? '')); $pdfSplitMode = trim((string)($input['pdf_split_mode'] ?? 'drinking_behavior')); + $pdfRowHeightPx = filter_var($input['pdf_row_height_px'] ?? null, FILTER_VALIDATE_INT); $errors = []; if (strlen($tenantName) < 3 || strlen($tenantName) > 255) { @@ -353,6 +356,9 @@ function saas_update_tenant_settings(PDO $pdo, int $tenantId, array $input): arr if (!in_array($pdfSplitMode, ['drinking_behavior', 'alphabetical'], true)) { $errors[] = 'Die Sortierung für den Ausdruck ist ungültig.'; } + if ($pdfRowHeightPx === false || $pdfRowHeightPx < 10 || $pdfRowHeightPx > 60) { + $errors[] = 'Die Zeilenhöhe im Ausdruck muss zwischen 10 und 60 Pixel liegen.'; + } if ($errors !== []) { return ['ok' => false, 'errors' => $errors]; @@ -376,8 +382,8 @@ function saas_update_tenant_settings(PDO $pdo, int $tenantId, array $input): arr 'INSERT INTO tenant_settings (tenant_id, mark_price_cents, self_entry_enabled, paypal_enabled, paypal_url_template, sheet_window_days, negative_warning_cents, - pdf_show_empty_rows, pdf_split_mode) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) + pdf_show_empty_rows, pdf_split_mode, pdf_row_height_px) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE mark_price_cents = VALUES(mark_price_cents), self_entry_enabled = VALUES(self_entry_enabled), @@ -386,7 +392,8 @@ function saas_update_tenant_settings(PDO $pdo, int $tenantId, array $input): arr sheet_window_days = VALUES(sheet_window_days), negative_warning_cents = VALUES(negative_warning_cents), pdf_show_empty_rows = VALUES(pdf_show_empty_rows), - pdf_split_mode = VALUES(pdf_split_mode)' + pdf_split_mode = VALUES(pdf_split_mode), + pdf_row_height_px = VALUES(pdf_row_height_px)' ); $stmt->execute([ $tenantId, @@ -398,6 +405,7 @@ function saas_update_tenant_settings(PDO $pdo, int $tenantId, array $input): arr -abs($negativeWarningCents), $pdfShowEmptyRows, $pdfSplitMode, + (int)$pdfRowHeightPx, ]); $pdo->commit(); diff --git a/database/migrations/0016_saas_pdf_row_height.sql b/database/migrations/0016_saas_pdf_row_height.sql new file mode 100644 index 0000000..29ec699 --- /dev/null +++ b/database/migrations/0016_saas_pdf_row_height.sql @@ -0,0 +1,2 @@ +ALTER TABLE tenant_settings + ADD COLUMN pdf_row_height_px SMALLINT NOT NULL DEFAULT 16 AFTER pdf_split_mode; diff --git a/exportKaffeeliste.php b/exportKaffeeliste.php index dfac242..c01024a 100644 --- a/exportKaffeeliste.php +++ b/exportKaffeeliste.php @@ -31,6 +31,7 @@ $windowDays = $settings['sheet_window_days'] ?? 100; $markPrice = number_format(($settings['mark_price_cents'] ?? 20) / 100, 2, ',', '.'); $showEmptyRows = (int)($settings['pdf_show_empty_rows'] ?? 1) === 1; $splitMode = (string)($settings['pdf_split_mode'] ?? 'drinking_behavior'); +$adminRowHeightPx = (int)($settings['pdf_row_height_px'] ?? 16); // Ab dieser Mitgliederzahl wird auf zwei Seiten (Vorder-/Rueckseite) statt // einer einzelnen Seite gedruckt. @@ -38,33 +39,46 @@ define('EXPORT_MULTI_PAGE_THRESHOLD', 50); // Platzbudget in TCPDF-HTML-"px" fuer die Datenzeilen einer Seite, kalibriert // an der bisherigen Ausgabe (63 Zeilen a 16px auf einer vollen A4-Seite). define('EXPORT_PAGE_ROW_BUDGET_PX', 63 * 16); -define('EXPORT_MIN_ROW_HEIGHT_PX', 16); -define('EXPORT_MAX_ROW_HEIGHT_PX', 40); -// Ziel-Zeilenzahl je Seite im Zweiseiten-Modus, wenn freie Zeilen erwuenscht -// sind - identisch zur bisherigen, getesteten Dichte (63 Zeilen a 16px). -define('EXPORT_MULTI_PAGE_FILL_ROWS', 63); +// Technischer Mindestwert, nur als letzte Bremse gegen 0/negative Hoehen bei +// absurd hohen Teilnehmerzahlen - kein gestalterisches Minimum. +define('EXPORT_HARD_MIN_ROW_HEIGHT_PX', 6); $allActive = ledger_fetch_participant_summaries($pdo, $tenantId, ['active_only' => true]); $memberCount = count($allActive); $isMultiPage = $memberCount >= EXPORT_MULTI_PAGE_THRESHOLD; -function export_row_height_px(int $targetRows): float +/** + * Ermittelt Zeilenanzahl und -hoehe fuer eine Seite: Ausgangspunkt ist die + * vom Admin gewuenschte Zeilenhoehe. Sind Fuellzeilen erwuenscht, wird die + * Seite bis zu dieser Hoehe komplett aufgefuellt (weniger Mitglieder -> + * groessere, statt immer gleich viele Zeilen). Passt die Mitgliederzahl bei + * der gewuenschten Hoehe nicht auf eine Seite, wird bei jeder Generierung + * automatisch so weit verkleinert, dass garantiert alle Teilnehmer passen. + * + * @return array{0: int, 1: float} [targetRows, rowHeightPx] + */ +function export_compute_layout(int $memberCount, bool $showEmptyRows, int $adminRowHeightPx): array { - $height = EXPORT_PAGE_ROW_BUDGET_PX / max(1, $targetRows); + $adminRowHeightPx = max(1, $adminRowHeightPx); + $capacityAtAdminHeight = (int)floor(EXPORT_PAGE_ROW_BUDGET_PX / $adminRowHeightPx); - return max(EXPORT_MIN_ROW_HEIGHT_PX, min(EXPORT_MAX_ROW_HEIGHT_PX, $height)); + $targetRows = $showEmptyRows ? max($memberCount, $capacityAtAdminHeight) : $memberCount; + $targetRows = max(1, $targetRows); + + $rowHeight = min((float)$adminRowHeightPx, EXPORT_PAGE_ROW_BUDGET_PX / $targetRows); + $rowHeight = max(EXPORT_HARD_MIN_ROW_HEIGHT_PX, $rowHeight); + + return [$targetRows, $rowHeight]; } /** - * Rendert eine Teilnehmerseite als HTML-Tabelle fuer TCPDF. Die Zeilenhoehe - * richtet sich nach der Ziel-Zeilenzahl (Mitglieder plus ggf. Fuellzeilen), - * damit wenige Mitglieder groessere statt winziger Zeilen ergeben. + * Rendert eine Teilnehmerseite als HTML-Tabelle fuer TCPDF. * * @param list $teilnehmer */ -function export_render_page_html(array $teilnehmer, string $titel, string $hinweisText, string $markPrice, int $targetRows, bool $showEmptyRows, ?string $seitenHinweis): string +function export_render_page_html(array $teilnehmer, string $titel, string $hinweisText, string $markPrice, bool $showEmptyRows, int $adminRowHeightPx, ?string $seitenHinweis): string { - $rowHeight = export_row_height_px($showEmptyRows ? $targetRows : max(1, count($teilnehmer))); + [$targetRows, $rowHeight] = export_compute_layout(count($teilnehmer), $showEmptyRows, $adminRowHeightPx); $html = ' @@ -146,8 +160,8 @@ if (!$isMultiPage) { 'Kaffeeliste', 'background-color: rgb(0, 94, 63);color: rgb(255, 255, 255);', $markPrice, - EXPORT_MULTI_PAGE_THRESHOLD, $showEmptyRows, + $adminRowHeightPx, null ), true, false, true, false, '' @@ -168,13 +182,13 @@ if (!$isMultiPage) { $pdf->AddPage(); $pdf->writeHTML( - export_render_page_html($front, $frontTitel, 'background-color: rgb(0, 94, 63);color: rgb(255, 255, 255);', $markPrice, EXPORT_MULTI_PAGE_FILL_ROWS, $showEmptyRows, 'Rückseite beachten!'), + export_render_page_html($front, $frontTitel, 'background-color: rgb(0, 94, 63);color: rgb(255, 255, 255);', $markPrice, $showEmptyRows, $adminRowHeightPx, 'Rückseite beachten!'), true, false, true, false, '' ); $pdf->AddPage(); $pdf->writeHTML( - export_render_page_html($back, $backTitel, 'background-color: rgb(91, 209, 215);color: rgb(0, 0, 0);', $markPrice, EXPORT_MULTI_PAGE_FILL_ROWS, $showEmptyRows, 'Vorderseite beachten!'), + export_render_page_html($back, $backTitel, 'background-color: rgb(91, 209, 215);color: rgb(0, 0, 0);', $markPrice, $showEmptyRows, $adminRowHeightPx, 'Vorderseite beachten!'), true, false, true, false, '' ); } diff --git a/mandant-einstellungen.php b/mandant-einstellungen.php index e40743a..e756e0a 100644 --- a/mandant-einstellungen.php +++ b/mandant-einstellungen.php @@ -34,6 +34,7 @@ if ($canManageSettings) { 'negative_warning_cents' => -abs(saas_parse_money_cents($_POST['negative_warning'] ?? '') ?? 0), 'pdf_show_empty_rows' => !empty($_POST['pdf_show_empty_rows']) ? 1 : 0, 'pdf_split_mode' => $_POST['pdf_split_mode'] ?? 'drinking_behavior', + 'pdf_row_height_px' => $_POST['pdf_row_height_px'] ?? 16, ]; } } else { @@ -127,6 +128,10 @@ include 'nav.php'; +
+ + +