feat: Sonderführungen automatisch im Register SoFue2 nachtragen — Version 1.13.0

Beim Speichern eines Logbuch-Eintrags der Art 'SF' wird der passende
Datensatz in SoFue2 über das Datum gesucht und dort stattgefunden=1,
anzahl_echt, bezahlt und remarks gesetzt. Gibt es an dem Tag mehrere
Führungen, gewinnt die zeitlich nächstgelegene.

Das Ergebnis kommt als Feld 'sofue' aus der PHP-Bridge zurück und wird
über dem Formular angezeigt — grün bei Erfolg, gelb, wenn keine Führung
gefunden wurde. Der Logbuch-Eintrag wird in dem Fall trotzdem gespeichert.
Die Meldung liegt in MainClient, weil LogbuchForm beim Bearbeiten durch
onSaved() neu gemountet wird.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-08 12:57:22 +02:00
parent cfa1852d5e
commit b5b75e0d77
10 changed files with 78 additions and 13 deletions
+42 -2
View File
@@ -2,13 +2,31 @@
import { useState } from 'react';
import { KUPPELN } from '@/types/logbuch';
import type { Kuppel, LogbuchEintrag } from '@/types/logbuch';
import type { Kuppel, LogbuchEintrag, SofueStatus } from '@/types/logbuch';
import LogbuchForm from '@/components/LogbuchForm';
import LogbuchList from '@/components/LogbuchList';
import Statistik from '@/components/Statistik';
import Fahrkosten from '@/components/Fahrkosten';
import packageJson from '@/package.json';
function datumDe(iso?: string): string {
if (!iso) return '';
const [j, m, t] = iso.slice(0, 10).split('-');
return t && m && j ? `${t}.${m}.${j}` : iso;
}
/** Rückmeldung zur Rückschreibung einer Sonderführung ins Register SoFue2. */
function sofueMeldung(s: SofueStatus): string {
switch (s.status) {
case 'ok':
return `Sonderführung vom ${datumDe(s.wtermin ?? s.datum)} im Register als stattgefunden eingetragen.`;
case 'kein_termin':
return `Keine Sonderführung am ${datumDe(s.datum)} gefunden — das Sonderführungs-Register wurde nicht aktualisiert.`;
default:
return 'Das Sonderführungs-Register konnte nicht aktualisiert werden. Bitte den Eintrag dort von Hand nachtragen.';
}
}
interface Props {
kuerzel: string;
beoId: number;
@@ -22,6 +40,7 @@ export default function MainClient({ kuerzel, beoId, beoName, role }: Props) {
const [refreshKey, setRefreshKey] = useState(0);
const [editEntry, setEditEntry] = useState<LogbuchEintrag | null>(null);
const [backupState, setBackupState] = useState<'idle' | 'running' | 'ok' | 'error'>('idle');
const [sofueHinweis, setSofueHinweis] = useState<SofueStatus | null>(null);
const grafikSrc = '/api/statistik/grafik';
@@ -32,9 +51,10 @@ export default function MainClient({ kuerzel, beoId, beoName, role }: Props) {
const currentUserBeo = { ID: beoId, Kuerzel: kuerzel, Name: beoName };
function handleSaved() {
function handleSaved(sofue?: SofueStatus | null) {
setRefreshKey((k) => k + 1);
setEditEntry(null);
setSofueHinweis(sofue ?? null);
}
function handleEdit(entry: LogbuchEintrag) {
@@ -148,6 +168,26 @@ export default function MainClient({ kuerzel, beoId, beoName, role }: Props) {
Eintrag bearbeiten (ID {editEntry.ID})
</div>
)}
{sofueHinweis && sofueHinweis.status !== 'uebersprungen' && (
<div
className={
'mb-3 text-sm rounded-lg px-3 py-2 border flex justify-between items-start gap-3 ' +
(sofueHinweis.status === 'ok'
? 'text-green-700 bg-green-50 border-green-300'
: 'text-amber-800 bg-amber-50 border-amber-400')
}
>
<span>{sofueMeldung(sofueHinweis)}</span>
<button
type="button"
onClick={() => setSofueHinweis(null)}
className="shrink-0 font-medium hover:opacity-70"
aria-label="Meldung schließen"
>
</button>
</div>
)}
<LogbuchForm
key={`${activeKuppel}-${editEntry?.ID ?? 'new'}`}
kuppel={activeKuppel}
+2 -2
View File
@@ -14,7 +14,7 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{
const body = await request.json();
const { Kuppel, ArtFuehrung, SonderName, Spende, SpendeBetrag, Beginn, Ende, Besucher, beoIds, objekte, Bemerkungen, Wetter } = body;
await phpdb.updateLogbuch(logbuchId, session.beoId, session.role ?? '', {
const result = await phpdb.updateLogbuch(logbuchId, session.beoId, session.role ?? '', {
Kuppel, ArtFuehrung, SonderName, Beginn, Ende,
Spende: Spende ?? null,
SpendeBetrag: SpendeBetrag ?? null,
@@ -26,7 +26,7 @@ export async function PUT(request: NextRequest, { params }: { params: Promise<{
});
triggerBackup();
return NextResponse.json({ ok: true });
return NextResponse.json({ ok: true, sofue: result?.sofue });
} catch (error: unknown) {
if (error instanceof Error) {
if (error.message.includes('404')) return NextResponse.json({ error: 'Eintrag nicht gefunden' }, { status: 404 });
+1
View File
@@ -45,6 +45,7 @@ export async function POST(request: NextRequest) {
});
triggerBackup();
// result enthält id und — bei Sonderführungen — den sofue-Status der Rückschreibung
return NextResponse.json(result, { status: 201 });
} catch (error) {
console.error('POST /api/logbuch:', error);