From 4d5f32ae6d5ed2d63305fad80cc17e1d17a65fc9 Mon Sep 17 00:00:00 2001 From: Clemens Creutzburg Date: Wed, 15 Jul 2026 00:57:51 +0200 Subject: [PATCH] M5: Sammelerfassung (Striche/Einzahlungen) tenant-nativ machen stricheintragen.php und einzahlung.php lasen ihren Mitarbeiter-Picker bisher direkt aus der global unscoped kl_Mitarbeiter-Tabelle. Fuer jeden Mandanten ausser dem Default-Mandanten zeigte das fremde Namen und Schreiben schlug (sicher, aber unverstaendlich) am Tenant-Check in ledger_mirror_legacy_* fehl. - Picker kommt jetzt aus participants (tenant-scoped), Formularfelder nutzen participant_id statt MitarbeiterID. - Schreibpfad pro Teilnehmer: mit legacy_mitarbeiter_id (Default-Mandant) weiterhin Dual-Write nach kl_Kaffeeverbrauch/kl_Einzahlungen plus Ledger-Spiegelung; ohne Legacy-Verknuepfung (jeder andere Mandant) direkt ueber neue ledger_record_consumption()/ledger_record_payment(). - Vorderseite/Rueckseite-Filter (100-Tage-Regel) bleiben fuer den Default-Mandanten exakt auf der bisherigen Legacy-Logik; andere Mandanten nutzen die neue ledger_fetch_participants_by_window_marks() mit tenant_settings.sheet_window_days. - Nebenbei behoben: einzahlung.php verlinkte auf ?aktion=... statt ?action=..., wodurch die Vorderseite/Rueckseite-Buttons nie griffen. Preis-pro-Strich-Vorbelegung kommt jetzt aus tenant_settings statt der seit M3 nicht mehr gepflegten kl_config-Tabelle. - Live getestet: isolierter Test-Mandant, Picker zeigt nur eigene Teilnehmer, Buchungen rein Ledger-nativ mit korrektem Saldo, 10-Striche- Schwelle korrekt sortiert, Default-Mandant-Dual-Write weiterhin gruen. Co-Authored-By: Claude Sonnet 5 --- app/ledger.php | 79 +++++++++++++ docs/m5-app-kern.md | 61 ++++++++-- docs/saas-umstrukturierungsplan.md | 9 +- einzahlung.php | 163 ++++++++++++++----------- stricheintragen.php | 184 +++++++++++++++++------------ 5 files changed, 346 insertions(+), 150 deletions(-) diff --git a/app/ledger.php b/app/ledger.php index 2640ac9..cd942da 100644 --- a/app/ledger.php +++ b/app/ledger.php @@ -564,6 +564,85 @@ function ledger_void_entry_by_legacy_id(PDO $pdo, int $tenantId, string $legacyT return $stmt->rowCount() > 0; } +/** + * Records a consumption entry straight into the ledger, without any legacy + * kl_Kaffeeverbrauch row. Used for tenants that have no legacy shadow table + * (every tenant except the migrated default tenant). + */ +function ledger_record_consumption(PDO $pdo, int $tenantId, int $participantId, int $marks, int $unitPriceCents, string $source, ?int $createdByUserId = null): int +{ + $stmt = $pdo->prepare( + "INSERT INTO ledger_entries + (tenant_id, participant_id, type, amount_cents, marks_count, unit_price_cents, booked_at, source, created_by_user_id) + SELECT ?, id, 'consumption', ?, ?, ?, NOW(), ?, ? + FROM participants + WHERE id = ? AND tenant_id = ?" + ); + $stmt->execute([$tenantId, -($marks * $unitPriceCents), $marks, $unitPriceCents, $source, $createdByUserId, $participantId, $tenantId]); + + if ($stmt->rowCount() !== 1) { + throw new RuntimeException('Der Strich-Eintrag konnte nicht gespeichert werden.'); + } + + return (int)$pdo->lastInsertId(); +} + +/** + * Records a payment entry straight into the ledger, without any legacy + * kl_Einzahlungen row. Used for tenants that have no legacy shadow table. + */ +function ledger_record_payment(PDO $pdo, int $tenantId, int $participantId, int $amountCents, string $source, ?int $createdByUserId = null): int +{ + $stmt = $pdo->prepare( + "INSERT INTO ledger_entries + (tenant_id, participant_id, type, amount_cents, booked_at, source, created_by_user_id) + SELECT ?, id, 'payment', ?, NOW(), ?, ? + FROM participants + WHERE id = ? AND tenant_id = ?" + ); + $stmt->execute([$tenantId, $amountCents, $source, $createdByUserId, $participantId, $tenantId]); + + if ($stmt->rowCount() !== 1) { + throw new RuntimeException('Die Einzahlung konnte nicht gespeichert werden.'); + } + + return (int)$pdo->lastInsertId(); +} + +/** + * Ledger-native equivalent of the legacy "100-Tage-Liste" front/back split: + * active participants whose summed marks within the trailing window are at + * or above the ten-mark threshold ($atLeastTen), or below it. Used for + * tenants without a legacy kl_Kaffeeverbrauch history to filter against. + * + * @return list + */ +function ledger_fetch_participants_by_window_marks(PDO $pdo, int $tenantId, int $windowDays, bool $atLeastTen): array +{ + $stmt = $pdo->prepare( + "SELECT p.id + FROM participants p + LEFT JOIN ledger_entries le + ON le.tenant_id = p.tenant_id + AND le.participant_id = p.id + AND le.type = 'consumption' + AND le.voided_at IS NULL + AND le.booked_at >= DATE_SUB(NOW(), INTERVAL ? DAY) + WHERE p.tenant_id = ? + AND p.active = 1 + GROUP BY p.id + HAVING COALESCE(SUM(le.marks_count), 0) " . ($atLeastTen ? '>= 10' : '< 10') + ); + $stmt->execute([$windowDays, $tenantId]); + $ids = array_map('intval', $stmt->fetchAll(PDO::FETCH_COLUMN)); + + if ($ids === []) { + return []; + } + + return ledger_fetch_participant_summaries($pdo, $tenantId, ['participant_ids' => $ids]); +} + /** * Options: * - participant_id: int diff --git a/docs/m5-app-kern.md b/docs/m5-app-kern.md index 5be5260..48d86d5 100644 --- a/docs/m5-app-kern.md +++ b/docs/m5-app-kern.md @@ -247,18 +247,60 @@ gepatcht: auf `kaffeeliste.php`), Zugang entzogen, anschließender Login-Versuch korrekt mit „kein aktiver Mandant" abgelehnt. +## Fortsetzung: Sammelerfassung tenant-nativ + +Umgesetzte Dateien: + +```text +stricheintragen.php (Picker + Schreibpfad neu) +einzahlung.php (Picker + Schreibpfad neu) +app/ledger.php (ledger_record_consumption, ledger_record_payment, + ledger_fetch_participants_by_window_marks) +``` + +`stricheintragen.php` und `einzahlung.php` lasen ihren Mitarbeiter-Picker +bisher direkt aus der global unscoped `kl_Mitarbeiter`-Tabelle. Für jeden +Mandanten außer dem Default-Mandanten war das faktisch nutzlos: Die Liste +zeigte fremde (Default-Mandanten-)Namen an, und Schreiben schlug wegen des +Tenant-Checks in `ledger_mirror_legacy_*` sicher, aber ohne verständliche +Fehlermeldung fehl. + +- Der Picker kommt jetzt aus `ledger_fetch_participant_summaries()` + (tenant-scoped), Formularfelder verwenden `participant_id` statt + `MitarbeiterID`. +- Beim Speichern wird pro Teilnehmer entschieden: Hat der Teilnehmer eine + `legacy_mitarbeiter_id` (Default-Mandant), läuft der Schreibpfad wie + bisher über `kl_Kaffeeverbrauch`/`kl_Einzahlungen` plus Ledger-Spiegelung. + Ohne Legacy-Verknüpfung (jeder andere Mandant) wird direkt über die neuen + Funktionen `ledger_record_consumption()`/`ledger_record_payment()` ins + Ledger gebucht, ohne Legacy-Tabellen zu berühren. +- Die "Vorderseite"/"Rückseite"-Filter (100-Tage-Listen-Regel, `>= 10` + beziehungsweise `< 10` Striche im Fenster) bleiben für den Default- + Mandanten exakt auf der bisherigen Legacy-Logik (Anker: jüngstes Datum in + `kl_Kaffeeverbrauch`). Andere Mandanten nutzen die neue, Ledger-native + Fensterprüfung `ledger_fetch_participants_by_window_marks()` mit dem in + `tenant_settings.sheet_window_days` konfigurierten Fenster. +- Nebenbei zwei Legacy-Bugs behoben: In `einzahlung.php` verlinkten die + Vorderseite/Rückseite/Alle-Buttons auf `?aktion=...`, ausgewertet wurde + aber `$_GET['action']` – die Filter griffen also nie. Und in beiden + Dateien blieb nach einem POST-Speichern `$sqlMitarbeiter` unbelegt + (behoben bereits in der vorherigen Session, jetzt strukturell nicht mehr + möglich, da die Anzeige nach dem Speichern regulär über denselben + Picker-Code läuft). +- Preis-pro-Strich-Vorbelegung kommt jetzt aus `tenant_settings.mark_price_cents` + statt aus der mandantenunabhängigen `kl_config`-Tabelle. Das ist auch für + den Default-Mandanten eine Korrektur: `kl_config` wird seit M3 nicht mehr + über `mandant-einstellungen.php` gepflegt und kann veraltet sein. +- Live gegen die Dev-Datenbank getestet: eigener isolierter Test-Mandant + angelegt, Picker zeigt nur dessen Teilnehmer, Striche und Einzahlung rein + Ledger-nativ gebucht (korrekter Saldo), Vorder-/Rückseiten-Schwelle bei + 10 Strichen korrekt ein-/aussortiert, Default-Mandant weiterhin mit + Dual-Write geprüft. Alle Testdaten anschließend entfernt. + ## Noch offen - Eigene PayPal-/Zahlungsbereich als eigenständiger App-Screen (aktuell nur im Dashboard integriert). -- `stricheintragen.php` und `einzahlung.php` lesen ihre Mitarbeiter-Picker - weiterhin aus der global unscoped `kl_Mitarbeiter`-Tabelle. Für den - Default-Mandanten funktioniert das unverändert; für jeden anderen Mandanten - ist die Liste faktisch leer beziehungsweise zeigt (nur lesend, Schreiben - schlägt dank Tenant-Scope in `ledger_mirror_legacy_*` sicher fehl) die - Namen der Default-Mandanten-Mitglieder an. Das ist ein bestehendes, - eigenständiges Scope-Thema für eine spätere Iteration, keine Regression - dieser Session. - Export, Mail und Jahresprozesse bleiben M6-Themen. ## Aktueller Prüfstatus @@ -276,3 +318,6 @@ gepatcht: (korrekt escaped), Soft-Delete, Banner-Anzeige auf Mandant geprüft. - Live-Test Zugangsvergabe/-entzug: kompletter Flow von Einladung bis Login-Sperre nach Entzug erfolgreich geprüft (siehe oben). +- Live-Test Sammelerfassung für Nicht-Default-Mandanten: eigener Test-Tenant, + Striche/Einzahlung Ledger-nativ gebucht, Vorder-/Rückseiten-Fenster + geprüft (siehe oben). diff --git a/docs/saas-umstrukturierungsplan.md b/docs/saas-umstrukturierungsplan.md index 21452d0..307377b 100644 --- a/docs/saas-umstrukturierungsplan.md +++ b/docs/saas-umstrukturierungsplan.md @@ -534,7 +534,14 @@ Schritte: die Revision erhalten. - Sammelerfassung (`stricheintragen.php`, `einzahlung.php`) tenant-sicher absichern und ans Ledger anbinden: erledigt. Beide Seiten hatten zuvor - keine Zugriffskontrolle außer CSRF; das ist behoben. + keine Zugriffskontrolle außer CSRF; das ist behoben. Zusätzlich lasen + beide Seiten ihre Mitarbeiterliste aus der global unscoped + `kl_Mitarbeiter`-Tabelle, was sie für jeden Mandanten außer dem + Default-Mandanten unbrauchbar machte. Der Picker kommt jetzt aus + `participants` (tenant-scoped); der Schreibpfad nutzt für den + Default-Mandanten weiterhin Dual-Write nach `kl_Kaffeeverbrauch`/ + `kl_Einzahlungen`, für alle anderen Mandanten schreibt er direkt und + ausschließlich ins Ledger. - Hinweise als tenant-spezifische Notices umsetzen: erledigt. Neue Tabelle `notices` mit Soft-Delete, `hinweise.php` und die Banner-Anzeige in `header.php` sind tenant-scoped umgestellt; `kl_hinweise` bleibt nur noch diff --git a/einzahlung.php b/einzahlung.php index 9b46969..a9b4ad1 100644 --- a/einzahlung.php +++ b/einzahlung.php @@ -42,28 +42,85 @@ if (!$hasAccess) { exit; } +/** + * Liefert die Mitarbeiter/Teilnehmerliste fuer die Vorder-/Rueckseiten- oder + * Alle-Auswahl. Siehe stricheintragen.php fuer dieselbe Logik: Default- + * Mandant nutzt die Legacy-Historie, andere Mandanten die Ledger-native + * Fensterpruefung mit dem konfigurierten Listenfenster. + * + * @return list + */ +function einzahlung_fetch_participants(PDO $pdo, int $tenantId, string $action): array +{ + $isDefault = ledger_is_default_tenant($pdo, $tenantId); + + if ($action === 'vorderseite' || $action === 'rueckseite') { + if ($isDefault) { + $vergleich = $action === 'vorderseite' ? '>= 10' : '< 10'; + $join = $action === 'vorderseite' ? 'JOIN' : 'LEFT JOIN'; + $stmt = $pdo->query( + "SELECT M.MitarbeiterID + FROM kl_Mitarbeiter M + {$join} kl_Kaffeeverbrauch V ON M.MitarbeiterID = V.MitarbeiterID AND V.Datum >= DATE_SUB(NOW(), INTERVAL 100 DAY) + WHERE M.aktiv = 1 + GROUP BY M.MitarbeiterID + HAVING COALESCE(SUM(V.AnzahlStriche), 0) {$vergleich}" + ); + $legacyIds = array_map('intval', $stmt->fetchAll(PDO::FETCH_COLUMN)); + + return ledger_fetch_participant_summaries($pdo, $tenantId, ['legacy_mitarbeiter_ids' => $legacyIds]); + } + + $settings = saas_fetch_tenant_settings($pdo, $tenantId); + $windowDays = $settings['sheet_window_days'] ?? 100; + + return ledger_fetch_participants_by_window_marks($pdo, $tenantId, $windowDays, $action === 'vorderseite'); + } + + return ledger_fetch_participant_summaries($pdo, $tenantId, ['active_only' => true]); +} + +$eingetragen = 0; +$fehlgeschlagen = false; +$hatGespeichert = false; + // Verarbeitung des Formulars, wenn es gesendet wurde if ($_SERVER["REQUEST_METHOD"] == "POST" ) { + $hatGespeichert = true; $datum = date("Y-m-d H:i:s"); // Das aktuelle Datum verwenden - $eingetragen = 0; - $fehlgeschlagen = false; + + // Legacy-Verknuepfung je Teilnehmer vorab laden, damit pro Zeile + // entschieden werden kann, ob (Default-Mandant) zusaetzlich in + // kl_Einzahlungen geschrieben oder (jeder andere Mandant) direkt ins + // Ledger gebucht wird. + $legacyMap = []; + $stmt = $pdo->prepare('SELECT id, legacy_mitarbeiter_id FROM participants WHERE tenant_id = ?'); + $stmt->execute([$tenantId]); + foreach ($stmt->fetchAll() as $row) { + $legacyMap[(int)$row['id']] = $row['legacy_mitarbeiter_id'] !== null ? (int)$row['legacy_mitarbeiter_id'] : null; + } try { $pdo->beginTransaction(); - $insert = $pdo->prepare( + $insertLegacy = $pdo->prepare( "INSERT INTO kl_Einzahlungen (MitarbeiterID, Betrag, Datum) VALUES (?, ?, ?)" ); - foreach ($_POST["anzahlBetrag"] ?? [] as $mitarbeiterID => $anzahlBetrag) { - $mitarbeiterID = (int)$mitarbeiterID; + foreach ($_POST["anzahlBetrag"] ?? [] as $participantId => $anzahlBetrag) { + $participantId = (int)$participantId; $anzahlBetrag = floatval($anzahlBetrag); - if ($mitarbeiterID <= 0 || $anzahlBetrag == 0.0) { + if ($participantId <= 0 || $anzahlBetrag == 0.0 || !array_key_exists($participantId, $legacyMap)) { continue; } - $insert->execute([$mitarbeiterID, $anzahlBetrag, $datum]); - $legacyPaymentId = (int)$pdo->lastInsertId(); - ledger_mirror_legacy_payment($pdo, $tenantId, $legacyPaymentId); + $legacyMitarbeiterId = $legacyMap[$participantId]; + if ($legacyMitarbeiterId !== null) { + $insertLegacy->execute([$legacyMitarbeiterId, $anzahlBetrag, $datum]); + $legacyPaymentId = (int)$pdo->lastInsertId(); + ledger_mirror_legacy_payment($pdo, $tenantId, $legacyPaymentId); + } else { + ledger_record_payment($pdo, $tenantId, $participantId, (int)round($anzahlBetrag * 100), 'manual_bulk'); + } $eingetragen++; } @@ -75,51 +132,13 @@ if ($_SERVER["REQUEST_METHOD"] == "POST" ) { $fehlgeschlagen = true; } - if ($fehlgeschlagen) { - echo "Die Einzahlungen konnten nicht gespeichert werden."; - } else { - echo $eingetragen . " Einträge erfolgreich hinzugefügt."; - } - - // Mitarbeiter aus der Datenbank abrufen und nach Namen sortieren - $sqlMitarbeiter = "SELECT MitarbeiterID, Name FROM kl_Mitarbeiter WHERE aktiv = 1 ORDER BY Name"; -}elseif($_SERVER["REQUEST_METHOD"] == "GET" AND isset($_GET["action"])){ - - if($_GET["action"] == "vorderseite"){ - - $sqlMitglieder = "SELECT M.MitarbeiterID, M.Name, M.Email - FROM kl_Mitarbeiter M - JOIN kl_Kaffeeverbrauch V ON M.MitarbeiterID = V.MitarbeiterID - WHERE V.Datum >= DATEADD(DAY, -100, GETDATE()) AND M.aktiv = 1 - GROUP BY M.MitarbeiterID, M.Name, M.Email - HAVING SUM(V.AnzahlStriche) >= 10 - ORDER BY Name;"; - - }elseif( $_GET["action"] == "rueckseite"){ - - $sqlMitglieder = "SELECT M.MitarbeiterID, M.Name, M.Email - FROM kl_Mitarbeiter M - LEFT JOIN kl_Kaffeeverbrauch V ON M.MitarbeiterID = V.MitarbeiterID AND V.Datum >= DATEADD(DAY, -100, GETDATE()) - WHERE M.aktiv = 1 - GROUP BY M.MitarbeiterID, M.Name, M.Email - HAVING COALESCE(SUM(V.AnzahlStriche), 0) < 10 - ORDER BY M.Name;"; - }elseif($_GET["action"] == "alle"){ - // Mitarbeiter aus der Datenbank abrufen und nach Namen sortieren - $sqlMitarbeiter = "SELECT MitarbeiterID, Name FROM kl_Mitarbeiter WHERE aktiv = 1 ORDER BY Name"; - } - -}else{ - // Mitarbeiter aus der Datenbank abrufen und nach Namen sortieren - $sqlMitarbeiter = "SELECT MitarbeiterID, Name FROM kl_Mitarbeiter WHERE aktiv = 1 ORDER BY Name"; - - + $action = 'alle'; +} else { + $action = (string)($_GET['action'] ?? 'alle'); } -$stmtMitarbeiter = sqlsrv_query($conn, $sqlMitarbeiter); +$mitarbeiter = einzahlung_fetch_participants($pdo, $tenantId, $action); -// Schließe die Verbindung zur Datenbank -#sqlsrv_close($conn); ?> @@ -132,50 +151,60 @@ $stmtMitarbeiter = sqlsrv_query($conn, $sqlMitarbeiter);

Einzahlungen für alle Mitarbeiter

+ + + +

Die Einzahlungen konnten nicht gespeichert werden.

+ +

Einträge erfolgreich hinzugefügt.

+ + +
  • -
    + + -
    +
  • -
    + + -
    +
  • -
    + + -
    +
  • -
    +
  • -
    +
"> "; echo " Mitarbeiter Betrag "; - // Iteriere über alle Mitarbeiter - while ($row = sqlsrv_fetch_array($stmtMitarbeiter, SQLSRV_FETCH_ASSOC)) { - $mitarbeiterID = $row["MitarbeiterID"]; - $mitarbeiterName = $row["Name"]; + foreach ($mitarbeiter as $teilnehmer) { + $participantId = $teilnehmer['participant_id']; + $name = saas_html($teilnehmer['display_name']); echo ""; - // Formularfeld für die Anzahl der Striche - echo ""; - echo ""; + echo ""; + echo ""; echo ""; } echo ""; diff --git a/stricheintragen.php b/stricheintragen.php index ccb4f37..2b996da 100644 --- a/stricheintragen.php +++ b/stricheintragen.php @@ -43,30 +43,102 @@ if (!$hasAccess) { exit; } +/** + * Liefert die Mitarbeiter/Teilnehmerliste fuer die Vorder-/Rueckseiten- oder + * Alle-Auswahl. Fuer den Default-Mandanten bleibt die Fensterlogik exakt wie + * im Legacy-Bestand (Anker ist das juengste Datum in kl_Kaffeeverbrauch); + * andere Mandanten haben keine Legacy-Historie und nutzen die + * Ledger-native Fensterpruefung mit dem konfigurierten Listenfenster. + * + * @return list + */ +function stricheintragen_fetch_participants(PDO $pdo, int $tenantId, string $action): array +{ + $isDefault = ledger_is_default_tenant($pdo, $tenantId); + + if ($isDefault && $action === 'vorderseite') { + $stmt = $pdo->query( + "SELECT M.MitarbeiterID + FROM kl_Mitarbeiter M + JOIN kl_Kaffeeverbrauch V ON M.MitarbeiterID = V.MitarbeiterID + WHERE V.Datum >= DATE_SUB((SELECT MAX(Datum) FROM kl_Kaffeeverbrauch WHERE Datum < CURDATE()), INTERVAL 100 DAY) + AND M.aktiv = 1 + GROUP BY M.MitarbeiterID + HAVING SUM(V.AnzahlStriche) >= 10" + ); + $legacyIds = array_map('intval', $stmt->fetchAll(PDO::FETCH_COLUMN)); + + return ledger_fetch_participant_summaries($pdo, $tenantId, ['legacy_mitarbeiter_ids' => $legacyIds]); + } + + if ($isDefault && $action === 'rueckseite') { + $stmt = $pdo->query( + "SELECT M.MitarbeiterID + FROM kl_Mitarbeiter M + LEFT JOIN kl_Kaffeeverbrauch V ON M.MitarbeiterID = V.MitarbeiterID AND V.Datum >= DATE_SUB((SELECT MAX(Datum) FROM kl_Kaffeeverbrauch), INTERVAL 100 DAY) + WHERE M.aktiv = 1 + GROUP BY M.MitarbeiterID + HAVING COALESCE(SUM(V.AnzahlStriche), 0) < 10" + ); + $legacyIds = array_map('intval', $stmt->fetchAll(PDO::FETCH_COLUMN)); + + return ledger_fetch_participant_summaries($pdo, $tenantId, ['legacy_mitarbeiter_ids' => $legacyIds]); + } + + if (!$isDefault && ($action === 'vorderseite' || $action === 'rueckseite')) { + $settings = saas_fetch_tenant_settings($pdo, $tenantId); + $windowDays = $settings['sheet_window_days'] ?? 100; + + return ledger_fetch_participants_by_window_marks($pdo, $tenantId, $windowDays, $action === 'vorderseite'); + } + + return ledger_fetch_participant_summaries($pdo, $tenantId, ['active_only' => true]); +} + +$eingetragen = 0; +$fehlgeschlagen = false; +$hatGespeichert = false; + // Verarbeitung des Formulars, wenn es gesendet wurde if ($_SERVER["REQUEST_METHOD"] == "POST") { + $hatGespeichert = true; $kostenproStrich = floatval($_POST["kostenproStrich"] ?? 0); - $datum = date("Y-m-d H:i:s"); // Das aktuelle Datum verwenden - $eingetragen = 0; - $fehlgeschlagen = false; + $unitPriceCents = (int)round($kostenproStrich * 100); + $datum = date("Y-m-d H:i:s"); + + // Legacy-Verknuepfung je Teilnehmer vorab laden, damit pro Zeile + // entschieden werden kann, ob (Default-Mandant) zusaetzlich in + // kl_Kaffeeverbrauch geschrieben oder (jeder andere Mandant) direkt + // ins Ledger gebucht wird. + $legacyMap = []; + $stmt = $pdo->prepare('SELECT id, legacy_mitarbeiter_id FROM participants WHERE tenant_id = ?'); + $stmt->execute([$tenantId]); + foreach ($stmt->fetchAll() as $row) { + $legacyMap[(int)$row['id']] = $row['legacy_mitarbeiter_id'] !== null ? (int)$row['legacy_mitarbeiter_id'] : null; + } try { $pdo->beginTransaction(); - $insert = $pdo->prepare( + $insertLegacy = $pdo->prepare( "INSERT INTO kl_Kaffeeverbrauch (MitarbeiterID, AnzahlStriche, Kosten, KostenproStrich, Datum) VALUES (?, ?, ?, ?, ?)" ); - foreach ($_POST["anzahlStriche"] ?? [] as $mitarbeiterID => $anzahlStriche) { - $mitarbeiterID = (int)$mitarbeiterID; + foreach ($_POST["anzahlStriche"] ?? [] as $participantId => $anzahlStriche) { + $participantId = (int)$participantId; $anzahlStriche = (int)$anzahlStriche; - if ($mitarbeiterID <= 0 || $anzahlStriche === 0) { + if ($participantId <= 0 || $anzahlStriche === 0 || !array_key_exists($participantId, $legacyMap)) { continue; } - $kosten = $anzahlStriche * $kostenproStrich; - $insert->execute([$mitarbeiterID, $anzahlStriche, $kosten, $kostenproStrich, $datum]); - $legacyConsumptionId = (int)$pdo->lastInsertId(); - ledger_mirror_legacy_consumption($pdo, $tenantId, $legacyConsumptionId); + $legacyMitarbeiterId = $legacyMap[$participantId]; + if ($legacyMitarbeiterId !== null) { + $kosten = $anzahlStriche * $kostenproStrich; + $insertLegacy->execute([$legacyMitarbeiterId, $anzahlStriche, $kosten, $kostenproStrich, $datum]); + $legacyConsumptionId = (int)$pdo->lastInsertId(); + ledger_mirror_legacy_consumption($pdo, $tenantId, $legacyConsumptionId); + } else { + ledger_record_consumption($pdo, $tenantId, $participantId, $anzahlStriche, $unitPriceCents, 'manual_bulk'); + } $eingetragen++; } @@ -78,55 +150,14 @@ if ($_SERVER["REQUEST_METHOD"] == "POST") { $fehlgeschlagen = true; } - if ($fehlgeschlagen) { - echo "Die Einträge konnten nicht gespeichert werden."; - } else { - echo $eingetragen . " Einträge erfolgreich hinzugefügt."; - } - - // Mitarbeiter aus der Datenbank abrufen und nach Namen sortieren - $sqlMitarbeiter = "SELECT MitarbeiterID, Name FROM kl_Mitarbeiter WHERE aktiv = 1 ORDER BY Name"; -}elseif($_SERVER["REQUEST_METHOD"] == "GET" AND isset($_GET["action"])){ - - if($_GET["action"] == "vorderseite"){ - - $sqlMitarbeiter = "SELECT M.MitarbeiterID, M.Name, M.Email - FROM kl_Mitarbeiter M - JOIN kl_Kaffeeverbrauch V ON M.MitarbeiterID = V.MitarbeiterID - WHERE V.Datum >= DATEADD(DAY, -100, (SELECT MAX(Datum) FROM kl_Kaffeeverbrauch WHERE Datum < CAST(GETDATE() AS DATE))) AND M.aktiv = 1 - GROUP BY M.MitarbeiterID, M.Name, M.Email - HAVING SUM(V.AnzahlStriche) >= 10 - ORDER BY Name;"; - - }elseif( $_GET["action"] == "rueckseite"){ - - $sqlMitarbeiter = "SELECT M.MitarbeiterID, M.Name, M.Email - FROM kl_Mitarbeiter M - LEFT JOIN kl_Kaffeeverbrauch V ON M.MitarbeiterID = V.MitarbeiterID AND V.Datum >= DATEADD(DAY, -100, (SELECT MAX(Datum) FROM kl_Kaffeeverbrauch)) - WHERE M.aktiv = 1 - GROUP BY M.MitarbeiterID, M.Name, M.Email - HAVING COALESCE(SUM(V.AnzahlStriche), 0) < 10 - ORDER BY M.Name;"; - }elseif($_GET["action"] == "alle"){ - // Mitarbeiter aus der Datenbank abrufen und nach Namen sortieren - $sqlMitarbeiter = "SELECT MitarbeiterID, Name FROM kl_Mitarbeiter WHERE aktiv = 1 ORDER BY Name"; - - } - -}else{ - // Mitarbeiter aus der Datenbank abrufen und nach Namen sortieren - $sqlMitarbeiter = "SELECT MitarbeiterID, Name FROM kl_Mitarbeiter WHERE aktiv = 1 ORDER BY Name"; - - + $action = 'alle'; +} else { + $action = (string)($_GET['action'] ?? 'alle'); } -$stmtMitarbeiter = sqlsrv_query($conn, $sqlMitarbeiter); -// Kosten pro Strich auslesen -$sqlKostenproStrich = "SELECT KostenproStrich FROM kl_config "; -$stmtKostenproStrich = sqlsrv_query($conn, $sqlKostenproStrich); -$row = sqlsrv_fetch_array($stmtKostenproStrich, SQLSRV_FETCH_ASSOC); -$KostenproStrichtemp = $row["KostenproStrich"]; -$KostenproStrich = number_format($KostenproStrichtemp, 2, '.', ''); +$mitarbeiter = stricheintragen_fetch_participants($pdo, $tenantId, $action); +$settings = saas_fetch_tenant_settings($pdo, $tenantId); +$kostenproStrichAnzeige = number_format(($settings['mark_price_cents'] ?? 20) / 100, 2, '.', ''); ?> @@ -140,54 +171,59 @@ $KostenproStrich = number_format($KostenproStrichtemp, 2, '.', '');

Anzahl der Striche für alle Mitarbeiter

+ + + +

Die Einträge konnten nicht gespeichert werden.

+ +

Einträge erfolgreich hinzugefügt.

+ + +
  • -
  • +
  • -
    +
  • -
    +
"> Kosten pro Strich: -
"; - +
"; + echo ""; echo " "; - // Iteriere über alle Mitarbeiter - while ($row = sqlsrv_fetch_array($stmtMitarbeiter, SQLSRV_FETCH_ASSOC)) { - $mitarbeiterID = $row["MitarbeiterID"]; - $mitarbeiterName = $row["Name"]; + foreach ($mitarbeiter as $teilnehmer) { + $participantId = $teilnehmer['participant_id']; + $name = saas_html($teilnehmer['display_name']); echo ""; - - // Formularfeld für die Anzahl der Striche - echo ""; - echo ""; + echo ""; + echo ""; echo ""; - } echo "
Mitarbeiter Anzahl Striche
"; - + ?>