feat: Sonderführungen aus dem Logbuch in SoFue2 nachtragen
RepoSoFue::ausLogbuch() sucht zu einem Logbuch-Eintrag der Art 'SF' den passenden Datensatz über DATE(wtermin) — bei mehreren Führungen an einem Tag gewinnt die zeitlich nächstgelegene — und setzt über das vorhandene updateAfter() stattgefunden, anzahl_echt, bezahlt und remarks. Aufgerufen wird das im Dispatcher nach dem Commit der Logbuch- Transaktion; die Methode wirft nie, sondern liefert einen Status, der als Feld 'sofue' in der Antwort zurückgeht. bezahlt wird wie im beoanswer-Formular geschrieben (Kasse 50€, Überweisung, Spendenkässle, keine). Da SoFue2 latin1 ist und remarks nur 100 Zeichen fasst, kürzt cp1252Safe() den Text und entfernt nicht darstellbare Zeichen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -588,6 +588,19 @@ class RepoSoFue
|
||||
{
|
||||
return DB::one("SELECT * FROM " . TBL_SOFUE . " WHERE wtermin=? AND deleted=0", [$termin]);
|
||||
}
|
||||
/**
|
||||
* Passende Führung zum Beginn eines Logbuch-Eintrags: gleiches Datum, zeitlich am
|
||||
* nächsten. Der Logbuch-Beginn liegt typisch 0-30 Minuten vor dem wtermin.
|
||||
*/
|
||||
public static function findByLogbuchBeginn(string $beginn): ?array
|
||||
{
|
||||
return DB::one(
|
||||
"SELECT id, wtermin FROM " . TBL_SOFUE
|
||||
. " WHERE deleted=0 AND DATE(wtermin) = DATE(?)"
|
||||
. " ORDER BY ABS(TIMESTAMPDIFF(MINUTE, ?, wtermin)) ASC, id ASC LIMIT 1",
|
||||
[$beginn, $beginn]
|
||||
);
|
||||
}
|
||||
public static function getAfterDate(string $date)
|
||||
{
|
||||
// Accept YYYYMMDD or YYYY-MM-DD; compare by DATE(wtermin)
|
||||
@@ -709,6 +722,75 @@ class RepoSoFue
|
||||
{
|
||||
return DB::exec("UPDATE " . TBL_SOFUE . " SET deleted=1 WHERE id=?", [$id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Spende aus dem Logbuch als bezahlt-Text — gleiche Schreibweise wie das
|
||||
* beoanswer-Formular (beoanswer/src/components/LastButtons.jsx).
|
||||
*/
|
||||
private static function bezahltText(array $p): string
|
||||
{
|
||||
switch ($p['Spende'] ?? '') {
|
||||
case 'bar':
|
||||
// Ganze Beträge ohne Nachkommastellen ("Kasse 50€"), sonst mit Komma
|
||||
// ("Kasse 25,50€") — passt zu den Bestandswerten und in VARCHAR(15).
|
||||
$betrag = (float)($p['SpendeBetrag'] ?? 0);
|
||||
$text = fmod($betrag, 1.0) === 0.0
|
||||
? number_format($betrag, 0, ',', '')
|
||||
: number_format($betrag, 2, ',', '');
|
||||
return 'Kasse ' . $text . '€';
|
||||
case 'ueberw': return 'Überweisung';
|
||||
case 'kasse': return 'Spendenkässle';
|
||||
case 'keine': return 'keine';
|
||||
default: return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Kürzt auf $max Zeichen und entfernt alles, was die latin1-Spalten von SoFue2
|
||||
* nicht darstellen können (z. B. Emoji) — sonst scheitert der UPDATE im Strict Mode.
|
||||
*/
|
||||
private static function cp1252Safe(string $s, int $max): string
|
||||
{
|
||||
// 'none' lässt nicht abbildbare Zeichen (z. B. Emoji) ersatzlos entfallen,
|
||||
// statt sie wie iconv//TRANSLIT durch '?' zu ersetzen.
|
||||
$vorher = mb_substitute_character();
|
||||
mb_substitute_character('none');
|
||||
$cp = mb_convert_encoding($s, 'CP1252', 'UTF-8');
|
||||
mb_substitute_character($vorher);
|
||||
return mb_substr(mb_convert_encoding($cp, 'UTF-8', 'CP1252'), 0, $max);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trägt eine im Logbuch erfasste Sonderführung im Register nach: stattgefunden,
|
||||
* anzahl_echt, bezahlt und remarks. Der Datensatz wird über das Datum gefunden.
|
||||
* Wirft nie — der Logbuch-Eintrag ist zu diesem Zeitpunkt bereits gespeichert.
|
||||
*/
|
||||
public static function ausLogbuch(array $p): array
|
||||
{
|
||||
if (($p['ArtFuehrung'] ?? '') !== 'SF') return ['status' => 'uebersprungen'];
|
||||
$datum = substr((string)($p['Beginn'] ?? ''), 0, 10);
|
||||
try {
|
||||
$treffer = self::findByLogbuchBeginn((string)($p['Beginn'] ?? ''));
|
||||
if (!$treffer) return ['status' => 'kein_termin', 'datum' => $datum];
|
||||
|
||||
self::updateAfter((int)$treffer['id'], [
|
||||
'stattgefunden' => 1,
|
||||
'besucher' => (int)($p['Besucher'] ?? 0),
|
||||
'remark' => self::cp1252Safe((string)($p['Bemerkungen'] ?? ''), 100),
|
||||
'bezahlt' => self::bezahltText($p),
|
||||
]);
|
||||
|
||||
return [
|
||||
'status' => 'ok',
|
||||
'id' => (int)$treffer['id'],
|
||||
'wtermin' => $treffer['wtermin'],
|
||||
'datum' => $datum,
|
||||
];
|
||||
} catch (Throwable $e) {
|
||||
error_log('SoFue-Rueckschreibung: ' . $e->getMessage());
|
||||
return ['status' => 'fehler', 'datum' => $datum];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RepoStatistik
|
||||
@@ -1679,8 +1761,8 @@ class Commands
|
||||
'LB_GET_BEO_ADRESSEN' => 'Logbuch: BEOs mit Adressen (km-Berechnung)',
|
||||
'LB_UPDATE_BEO_KM' => 'Logbuch: km-Entfernung eines BEOs setzen',
|
||||
'LB_LIST_LOGBUCH' => 'Logbuch: Einträge abrufen',
|
||||
'LB_CREATE_LOGBUCH' => 'Logbuch: Eintrag anlegen',
|
||||
'LB_UPDATE_LOGBUCH' => 'Logbuch: Eintrag ändern',
|
||||
'LB_CREATE_LOGBUCH' => 'Logbuch: Eintrag anlegen (trägt Sonderführungen in SoFue2 nach)',
|
||||
'LB_UPDATE_LOGBUCH' => 'Logbuch: Eintrag ändern (trägt Sonderführungen in SoFue2 nach)',
|
||||
'LB_DELETE_LOGBUCH' => 'Logbuch: Eintrag löschen',
|
||||
'LB_GET_BEOS' => 'Logbuch: Guide-BEOs abrufen',
|
||||
'LB_GET_OBJEKTE' => 'Logbuch: Objekte (zuletzt genutzt)',
|
||||
@@ -2192,13 +2274,15 @@ Spendenbescheinigung: \t" . $info['spende'] . "
|
||||
respond(RepoLogbuch::listLogbuch($input));
|
||||
case 'LB_CREATE_LOGBUCH':
|
||||
$newId = RepoLogbuch::createLogbuch($input);
|
||||
respond(['id' => $newId], 201);
|
||||
// Erst nach dem Commit: eine Sonderführung wird im Register nachgetragen,
|
||||
// ein Problem dabei darf den Logbuch-Eintrag nicht zurückrollen.
|
||||
respond(['id' => $newId, 'sofue' => RepoSoFue::ausLogbuch($input)], 201);
|
||||
case 'LB_UPDATE_LOGBUCH':
|
||||
$id = (int)($input['id'] ?? respondError('id missing'));
|
||||
$userId = (int)($input['user_id'] ?? respondError('user_id missing'));
|
||||
$userRole = (string)($input['user_role'] ?? '');
|
||||
RepoLogbuch::updateLogbuch($id, $userId, $userRole, $input);
|
||||
respond(['ok' => true]);
|
||||
respond(['ok' => true, 'sofue' => RepoSoFue::ausLogbuch($input)]);
|
||||
case 'LB_DELETE_LOGBUCH':
|
||||
$id = (int)($input['id'] ?? respondError('id missing'));
|
||||
$userId = (int)($input['user_id'] ?? respondError('user_id missing'));
|
||||
|
||||
Reference in New Issue
Block a user