30 Commits

Author SHA1 Message Date
bfdd4d2307 feat(sync): Anschrift je Kontaktquelle mitführen
Beleg-, Liefer-, Rechnungsadresse, Ansprechpartner und Kundenstamm tragen
jetzt neben Namensblock und Kanälen auch ihre Anschrift (Straße, Nr., PLZ,
Ort, Land aus dem Länderstamm, Adresszusatz), sofern im ERP gepflegt.
Migration 0035 ergänzt die Spalten an delivery_contact_sources;
ContactSource.address in der API.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 16:26:00 +02:00
c9d0ccfdef fix(sync): Lieferadresse nicht mehr feldweise mischen, Adresszusatz statt Land
- Sync nimmt die Lieferadresse als Ganzes: mit eigener LieferAdressId
  alle Felder aus ihr, sonst alle aus der Belegadresse. Das bisherige
  COALESCE je Feld hat leere Felder mit Werten des Bestellers gefüllt
  (z. B. dessen Ortsteil an eine fremde Anschrift gehängt).
- ERP-Freitext `Adressen.Land` (praktisch Ortsteil/Etage/Hinweis) wird
  zum Adresszusatz `Address.addition`; `country` kommt jetzt aus dem
  Länderstamm (`LandID` → `Laender.Land`). Deutschland-Kürzel und reine
  Landnamen sind kein Zusatz.
- Migration 0034: `customers.address_addition`, `deliveries.snap_addition`;
  Bestandstext aus `country` wandert in den Zusatz.
- Lieferbericht und Beleg-Ansicht: Land nur außerhalb Deutschlands,
  Zusatz als eigene Zeile „Zusatz: …".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 16:13:13 +02:00
af6d522290 feat(report): abweichenden Empfänger im Lieferbericht ausweisen
Weicht Lieferadresse oder Empfänger (ERP-LieferAdressId) vom Besteller ab,
führt der Abschnitt „Kunde & Lieferadresse" Besteller und Empfänger
getrennt auf — jeweils mit Anschrift und Telefonnummern — plus Hinweis
„Abweichende Lieferadresse". Sonst bleibt der Abschnitt unverändert.

Erkennung wie in der App: andere Anschrift ODER anderer Name an der
Lieferadresse (gegen die Belegadresse), Schreibweise egal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 15:57:11 +02:00
7cb054cf6c feat(report): bei offenem Betrag 0 den Grund nennen ("bei Bestellung bezahlt")
Ohne offenen Betrag gab es im Abschnitt "Zahlung" keine Aussage zur
Abwicklung, nur die Zahlungsmethode vom Beleg (z. B. "Bar"). Jetzt steht
dort "Zahlungsabwicklung: Nicht erforderlich, ..." mit Begruendung:
bereits bei Bestellung bezahlt, durch Anzahlung und Gutschrift oder nur
durch Gutschrift ausgeglichen, bzw. kein Warenwert. Unit-Tests dazu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 15:25:13 +02:00
695ac39c19 feat(abschluss): interne Notiz des Fahrers beim Abschluss
- CompleteDeliveryAcknowledgements.internalNote (optional, getrimmt,
  max. 2000 Zeichen) wird atomar mit dem Abschluss gespeichert
  (delivery_completions.internal_note)
- Bewusst keine delivery_notes-Zeile: loest weder die Kunden-Bestaetigung
  der Notizen aus noch erscheint sie im Notizen-Step der App
- Lieferbericht: eigener Abschnitt "Interne Notiz" mit Zeitpunkt und Fahrer
- Bericht: Betrag in "Betrag erhalten" ans Zeilenende (Helvetica setzt nach
  dem Euro-Zeichen zu eng)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 14:41:05 +02:00
caaf88ac3d feat(report): Zahlungsabwicklung im Lieferbericht dokumentieren
- Abschnitt "Zahlung": Zahlungsart mit Ergebnis (erhalten / bestaetigt),
  Zahlbetrag, Zeitpunkt, erfassender Fahrer und Fahrzeug der beim
  Abschluss gueltigen Zahlung (vor dem Abschluss: juengster Eintrag).
  Abschluesse ohne Zahlungs-Step behalten die bisherige Zeile
  "Betrag erhalten"
- Neuer Abschnitt "Protokoll - Zahlungsabwicklung" mit allen Eintraegen
  inkl. Korrekturen; Status "beim Abschluss" / "aktuell" / "ersetzt"
- Report-Daten: ReportPayment + ReportCompletion.payment_id

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 14:28:41 +02:00
954c5f52b2 feat(zahlung): Zahlungsabwicklung als eigenes Protokoll (POST /deliveries/{id}/payment)
- Neue Tabelle delivery_payments (append-only, idempotent ueber
  client_event_id): Methode + Code-Snapshot, server-seitig berechneter
  offener Betrag, Fahrer, Fahrzeug, Zeitpunkt
- Endpoint prueft unter Zeilen-Lock: Lieferung aktiv, Methode aktiv,
  offener Betrag > 0 und identisch mit dem vom Fahrer bestaetigten Betrag
- Offener Betrag als gemeinsamer Helper (open_amount_cents) fuer
  Zahlungsprotokoll und Abschluss
- Abschluss-Gate: gueltige protokollierte Zahlung erfuellt die
  Inkasso-Pflicht und liefert die Methode; delivery_completions.payment_id
  verknuepft den Abschluss mit der Zahlung. Altes payment_collected-Flag
  bleibt fuer aeltere App-Versionen gueltig
- Tour-Aggregat und Admin-Belegdetails liefern die juengste Zahlung
- Einzel-Reset loescht auch das Zahlungsprotokoll
- Integrationstest (ignored, braucht Wegwerf-DB) fuer Protokoll + Gate

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 14:04:35 +02:00
ed27859293 fix(cron): Cron-Ausdruecke in server.timezone auswerten statt UTC
Job::new_async interpretiert den Cron-Ausdruck in UTC - '0 0 17 * * *'
feuerte dadurch erst um 19:00 deutscher Zeit (CEST), der 17-Uhr-Import
fuer 'morgen' blieb aus. Alle drei Scheduler (ERP-Import, Report-Retry,
Signatur-Cleanup) nutzen jetzt Job::new_async_tz mit der konfigurierten
Fach-Zeitzone, sodass die Uhrzeit in der Config als Ortszeit gilt.

Hinweis: tokio-cron-scheduler ermittelt den UTC-Offset einmalig beim
Anlegen des Jobs (Serverstart); ein DST-Wechsel greift erst nach Neustart.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-16 18:24:17 +03:00
f74b6c608a fix(time): konfigurierbare Fach-Zeitzone statt UTC/OS-Local fuer alle Datumsangaben
Lieferbericht zeigte UTC (08.09. 22:54 statt 09.09. 00:54): renderer.dt()
formatierte DateTime<Utc> ohne Umrechnung. Weitere Stellen hingen an
chrono::Local (OS-Zone des Hosts) bzw. hardcodiertem 'Europe/Berlin'.

Neu: server.timezone (IANA, Default Europe/Berlin, chrono-tz) und
durchgaengig UTC-Instant -> konfigurierte Zone:
- Lieferbericht: alle Zeitstempel (Abgeschlossen, Notizen, Scans, erzeugt am)
- Belegansicht (fmt_datetime)
- ERP-Rueckschreibung delivered_at (Wanduhrzeit)
- 'heute'-Ableitung: /me/tours, /dev/resync, /admin/import-erp,
  Import-Cron + Startup-Catch-up
- Completion-Repo: AT TIME ZONE als Bind-Param statt Literal

Speicherung bleibt UTC (TIMESTAMPTZ / DateTime<Utc>) - nur Anzeige und
Kalendertag-Ableitung nutzen die Zone.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-09 01:11:06 +02:00
54ed619396 feat(report): Kundennummer an DOCUframe-Makro _SV_assignDeliveryReport uebergeben
Das Makro ordnet den Lieferbericht dem Vorgang des Kunden zu und braucht
dafuer jetzt zusaetzlich die im ERP hinterlegte Kundennummer:
  { objectId, belegnummer, kundennummer }

- AssignReportRequest + Gateway-Port um kundennummer (String) erweitert
- AttachmentRepository.delivery_customer_number(): liest
  customers.erp_customer_id (= ERP Kunden.Kundennummer) ueber
  deliveries.customer_id
- ProcessDeliveryReportUseCase liest die Nummer aus der DB (nicht aus dem
  Render-Ergebnis), damit sie auch im Retry-Pfad ohne erneutes Rendern vorliegt

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-09 00:44:26 +02:00
17dcc42291 feat(tours): GET /me/tours mit optionalem ?date= (statt /me/tours/today)
Fahrer können jetzt die Tour eines beliebigen Datums abrufen (z. B. morgen
vorab). Ohne date-Param bleibt serverseitig 'heute' (Local::now bzw.
Dev-today_override); Praezedenz: requested_date > today_override > Uhr.
Endpoint umbenannt /me/tours/today -> /me/tours (semantisch sauberer).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-09-01 15:41:28 +02:00
a30f2778fd fix(tours): Tagesumschlag nach Ortszeit statt UTC
'Heute' wurde per Utc::now().date_naive() bestimmt. Bei CEST (UTC+2)
wechselte der Tag dadurch erst um 02:00 Ortszeit — der Fahrer sah nach
Mitternacht noch die Touren von gestern. Umgestellt auf Local::now() in
list_my_tours_today sowie den Import-/Resync-Defaults und dem
Scheduler-Catch-up.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 00:10:53 +02:00
f4f534463d refactor(report): Lieferbericht-Sektion "Dienstleistungen" → "Checkliste"
Konsistent zur App-Umbenennung (Step "Services" → "Checkliste"): die
Report-Sektion für delivery_services heißt im PDF jetzt "Checkliste".
Nur Anzeige/Wording (+ Kommentare), Datenmodell unverändert.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 23:32:50 +02:00
d7a63b5df3 feat(erp): Writeback schreibt abgeleitete Beleg-Felder mit, _SV_DELIVERY raus
Beim Mengen-Rückschreiben werden jetzt auch die von der ERPframe-Engine
mitgeführten abgeleiteten Felder gesetzt, damit der Beleg konsistent bleibt
(Lagerbestand bucht das Lager weiterhin manuell — keine Lagerbewegung):

- Belegzeile: StatUmsatz, StatKosten (linear aus Alt-Stand skaliert),
  Lagerverteilung (XML neu), row_update_time. VPEMenge NICHT (berechnete
  Spalte → Fehler 271).
- Belegkopf: zusätzlich StatKosten, kalk (Kalkulations-XML), Druckkennzeichen,
  row_update_time.
- Entfernt: mark_delivered (_SV_DELIVERY_DELIVERED_AT/_SV_DELIVERY_STATE) —
  wird nicht mehr benötigt.

Alle geschriebenen Spalten gegen sys.computed_columns verifiziert.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 23:25:09 +02:00
09cd45d67c revert(erp): Belegzeilen-Mengen wieder ins ERP zurückschreiben
Rücknahme der Entkernung: set_line_menge + die Schleife in run() sind
wieder aktiv, d. h. die tatsächlich ausgelieferte Menge wird pro Belegzeile
gesetzt. Das umgeht bewusst die ERPframe-Engine (keine Lagerbuchung) — das
Lager verbucht den Bestand jetzt MANUELL nach Rückgabe des Geräts.

Gutschrift-Toggle + Vier-Augen-Review bleiben unverändert bestehen.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 21:55:52 +02:00
02aa216bc6 feat(admin): GET /admin/belege/{belegnummer}/html - HTML-Detailansicht
Rendert dieselben Daten wie der JSON-Detail-Endpunkt als schoen kategorisierte
HTML-Seite (askama, compile-time-Template). Der Handler destilliert das
DeliveryDetails-Aggregat in ein flaches View-Modell (Artikel-/Lagernamen
aufgeloest, Euro/Datum formatiert, deutsche Status-/Rollen-Labels); die
Template-Datei bleibt frei von Domain-Logik. Kategorien: Uebersicht, Kunde,
Lieferadresse, Positionen (Tabelle, veraenderte Zeilen hervorgehoben),
Geld-Gutschrift, Dienstleistungen, Notizen, Kontakte. Nutzt den bestehenden
get_delivery_details-Use-Case; admin-key-geschuetzt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 00:11:05 +02:00
af034250e7 feat(admin): GET /admin/belege/{belegnummer} - volle Lieferdetails
Neuer Admin-Endpunkt, der zu einer ERP-Belegnummer das komplette Detail-Paket
einer Lieferung liefert (Kopf, Positionen mit Scan-Ständen, Kunde +
Ansprechpartner, referenzierte Artikel/Lager, Notizen, Geld-Gutschrift,
Dienstleistungswerte, Kontakte). Jede Lieferung, unabhaengig vom Status;
404 bei unbekannter Belegnummer.

Umsetzung: TourRepository::find_tour_id_by_belegnummer loest die Belegnummer
auf ihre tour_id auf; der Use Case laedt das bestehende Tour-Aggregat und
destilliert daraus in-memory die eine Lieferung samt referenzierter Stammdaten
(neues DTO DeliveryDetails). Kein neuer grosser Query.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 23:42:57 +02:00
7c6d883d44 feat(admin): completed-deliveries ohne Parameter = ALLE ausgelieferten Belege
Die "mindestens eine Grenze"-Sperre (400) entfernt. Ruft man
/admin/completed-deliveries ohne day/from/to, kommt jetzt der komplette
Bestand aller ausgelieferten (abgeschlossenen) Belege zurück, datumsunabhängig.
Der Repo-Query unterstützt from=NULL/to=NULL bereits.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 22:33:07 +02:00
865fcfcd23 feat(admin): GET /admin/belege/{belegnummer}/positions-modified
Neuer Admin-Endpunkt, der zu EINER ERP-Belegnummer das positions_modified-Flag
liefert (Stück-Gutschrift auf einer Belegzeile ODER aktive Geld-Gutschrift) —
gleiche Definition wie in completed-deliveries, aber pro Beleg und ohne
Abschluss-Voraussetzung. 404 bei unbekannter Belegnummer; bei mehreren
Lieferungen gleicher Belegnummer true, sobald eine verändert ist (bool_or).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 21:32:04 +02:00
6378b1101b feat(admin): completed-deliveries um from/to-Range erweitern
/admin/completed-deliveries akzeptiert jetzt zusätzlich zu `day` (Kurzform
from=to) optionale `from`/`to` (DD-MM-YYYY, inklusive, je offen). Damit lassen
sich Halb-Grenzen abfragen: `?from=` bzw. `?to=` liefern je eine Belegnummern-
Menge, deren SQL-AND-Schnitt einen Zeitraum ergibt (Range-Filter über zwei
unabhängige ERPframe-Filter). Response gibt statt `day` nun `from`/`to` zurück.
Mindestens eine Grenze ist Pflicht (kein Full-Table-Dump).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 17:35:11 +02:00
748df577ef feat(admin): GET /admin/completed-deliveries — abgeschlossene Lieferungen eines Tages
Neuer Admin-Endpunkt, der ALLE an einem Tag (Berliner Kalendertag von
completed_at) abgeschlossenen Lieferungen liefert — unabhängig vom
Mail-Versand-Status. Pro Lieferung: Belegnummer und ein Flag
`positions_modified`, das true ist, wenn eine Belegzeile in der Menge
reduziert/entfernt wurde (delivery_items.credited_quantity > 0) oder eine
aktive Geld-Gutschrift vorliegt (jüngstes delivery_credit_audit-Event 'set').

Pflichtparameter `day` (DD-MM-YYYY). Geschützt per admin_api_key.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-09 16:06:01 +02:00
819005eaa5 feat(dev): /dev/reset-delivery — einzelne Lieferung per Belegnummer zurücksetzen
Setzt EINE Lieferung zurück, ohne die Tour zu löschen (Alternative zum
destruktiven /dev/resync): Abschluss (delivery_completions), Scan-/
Gutschrift-Audit + offener Report-Job weg, Positionen zurück (scanned/
credited = 0, Status in_progress), Lieferung wieder active (state_reason/
assigned_car/review_* geleert). Notizen/Dienstleistungen/Anhänge bleiben.

Vervollständigt den bisher nur als Port-Stub vorhandenen
reset_delivery_by_belegnummer (Build war dadurch kaputt) + Use Case +
DEV-Route (nur bei dev.sync_enabled gemountet, unauthentifiziert).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 13:35:45 +02:00
fb5f43ed7a feat(erp): Gutschrift-Rückschreibung per Config togglen
Neues Flag [erp] gutschrift_writeback_enabled (Default true): steuert, ob
die Geld-Gutschrift (GUTSCHRIFT10-Belegzeile) beim Abschluss ins ERP
zurückgeschrieben wird. Gutschriften sind bestandsneutral (keine
Bestandsführung) → unkritisch, daher standardmäßig an, aber abschaltbar.

Die Belegzeilen-Menge wird weiterhin generell NICHT zurückgeschrieben
(Bestands-Inkonsistenz); geänderte Belege gehen in die Vier-Augen-Prüfung.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 10:26:05 +02:00
2d364f3fb7 feat(review): Vier-Augen-Prüfung für geänderte Lieferscheine
Geänderte Lieferscheine (Artikel entfernt/teil-gutgeschrieben ODER
Geld-Gutschrift) sollen von der Fakturierung gegengeprüft werden, statt
die Menge im ERP zu reduzieren (was den Lagerbestand inkonsistent machte,
weil der Raw-Writeback die ERPframe-Engine umgeht).

- ERP-Writeback: kein Setzen der Belegzeilen-Menge mehr → ERP-Beleg bleibt
  im Original (kein Bestandskonflikt). Geld-Gutschrift wird weiter
  geschrieben (unkritisch, nur Vier-Augen). delivered-at/State/Zahlbed
  bleiben.
- Migration 0031: review_resolved_at/by/note auf deliveries. Der Status
  wird ABGELEITET (Abweichung via credited_quantity / aktueller Gutschrift
  vs. resolved_at), daher: Originalzustand wiederhergestellt ⇒ Flag weg;
  nach Bestätigung erneut geändert ⇒ wieder offen.
- ReviewRepository (Port + Pg-Impl) + Use Cases ListPendingReviews/
  ResolveReview.
- Endpoints: GET /admin/reviews (offene Prüfungen inkl. Änderungsdetails
  aus scan_audit/credit_audit) + POST /admin/reviews/{delivery_id}/resolve
  (Admin-Key).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 18:02:27 +02:00
1e6dfb10b0 feat(signature): Dateinamen-Schema delivery_{Belegnummer}_signature_{role}.png
Unterschrifts-Dateien folgen jetzt dem Schema
delivery_{Belegnummer}_signature_{customer|driver}.png
(z. B. delivery_V-30690287_signature_customer.png) statt zuvor
{delivery_id}_{role}.png.

- SignatureStorage::save nimmt Belegnummer statt delivery_id; Adapter
  baut den Dateinamen + saubere Sanitisierung des Belegnummer-Anteils
  (Schutz gegen Pfad-Ausbruch, übliche Werte wie V-30690287 bleiben).
- CompleteDeliveryUseCase löst die Belegnummer vor dem Speichern auf.
- Neue Lookup-Methode DeliveryCompletionRepository::belegnummer_for.
- Totes delete_for_delivery (Reconstruktion via delivery_id, keine
  Aufrufer mehr seit Cron-Cleanup) entfernt.

Abwärtskompatibel: bestehende Signaturen werden über die in
delivery_completions gespeicherte Referenz geladen, alte Dateinamen
bleiben lesbar. Nur neue Abschlüsse verwenden das neue Schema.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 16:56:58 +02:00
47eb8ec57d feat(signature): Signaturen beim Report-Upload behalten + Cron-Cleanup nach Frist
Bisher loeschte die Report-Pipeline die Unterschriften nach erfolgreichem
DOCUframe-Upload. Wir brauchen die Signatur-Dateien aber weiterhin, daher:

- ProcessDeliveryReportUseCase: Signatur-Loeschung (delete_for_delivery) aus dem
  Cleanup entfernt + SignatureStorage-Dependency raus (Report-PDF/Bild-Notiz-
  Cleanup bleibt).
- SignatureStorage: neue Methode delete_older_than(max_age) -> Anzahl; lokaler
  Adapter loescht PNGs aelter als die Frist (per mtime).
- Config [signature]: retention_days (Default 90, 0 = aus) + cleanup_cron
  (Default taeglich 04:00).
- main.rs: Signatur-Cleanup-Scheduler (gated retention_days > 0).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 16:16:12 +02:00
2f4368ec52 feat(import): ERP-Sync in DB dokumentieren + Startup-Catch-up
Jeder ERPframe-Import (Scheduler, Startup, manuell) wird in der neuen Tabelle
erp_sync_runs protokolliert (target_date, trigger, success, Zaehler, Fehler).
Beim Serverstart synct das Backend das Zieldatum (heute + offset, i.d.R. morgen)
nach, falls dafuer noch kein erfolgreicher Lauf dokumentiert ist — deckt
Erststart UND laengere Unterbrechungen ab, bei denen der Cron-Zeitpunkt verpasst
wurde. Gated ueber [import] enabled.

- Migration 0030_erp_sync_runs
- Port SyncRunRepository (+ SyncRunRecord, SyncTrigger)
- Adapter PgSyncRunRepository
- ImportErpToursUseCase dokumentiert jeden Lauf; neues execute_with(date, trigger)
- main.rs: Repo verdrahtet, Scheduler-Trigger, Startup-Catch-up (tokio::spawn)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 16:23:12 +02:00
d30d43df3a Backend als Windows-Dienst registrierbar (SCM, wie Mail-Client)
Das Backend kann jetzt — analog zum Mail-Client — als Windows-Dienst laufen.

- main() refaktoriert: App-Logik in run_app(shutdown, service_mode); eigene
  tokio-Runtime statt #[tokio::main]. Windows startet zuerst den SCM-Dispatcher,
  fällt bei interaktivem Start auf Konsolenmodus zurück (--console erzwingt ihn).
- src/service.rs (windows-only): SCM-Integration via windows-service-Crate,
  Stop/Shutdown-Handler, Running/Stopped-Status. Setzt das Arbeitsverzeichnis
  aufs EXE-Verzeichnis (Dienst startet sonst in System32), damit config.toml/
  data/logs daneben liegen. Fallback-Log bei Boot-Fehler.
- Graceful Shutdown: GSD-Lizenz-Freigabe in den Serve-Wrapper gezogen (greift
  in beiden Modi); Stop-Trigger ist das übergebene shutdown-Future.
- Logging: Konsolenmodus → stderr (wie bisher); Dienst-Modus → rollende
  Tagesdatei (tracing-appender) unter [logging] dir (Default logs/).
- install-service.ps1 / uninstall-service.ps1 (Dienst "Holzleitner Backend").
- README: Windows-Dienst-Abschnitt; .gitignore: /logs + Fatal-Log.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 18:12:12 +02:00
6a9b5872e1 Backend-Arbeitsstand: ERP-Sync, Lieferlebenszyklus, Reports + config.toml
Bringt das Backend vom initialen Skeleton auf den aktuellen Arbeitsstand
(Clean Architecture: domain → application → infrastructure → api).

Wesentliche Bereiche:
- ERP-Anbindung (MSSQL-Pull der Touren, Import-Scheduler, Rückschreiben)
- Lieferlebenszyklus: Scan/Hold/Cancel/Complete, Gutschriften, Notizen,
  Bild-Anhänge, Unterschriften, PDF-Lieferreport → DOCUframe
- Stammdaten: Kunden, Artikel, Lager, Zahlungsarten, Services
- Keycloak-JWT-Gate + Fahrer-Provisionierung via Admin-API
- Admin-API-Key-Gate (X-Admin-Api-Key) für Maschinen-Endpunkte

Jüngste Änderungen dieser Session:
- Belegspezifische Kontaktdaten: alle ERP-Adressen (Beleg-/Liefer-/
  Rechnungsadresse, Ansprechpartner, Kundenstamm) mit Telefon/Mobil/
  E-Mail werden gesynct (Migration 0029, MSSQL-Query, TourDetails)
- Konfiguration von .env (envy/dotenvy) auf config.toml (toml/serde)
  umgestellt; Vorlage config.example.toml, Pfad via HOLZLEITNER_CONFIG

Nicht im Repo (per .gitignore): config.toml (Secrets), data/ (Laufzeit-/
Kundendaten), demo.mp4, .claude/, variocontrol-ai/.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 17:52:58 +02:00
438040acce Initial: Rust-Backend mit Clean Architecture (domain/application/infrastructure/api)
Vier-Crate-Workspace mit:
- Domain: Account, Car, Tour, Delivery, DeliveryItem, DeliveryNote, Customer,
  Article, Warehouse, ScanState, AuditAction — alle mit serde + feature-gated
  utoipa::ToSchema.
- Application: Ports (TourRepository, DeliveryRepository, ScanRepository,
  DeliveryNoteRepository, CarRepository, AuthService) und Use Cases.
- Infrastructure: Postgres-Adapter via sqlx (PgTourRepository etc.) +
  Keycloak-AuthService mit JWKS-Cache + OIDC-Discovery.
- API: Axum 0.8, utoipa-OpenAPI + Swagger-UI, JWT-Bearer-Middleware,
  AuthenticatedUser-Extractor.

Endpoints:
- GET /me/tours/today, /tours/{id}, /accounts/{pn}, /me/cars, /health
- POST /sync/tour, /scans (bulk + idempotent via clientScanId),
  /deliveries/{id}/{hold,resume,cancel,complete,notes}, /me/cars
- PUT /tours/{id}/delivery-order, /deliveries/{id}/assigned-car, /me/cars/{id}
- PATCH /me/cars/{id}

Datenmodell:
- 6 Migrationen (accounts, tours/deliveries/items + Stammdaten,
  scan_audit mit clientScanId-UNIQUE, state_reason refactor,
  delivery_notes, cars + FKs nachziehen).
- Business-stabile Beleg-Keys (belegart_id, belegnummer) für ERP-Sync.
- Append-only scan_audit + embedded scan_state als doppelte Wahrheit.

Dev-Setup:
- docker-compose mit Postgres 17 + Keycloak 26
- Keycloak-Realm 'holzleitner' mit Public-Client (PKCE), Testfahrer
  (PN 1001) + Audience-/Personalnummer-Mapper
2026-05-14 22:28:31 +02:00