dev #251

Merged
flfeders merged 10 commits from dev into main 2026-10-25 13:17:14 +00:00
Showing only changes of commit 89185b4b08 - Show all commits

309
docs/fahrtenbuch-konzept.md Normal file
View File

@@ -0,0 +1,309 @@
# Konzept: Fahrtenbuch mit direkter Teltonika-Anbindung
## Zielbild
Die Teltonika-OBD-II-Tracker senden ihre AVL-Daten direkt an FEDEO. Traccar entfällt vollständig aus der produktiven Kette.
```text
Teltonika-Tracker
│ TCP/TLS, Codec 8 Extended
▼
FEDEO Tracking-Ingest
│ atomare Speicherung, danach ACK
▼
PostgreSQL ──► Fahrtbildungs-Worker ──► Fahrtenbuch-API ──► Nuxt-Oberfläche/PDF-Export
```
Die empfohlene Lösung besteht aus einem kleinen, eigenständigen Dienst `tracking-server`, den bestehenden FEDEO-Diensten und PostgreSQL. Der Empfangsdienst darf nicht Teil des HTTP-Prozesses sein: Teltonika verwendet langlebige TCP-Verbindungen und ein Binärprotokoll, während das bestehende Backend Fastify/HTTP bedient. Die getrennten Prozesse können unabhängig neu gestartet und skaliert werden.
Ein zusätzlicher Message-Broker ist für die derzeit erkennbare Größenordnung nicht nötig. Der Tracker puffert Daten bei Verbindungsproblemen; PostgreSQL übernimmt die dauerhafte Übergabe an den Fahrtbildungs-Worker.
## Annahmen und noch zu bestätigende Angaben
- Mit „Teltonika OBD2“ ist voraussichtlich ein Gerät der FMx003-Familie gemeint, zum Beispiel FMB003, FMC003 oder FMM003. Vor der Implementierung müssen exaktes Modell, Firmwarestand und Mobilfunkvariante feststehen.
- Die Geräte werden auf **Codec 8 Extended über TCP/TLS** eingestellt. Teltonika empfiehlt Codec 8 Extended, weil nur damit auch AVL-IDs oberhalb von 255 und variable I/O-Werte vollständig übertragen werden können.
- Das Fahrtenbuch soll nicht nur eine Streckenübersicht, sondern bei Bedarf ein steuerlich nutzbares elektronisches Fahrtenbuch für Deutschland sein.
- Ein OBD-Tracker erkennt das Fahrzeug, aber nicht automatisch die fahrende Person. Ohne zusätzlichen BLE-/iButton-Fahrerschlüssel wird der im Fahrzeug hinterlegte Standardfahrer vorgeschlagen und muss gegebenenfalls geändert werden.
## 1. Tracking-Ingest
### Aufgabe
Ein neues Paket beziehungsweise ein neuer Dienst `tracking-server/` wird in TypeScript implementiert. Er verwendet `node:tls` beziehungsweise während der lokalen Entwicklung `node:net` und übernimmt ausschließlich:
1. TCP-Verbindung und IMEI-Handshake,
2. Prüfung, ob die IMEI einem aktiven FEDEO-Fahrzeug zugeordnet ist,
3. Framing und Parsen von Codec 8 (`0x08`) und Codec 8 Extended (`0x8E`),
4. Prüfung von Paketlänge, doppelter Datensatzanzahl und CRC-16/IBM,
5. normalisierte Speicherung aller AVL-Datensätze in einer Datenbanktransaktion,
6. Teltonika-ACK erst nach erfolgreichem Commit,
7. Aktualisierung von `last_seen_at` und einfachen Betriebsmetriken.
Der produktive Gerätevertrag ist Codec 8 Extended. Codec 8 sollte der Parser trotzdem beherrschen, damit ein falsch oder noch nicht umkonfiguriertes Gerät sichtbar diagnostiziert werden kann.
Bei Datenbankfehlern wird **kein** positiver ACK gesendet. Das Gerät behält die Datensätze und versucht die Übertragung erneut. Doppelte Zustellungen müssen durch einen fachlichen Idempotenzschlüssel wirkungslos bleiben. Unbekannte IMEIs werden abgewiesen und nur als begrenztes Sicherheitsereignis protokolliert.
### Parser-Grenzen
Der Binärparser darf nicht mit Fachlogik vermischt werden. Sein Ergebnis ist ein neutrales Objekt:
```ts
type AvlRecord = {
recordedAt: Date
priority: number
longitude: number
latitude: number
altitude: number
angle: number
satellites: number
speedKmh: number
eventIoId: number
io: Record<number, bigint | Buffer>
}
```
Für die Fachlogik gibt es danach ein modell- und firmwareabhängiges I/O-Mapping. Relevante Standardwerte sind typischerweise:
- AVL 239: Zündung,
- AVL 240: Bewegung,
- AVL 16: Teltonika-Gesamtkilometerzähler,
- AVL 389: OEM-Gesamtkilometerstand,
- AVL 256: VIN,
- AVL 134: Fahrtdistanz.
Einheiten und Verfügbarkeit werden nicht im Parser fest verdrahtet, sondern über getestete Definitionen je Gerätefamilie normalisiert. Insbesondere der OEM-Kilometerstand ist vom unterstützten Fahrzeug und der auf das Gerät geladenen OBD-Konfiguration abhängig.
### Sicherheit und Betrieb
- Eigener TCP-Port, zum Beispiel `5027`, mit TLS 1.2; die konkrete TLS-Unterstützung muss für Modell und Firmware geprüft werden.
- IMEI-Zulassungsliste aus der Datenbank, Rate-Limit pro Quell-IP und harte Grenzen für Paketgröße und Verbindungsdauer.
- Keine Befehlssteuerung in Version 1. Codec 12/14 kann später getrennt ergänzt werden.
- Metriken: aktive Verbindungen, akzeptierte/abgelehnte Pakete, CRC-Fehler, unbekannte IMEIs, Verzögerung `received_at - recorded_at`, letzter Datensatz je Tracker und offene Fahrten.
- Alarm, wenn ein aktiver Tracker länger als eine konfigurierbare Zeit keine Daten liefert.
## 2. Datenmodell
Die vorhandene Tabelle `vehicles` bleibt die fachliche Fahrzeugquelle. Die generische Tabelle `devices` sollte nicht mit GPS-Sonderfeldern überladen werden.
### `vehicle_trackers`
Zuordnung und Gerätekonfiguration:
- `id uuid`, `tenant bigint`, `vehicle bigint`
- `imei text unique`, `model text`, `firmware text`
- `protocol text`, `enabled boolean`
- `installed_at`, `removed_at`, `last_seen_at`
- `io_profile_version text`
- eindeutige aktive Zuordnung eines Trackers zu einem Fahrzeug
Eine zeitliche Zuordnung ist wichtig: Beim Umbau eines Trackers dürfen alte Positionen nicht nachträglich dem neuen Fahrzeug zugerechnet werden.
### `tracking_packets`
Technische Nachvollziehbarkeit und gezielte Fehleranalyse:
- Tracker, Empfangszeit, Codec, CRC, Datensatzanzahl
- SHA-256 des vollständigen Frames als Idempotenzschlüssel
- optionaler Rohframe als `bytea` mit kurzer, konfigurierbarer Aufbewahrung, zum Beispiel 30 oder 90 Tage
- Parse-Status und Fehlercode
### `tracking_positions`
Unveränderliche normalisierte Messpunkte:
- Mandant, Tracker und die zum Messzeitpunkt gültige Fahrzeugzuordnung
- Gerätezeit `recorded_at` und Serverzeit `received_at`
- Koordinaten, Höhe, Richtung, Geschwindigkeit, Satelliten, Priorität
- normalisierte Werte `ignition`, `movement`, `odometer_m`, `oem_odometer_m`
- `event_io_id` und vollständige I/O-Werte als `jsonb`
- Qualitätsmerkmale wie `valid_gnss`, Zeitabweichung und Kilometerstandsquelle
Eindeutigkeit entsteht aus Tracker, Gerätezeit und einem stabilen Datensatz-Fingerabdruck. Für größere Datenmengen kann die Tabelle später monatsweise nach `recorded_at` partitioniert werden. Indizes werden mindestens auf `(vehicle, recorded_at)` und `(tracker, recorded_at)` benötigt.
### `tracking_trips`
Das fachliche Fahrtenbuch:
- Fahrzeug, Fahrer, Start/Ende und Start-/Zielkoordinaten
- Start-/Zieladresse als zum Abschlusszeitpunkt gespeicherter Text
- Kilometerstand Beginn/Ende, berechnete Distanz und jeweilige Quelle
- Klassifizierung `betrieblich`, `privat` oder `wohnung_erste_taetigkeitsstaette`
- Reisezweck, Kunde/Geschäftspartner sowie optionale FEDEO-Verknüpfungen zu Kunde, Projekt und Termin
- Status `offen`, `zu_pruefen`, `bestaetigt`, `gesperrt`
- Qualitätsstatus mit konkreten Hinweisen, zum Beispiel fehlender OEM-Kilometerstand, GPS-Lücke oder unplausibler Sprung
- Referenzen auf ersten und letzten Messpunkt
### `tracking_trip_revisions` und `tracking_logbook_exports`
Fahrten werden nie unbemerkt überschrieben. Jede Änderung erzeugt eine unveränderliche Revision mit altem und neuem Wert, Benutzer, Zeit, Änderungsgrund sowie einer Hash-Verkettung zur vorherigen Revision. Auch Zusammenführen, Teilen und Löschen sind fachliche Revisionen; ein physisches Löschen bestätigter Fahrten ist nicht erlaubt.
Ein abgeschlossener Monats- oder Jahresexport speichert PDF, maschinenlesbare CSV/JSON-Daten und ein Manifest mit SHA-256-Prüfsummen. Der Export enthält die Änderungshistorie in gewöhnlich einsehbarer Form. „Gesperrt“ bedeutet: weitere Korrekturen erzeugen eine sichtbare Folgerevision und gegebenenfalls eine neue Exportversion.
## 3. Fahrtbildung
Die Fahrtbildung läuft als wiederholbarer Worker auf Ereigniszeit, nicht auf Empfangszeit. Das ist wichtig, weil Tracker bei Funklöchern ältere Datensätze nachliefern.
### Zustandsautomat je Tracker
```text
STEHT
└─ Zündung an + Bewegung/Geschwindigkeit ─► FÄHRT
FÄHRT
├─ weitere Punkte ────────────────────────► FÄHRT
└─ Zündung aus + Nachlaufzeit ────────────► STEHT + Fahrt abschließen
```
- Primäres Start-/Endesignal ist AVL 239 (Zündung), plausibilisiert mit Bewegung und Geschwindigkeit.
- Eine konfigurierbare Nachlaufzeit von zunächst 120 Sekunden verhindert Fahrtteilungen an Ampeln oder bei kurzen Signalwechseln.
- Eine Fahrt wird nicht sofort endgültig: Ein Watermark, zum Beispiel zehn Minuten nach dem letzten bekannten Gerätezeitpunkt, erlaubt verspätete Datensätze. Betroffene offene Zeitfenster werden deterministisch neu berechnet.
- Bei fehlendem Zündungssignal greift ein klar markierter Fallback aus Bewegung, Geschwindigkeit und Stillstandszeit.
- Kilometerstandsquelle in absteigender Qualität: OEM-Gesamtkilometerstand (AVL 389), kalibrierter Teltonika-Gesamtkilometerzähler (AVL 16), Fahrtdistanz/Positionssumme. FEDEO speichert Quelle und Abweichung, statt Werte still zu vermischen.
- Unplausible GNSS-Punkte werden nicht für die Distanz verwendet, bleiben aber unverändert gespeichert.
- Zeitrücksprünge, Kilometerzählersprünge, GPS-Lücken und Tracker-Ausbau erzeugen Prüffälle und niemals stillschweigend „saubere“ Fahrten.
Die Zählerwerte müssen im Pilotfahrzeug gegen den Tachostand geprüft werden. Liefert ein Fahrzeug keinen OEM-Gesamtkilometerstand, wird einmalig ein dokumentierter Tachostand mit dem Teltonika-Zähler verknüpft. Der daraus entstehende Offset bleibt versioniert.
## 4. Bedienung in FEDEO
Der vorhandene, bislang nur konfigurierte Ressourcenschlüssel `trackingtrips` kann weiterverwendet werden. Für das Fahrtenbuch ist dennoch eine eigene Seite sinnvoller als die generische Standard-Entity-Maske.
### Ansichten
- **Fahrten:** chronologische Liste mit Fahrzeug, Fahrer, Start/Ziel, Zeit, Kilometerständen, Distanz, Klassifizierung, Zweck und Prüfstatus.
- **Prüfen:** schnelle Stapelbearbeitung aller offenen Fahrten; Vorschläge aus Kalender, Kunde/Projekt und wiederkehrenden Zielen.
- **Karte:** Route einer ausgewählten Fahrt, nicht standardmäßig die vollständige Mitarbeiterhistorie.
- **Fahrzeug:** Trackerstatus, letzter Empfang, Kilometerstandsqualität und offene Warnungen.
- **Export:** Monats-/Jahresabschluss, PDF-Vorschau und Prüfdateien.
Eine betriebliche Fahrt verlangt Ziel, Reisezweck und Geschäftspartner beziehungsweise einen konkreten betrieblichen Vorgang. Privatfahrten speichern für normale Benutzer nur die steuerlich erforderlichen Werte; die Sichtbarkeit genauer Privat-Routen sollte über eine eigene Berechtigung begrenzt werden.
### API-Schnittstellen
Spezialisierte, mandantengeprüfte Routen sind der generischen Ressourcen-API vorzuziehen:
- `GET /api/tracking/trips`
- `GET /api/tracking/trips/:id`
- `PATCH /api/tracking/trips/:id/classification`
- `POST /api/tracking/trips/:id/confirm`
- `POST /api/tracking/trips/:id/split`
- `POST /api/tracking/trips/merge`
- `GET /api/tracking/vehicles/:id/status`
- `POST /api/tracking/exports`
- `GET /api/tracking/exports/:id`
Ändernde Aufrufe verlangen einen Änderungsgrund, sobald eine Fahrt bereits bestätigt war. Berechtigungen sollten mindestens `tracking.view`, `tracking.edit_own`, `tracking.edit_all`, `tracking.view_private_routes`, `tracking.manage_devices`, `tracking.lock` und `tracking.export` unterscheiden.
## 5. Steuerliche Nachvollziehbarkeit und Datenschutz
Für ein deutsches elektronisches Fahrtenbuch reichen GPS-Punkte allein nicht. Nach der Rechtsprechung müssen die Aufzeichnungen zeitnah, fortlaufend und in geschlossener Form vorliegen. Änderungen müssen technisch ausgeschlossen oder in ihrer Reichweite direkt im Fahrtenbuch erkennbar sein. Für betriebliche Fahrten gehören grundsätzlich Datum, Kilometerstand zu Beginn und Ende, Reiseziel, Reisezweck und aufgesuchte Geschäftspartner dazu.
Deshalb sind folgende Punkte keine spätere Zusatzfunktion, sondern Bestandteil der ersten fachlich nutzbaren Version:
- append-only Messdaten und Änderungshistorie,
- sichtbare Revisionsangaben im Fahrtenbuch und im Export,
- Erinnerung und Eskalation für zeitnahe Klassifizierung,
- Vollständigkeits- und Plausibilitätsprüfung pro Fahrzeug,
- reproduzierbare, versionierte Monatsabschlüsse,
- dokumentierte Verfahrensbeschreibung und Wiederherstellungstests.
Das Konzept ist eine technische Auslegung, keine Zusicherung der steuerlichen Anerkennung. Export und Verfahrensdokumentation sollten vor dem produktiven steuerlichen Einsatz mit der Steuerberatung abgestimmt werden.
Standortdaten von Beschäftigten erfordern zudem ein Rollen- und Löschkonzept, Zweckbindung, transparente Information und gegebenenfalls Beteiligung des Betriebsrats. Rohpunkte sollten nur so lange wie fachlich und rechtlich erforderlich gespeichert werden. Reverse-Geocoding erfolgt nur für Start und Ziel, asynchron und mit Cache; ein selbst betriebener Dienst oder ein vertraglich geprüfter Anbieter vermeidet unnötige Weitergabe vollständiger Bewegungsprofile.
## 6. Gerätekonfiguration
Für einen Pilot-Tracker wird ein versioniertes Teltonika-Konfigurationsprofil erstellt:
- Hauptserver: FEDEO-Domain und Tracking-Port
- Transport: TCP mit TLS, sofern Modell/Firmware dies unterstützen
- Datenprotokoll: Codec 8 Extended
- Datenerfassung in Bewegung zunächst alle 30 Sekunden oder 100 Meter sowie bei relevanter Richtungsänderung
- Ereignisdatensatz bei Zündung an/aus
- AVL 239, 240, 16, 389, 256 und 134 aktivieren, soweit vom Gerät unterstützt
- lokale Pufferung und älteste Datensätze zuerst senden
- Serverbestätigung auf AVL-Ebene
Das Standardintervall von mehreren Minuten ist für belastbare Start-/Zielpunkte und Routen zu grob. Die endgültigen Intervalle werden im Pilot gegen Datenvolumen, SIM-Tarif und Fahrzeugbatterie getestet.
## 7. Einführung ohne Datenverlust
### Phase 0 – Geräte-Spike
Ein reales Gerät und Fahrzeug werden festgelegt. Modell, Firmware, IMEI, empfangene AVL-IDs, Zündungslogik und Kilometerstandsquellen werden dokumentiert. Ein aufgezeichneter Binärdatenbestand aus definierten Testfahrten bildet anschließend einen dauerhaft nutzbaren Parser- und Fahrtbildungs-Testvektor.
### Phase 1 – Shadow-Ingest
Teltonika unterstützt einen zweiten Server und einen Duplikatmodus. Während der Pilotphase sendet das Gerät an Traccar **und** FEDEO. FEDEO bestätigt und speichert Daten, beeinflusst aber noch kein produktives Fahrtenbuch. Über mehrere Wochen werden Paketvollständigkeit, Kilometerstände und erkannte Fahrten verglichen.
### Phase 2 – Fahrtenbuch-Pilot
Fahrtbildung, Prüfansicht, Klassifizierung, Revisionen und Export werden für wenige Fahrzeuge aktiviert. Kriterien für die Freigabe:
- kein Paketverlust bei Neustart oder Datenbankunterbrechung,
- idempotente Verarbeitung wiederholter Pakete,
- reproduzierbare Fahrtbildung bei ungeordneter Zustellung,
- Kilometerstandsabweichungen erklärt und sichtbar,
- vollständiger Export inklusive Änderungen,
- Rechte- und Mandantentrennung getestet.
### Phase 3 – Umschaltung
Nach erfolgreichem Parallelbetrieb wird FEDEO Hauptserver. Traccar bleibt zunächst als Teltonika-Backupserver konfiguriert, anschließend wird es entfernt. Der Rückfall besteht in der ersten Zeit lediglich aus dem Zurückschalten des Geräteprofils; ein Datenbank-Rollback ist nicht erforderlich.
## 8. Empfohlener Implementierungsumfang
### Iteration 1: belastbarer Empfang
- Schemata `vehicle_trackers`, `tracking_packets`, `tracking_positions`
- Codec-8/8E-Parser mit CRC- und offiziellen Beispielvektoren
- TCP/TLS-Dienst, IMEI-Zuordnung, ACK nach Commit
- Trackerstatus im Backend und technische Metriken
- Docker-Compose-Erweiterung und Betriebsdokumentation
### Iteration 2: Fahrten
- Schemata `tracking_trips` und Qualitätsmeldungen
- deterministischer Fahrtbildungs-Worker
- Listen-, Detail- und Kartenansicht
- manuelle Klassifizierung und Fahrerzuordnung
### Iteration 3: belastbares Fahrtenbuch
- unveränderliche Revisionen und Hash-Verkettung
- zeitnahe Erinnerungen, Bestätigung und Monatsabschluss
- PDF/CSV/JSON-Export mit Prüfmanifest
- Projekt-/Kunden-/Kalender-Vorschläge
- Rechte-, Datenschutz- und Verfahrensdokumentation
## 9. Tests, die vor Abschaltung von Traccar bestehen müssen
- fragmentierte und zusammengefasste TCP-Frames
- ungültige Länge, CRC, Codec-ID, IMEI und Datensatzanzahl
- Disconnect vor und nach ACK
- dasselbe Paket mehrfach und gleichzeitig zugestellt
- mehrere AVL-Datensätze pro Paket
- negative Koordinaten und variable I/O-Felder aus Codec 8 Extended
- Offline-Nachlieferung über mehrere Stunden in falscher Empfangsreihenfolge
- Zündung an ohne Bewegung, kurze Stopps, Tunnel, GPS-Sprung und Mitternacht
- fehlender oder zurückgesetzter Kilometerzähler
- Trackerwechsel zwischen Fahrzeugen
- Mandantentrennung jeder API-Route
- Änderung einer bestätigten und einer gesperrten Fahrt
- Export-Reproduzierbarkeit und Prüfung der Hash-Kette
## Empfehlung
Zuerst sollte **nicht** die Oberfläche gebaut werden, sondern ein zwei- bis dreitägiger Geräte-Spike mit einem konkreten Tracker. Danach ist Iteration 1 überschaubar und risikoarm. Der direkte Teltonika-Empfang selbst ist technisch klar definiert; die eigentliche Sorgfalt liegt in verspäteten Daten, Kilometerstandsqualität, Fahreridentität und der unveränderlich sichtbaren Bearbeitungshistorie.
## Quellen
- [Teltonika: Codec 8 und Codec 8 Extended](https://wiki.teltonika-gps.com/view/Teltonika_Data_Sending_Protocols)
- [Teltonika: Empfehlung für Codec 8 Extended](https://wiki.teltonika-gps.com/view/Which_data_protocol_to_choose_Codec_8_or_Codec_8_Extended)
- [Teltonika: FMC003 Data Sending Parameters](https://wiki.teltonika-gps.com/view/FMC003_Teltonika_Data_Sending_Parameters_ID)
- [Teltonika: FMB003 Trip-/Odometer-Einstellungen](https://wiki.teltonika-gps.com/view/FMB003_Trip/Odometer_settings)
- [Teltonika: FMB003 GPRS-, Zweitserver- und TLS-Einstellungen](https://wiki.teltonika-gps.com/view/FMB003_GPRS_settings)
- [Teltonika: OEM-Gesamtkilometerstand und Fahrzeugkompatibilität](https://wiki.teltonika-gps.com/view/Pay_as_You_Drive)
- [Bundesfinanzhof: Anforderungen an ein elektronisches Fahrtenbuch, VI B 37/23](https://www.bundesfinanzhof.de/de/entscheidung/entscheidungen-online/detail/STRE202450011/)
- [Bundesfinanzhof: Inhalt und geschlossene Form des Fahrtenbuchs, VI R 33/10](https://www.bundesfinanzhof.de/de/entscheidung/entscheidungen-online/detail/STRE201210116/)
- [BMF: Einkommensteuer-Handbuch 2025, Anhang 16 III](https://lsth.bundesfinanzministerium.de/esth/2025/B-Anhaenge/Anhang-16/III/inhalt.html)