Bemerkung und Abzuege bei Einzahlungen
Einzahlungen koennen jetzt eine Bemerkung tragen und negativ sein, damit Abzuege (Auszahlung bei Austritt, Erstattung) nachvollziehbar gebucht werden koennen. - ledger_entries.note wurde bisher nur beim Import befuellt und wird nun auch bei Einzahlungen geschrieben und angezeigt - Negative Betraege verlangen zwingend eine Bemerkung, sonst ist spaeter nicht mehr nachvollziehbar, warum jemandem Geld abgezogen wurde - Plausibilitaetsgrenze von 1000 EUR gegen Groessenordnungs-Tippfehler; bei einem Fehler in einer Zeile wird gar nichts gebucht und die Eingaben bleiben stehen - Legacy-Tabelle kl_Einzahlungen bekommt eine Bemerkung-Spalte, sonst haette ledger_mirror_legacy_payment die Notiz beim Re-Sync wieder mit NULL ueberschrieben - PayPal-Buchungen tragen jetzt Zahler, Datum und Mitteilung als Bemerkung Ausserdem behoben: letzteneintraege.php hat Einzahlungen und Striche direkt aus den Legacy-Tabellen gelesen, ohne nach Mandant zu filtern. Jeder Mandanten-Admin sah damit die Buchungen aus den Legacy-Tabellen, und das Stornieren lief ueber die nicht mandantengetrennten Legacy-IDs. Die Seite arbeitet jetzt auf dem mandantengebundenen Journal, und ledger_void_entry prueft Mandant und Buchungsart, bevor es storniert. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+135
-34
@@ -83,6 +83,13 @@ function einzahlung_fetch_participants(PDO $pdo, int $tenantId, string $action):
|
||||
$eingetragen = 0;
|
||||
$fehlgeschlagen = false;
|
||||
$hatGespeichert = false;
|
||||
$validierungsFehler = [];
|
||||
$eingaben = [];
|
||||
|
||||
// Obergrenze je Einzahlungszeile. Faengt Groessenordnungs-Tippfehler ab
|
||||
// (z. B. 500 statt 5,00); groessere Betraege lassen sich in mehreren
|
||||
// Schritten eintragen.
|
||||
const EINZAHLUNG_MAX_BETRAG = 1000.00;
|
||||
|
||||
// Verarbeitung des Formulars, wenn es gesendet wurde
|
||||
if ($_SERVER["REQUEST_METHOD"] == "POST" ) {
|
||||
@@ -100,39 +107,114 @@ if ($_SERVER["REQUEST_METHOD"] == "POST" ) {
|
||||
$legacyMap[(int)$row['id']] = $row['legacy_mitarbeiter_id'] !== null ? (int)$row['legacy_mitarbeiter_id'] : null;
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo->beginTransaction();
|
||||
$insertLegacy = $pdo->prepare(
|
||||
"INSERT INTO kl_Einzahlungen (MitarbeiterID, Betrag, Datum) VALUES (?, ?, ?)"
|
||||
);
|
||||
|
||||
foreach ($_POST["anzahlBetrag"] ?? [] as $participantId => $anzahlBetrag) {
|
||||
$participantId = (int)$participantId;
|
||||
$anzahlBetrag = floatval($anzahlBetrag);
|
||||
if ($participantId <= 0 || $anzahlBetrag == 0.0 || !array_key_exists($participantId, $legacyMap)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$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++;
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
} catch (Throwable $e) {
|
||||
if ($pdo->inTransaction()) {
|
||||
$pdo->rollBack();
|
||||
}
|
||||
$fehlgeschlagen = true;
|
||||
// Namen fuer verstaendliche Fehlermeldungen.
|
||||
$namen = [];
|
||||
$stmtNamen = $pdo->prepare('SELECT id, display_name FROM participants WHERE tenant_id = ?');
|
||||
$stmtNamen->execute([$tenantId]);
|
||||
foreach ($stmtNamen->fetchAll() as $row) {
|
||||
$namen[(int)$row['id']] = (string)$row['display_name'];
|
||||
}
|
||||
|
||||
$action = 'alle';
|
||||
// Erst alle Zeilen einsammeln und pruefen, dann buchen. Schlaegt eine
|
||||
// Zeile fehl, wird gar nichts gebucht - sonst waere nach einem Tippfehler
|
||||
// unklar, was schon drin ist und was nicht.
|
||||
$zuBuchen = [];
|
||||
foreach ($_POST["anzahlBetrag"] ?? [] as $participantId => $anzahlBetrag) {
|
||||
$participantId = (int)$participantId;
|
||||
if ($participantId <= 0 || !array_key_exists($participantId, $legacyMap)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$rohBetrag = trim((string)$anzahlBetrag);
|
||||
$bemerkung = trim((string)($_POST['bemerkung'][$participantId] ?? ''));
|
||||
$eingaben[$participantId] = ['betrag' => $rohBetrag, 'bemerkung' => $bemerkung];
|
||||
|
||||
if ($rohBetrag === '') {
|
||||
// Leere Zeile: nur meckern, wenn trotzdem eine Bemerkung dransteht
|
||||
// (sonst hat jemand getippt und den Betrag vergessen).
|
||||
if ($bemerkung !== '') {
|
||||
$validierungsFehler[] = sprintf(
|
||||
'%s: Es wurde eine Bemerkung ohne Betrag angegeben.',
|
||||
$namen[$participantId] ?? ('Teilnehmer ' . $participantId)
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
$anzahlBetrag = floatval(str_replace(',', '.', $rohBetrag));
|
||||
if ($anzahlBetrag == 0.0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$name = $namen[$participantId] ?? ('Teilnehmer ' . $participantId);
|
||||
|
||||
// Abzuege muessen begruendet werden, sonst ist spaeter nicht mehr
|
||||
// nachvollziehbar, warum jemandem Geld abgezogen wurde.
|
||||
if ($anzahlBetrag < 0 && $bemerkung === '') {
|
||||
$validierungsFehler[] = sprintf(
|
||||
'%s: Bei einem Abzug (negativer Betrag) ist eine Bemerkung Pflicht.',
|
||||
$name
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Groessenordnungs-Tippfehler abfangen (500 statt 5,00).
|
||||
if (abs($anzahlBetrag) > EINZAHLUNG_MAX_BETRAG) {
|
||||
$validierungsFehler[] = sprintf(
|
||||
'%s: %s € übersteigt die Plausibilitätsgrenze von %s €. Bitte prüfen oder in mehreren Schritten eintragen.',
|
||||
$name,
|
||||
number_format($anzahlBetrag, 2, ',', '.'),
|
||||
number_format(EINZAHLUNG_MAX_BETRAG, 2, ',', '.')
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
$zuBuchen[$participantId] = [
|
||||
'betrag' => $anzahlBetrag,
|
||||
'bemerkung' => $bemerkung !== '' ? $bemerkung : null,
|
||||
];
|
||||
}
|
||||
|
||||
if ($validierungsFehler !== []) {
|
||||
$action = 'alle';
|
||||
} else {
|
||||
try {
|
||||
$pdo->beginTransaction();
|
||||
$insertLegacy = $pdo->prepare(
|
||||
"INSERT INTO kl_Einzahlungen (MitarbeiterID, Betrag, Bemerkung, Datum) VALUES (?, ?, ?, ?)"
|
||||
);
|
||||
|
||||
foreach ($zuBuchen as $participantId => $zeile) {
|
||||
$legacyMitarbeiterId = $legacyMap[$participantId];
|
||||
if ($legacyMitarbeiterId !== null) {
|
||||
$insertLegacy->execute([$legacyMitarbeiterId, $zeile['betrag'], $zeile['bemerkung'], $datum]);
|
||||
$legacyPaymentId = (int)$pdo->lastInsertId();
|
||||
ledger_mirror_legacy_payment($pdo, $tenantId, $legacyPaymentId);
|
||||
} else {
|
||||
ledger_record_payment(
|
||||
$pdo,
|
||||
$tenantId,
|
||||
$participantId,
|
||||
(int)round($zeile['betrag'] * 100),
|
||||
'manual_bulk',
|
||||
$saasUser['user_id'] ?? null,
|
||||
$zeile['bemerkung']
|
||||
);
|
||||
}
|
||||
$eingetragen++;
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
$eingaben = []; // Erfolgreich gebucht: Formular wieder leeren.
|
||||
} catch (Throwable $e) {
|
||||
if ($pdo->inTransaction()) {
|
||||
$pdo->rollBack();
|
||||
}
|
||||
$fehlgeschlagen = true;
|
||||
}
|
||||
|
||||
$action = 'alle';
|
||||
}
|
||||
} else {
|
||||
$action = (string)($_GET['action'] ?? 'alle');
|
||||
}
|
||||
@@ -144,13 +226,27 @@ $mitarbeiter = einzahlung_fetch_participants($pdo, $tenantId, $action);
|
||||
<h2>Einzahlungen für alle Mitarbeiter</h2>
|
||||
|
||||
<?php if ($hatGespeichert): ?>
|
||||
<?php if ($fehlgeschlagen): ?>
|
||||
<?php if ($validierungsFehler !== []): ?>
|
||||
<div class="hint-box error">
|
||||
<p><b>Es wurde nichts gebucht.</b> Bitte korrigiere zuerst:</p>
|
||||
<ul>
|
||||
<?php foreach ($validierungsFehler as $fehler): ?>
|
||||
<li><?php echo saas_html($fehler); ?></li>
|
||||
<?php endforeach; ?>
|
||||
</ul>
|
||||
</div>
|
||||
<?php elseif ($fehlgeschlagen): ?>
|
||||
<div class="hint-box error"><p>Die Einzahlungen konnten nicht gespeichert werden.</p></div>
|
||||
<?php else: ?>
|
||||
<div class="hint-box success"><p><?php echo (int)$eingetragen; ?> Einträge erfolgreich hinzugefügt.</p></div>
|
||||
<?php endif; ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="hint-box">
|
||||
<p>In der Bemerkung könnt ihr festhalten, worum es bei einer Buchung ging. Ein <b>negativer Betrag</b> bucht einen Abzug (z. B. Auszahlung bei Austritt oder eine Erstattung) und braucht deshalb immer eine Bemerkung.</p>
|
||||
<p><small>Wurde dagegen schlicht <b>falsch eingetragen</b>, ist ein Abzug der falsche Weg: Die Auswertung zählt sonst beide Buchungen als Umsatz. Solche Fehler stattdessen unter <a href="letzteneintraege.php">Letzte Einträge</a> stornieren.</small></p>
|
||||
</div>
|
||||
|
||||
<ul class="actions">
|
||||
<li>
|
||||
<form action="einzahlung.php" method="get">
|
||||
@@ -189,13 +285,18 @@ $mitarbeiter = einzahlung_fetch_participants($pdo, $tenantId, $action);
|
||||
echo " <tr>
|
||||
<th>Mitarbeiter</th>
|
||||
<th>Betrag</th>
|
||||
<th>Bemerkung</th>
|
||||
</tr>";
|
||||
foreach ($mitarbeiter as $teilnehmer) {
|
||||
$participantId = $teilnehmer['participant_id'];
|
||||
$participantId = (int)$teilnehmer['participant_id'];
|
||||
$name = saas_html($teilnehmer['display_name']);
|
||||
// Nach einem Validierungsfehler die Eingaben stehen lassen.
|
||||
$betragWert = saas_html($eingaben[$participantId]['betrag'] ?? '');
|
||||
$bemerkungWert = saas_html($eingaben[$participantId]['bemerkung'] ?? '');
|
||||
echo "<tr>";
|
||||
echo "<td><label for='anzahlBetrag[$participantId]'>$name </label></td>";
|
||||
echo "<td><input type='number' name='anzahlBetrag[$participantId]' step='0.01'></td>";
|
||||
echo "<td><input type='number' name='anzahlBetrag[$participantId]' step='0.01' value='$betragWert'></td>";
|
||||
echo "<td><input type='text' name='bemerkung[$participantId]' maxlength='1000' value='$bemerkungWert' placeholder='optional, bei Abzug Pflicht'></td>";
|
||||
echo "</tr>";
|
||||
}
|
||||
echo "</table>";
|
||||
|
||||
Reference in New Issue
Block a user