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>
This commit is contained in:
54
crates/application/src/ports/signature_storage.rs
Normal file
54
crates/application/src/ports/signature_storage.rs
Normal file
@ -0,0 +1,54 @@
|
||||
//! Port für den Unterschriften-Speicher.
|
||||
//!
|
||||
//! Im Gegensatz zu Notiz-Bildern (die nach DOCUframe gehen) liegen
|
||||
//! Unterschriften bewusst **lokal im Backend-Server** — ein einfacher
|
||||
//! Datei-Speicher reicht, und die Daten verlassen die Maschine nicht.
|
||||
//!
|
||||
//! Die konkrete Impl (lokales Dateisystem) lebt in
|
||||
//! `holzleitner-infrastructure`. Der Use Case erhält eine relative
|
||||
//! Referenz zurück, die in `delivery_completions` persistiert wird.
|
||||
|
||||
use async_trait::async_trait;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::error::ApplicationError;
|
||||
|
||||
/// Wer hat unterschrieben — bestimmt den Dateinamen.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum SignatureRole {
|
||||
Customer,
|
||||
Driver,
|
||||
}
|
||||
|
||||
impl SignatureRole {
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
SignatureRole::Customer => "customer",
|
||||
SignatureRole::Driver => "driver",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait SignatureStorage: Send + Sync {
|
||||
/// Speichert eine Unterschrift (PNG-Bytes) für eine Lieferung+Rolle und
|
||||
/// liefert die persistente, relative Referenz (Dateiname) zurück.
|
||||
/// Deterministisch über `delivery_id`+`role` — ein Retry überschreibt
|
||||
/// dieselbe Datei statt Müll anzuhäufen.
|
||||
async fn save(
|
||||
&self,
|
||||
delivery_id: Uuid,
|
||||
role: SignatureRole,
|
||||
bytes: Vec<u8>,
|
||||
) -> Result<String, ApplicationError>;
|
||||
|
||||
/// Lädt die gespeicherten PNG-Bytes einer Unterschrift über ihre relative
|
||||
/// Referenz (Dateiname, wie von [`save`](Self::save) geliefert) — fürs
|
||||
/// Einbetten in den PDF-Report. `None`, wenn die Datei fehlt.
|
||||
async fn load(&self, reference: &str) -> Result<Option<Vec<u8>>, ApplicationError>;
|
||||
|
||||
/// Löscht beide Unterschriften (Kunde + Fahrer) einer Lieferung — Aufräumen
|
||||
/// nach erfolgreichem Report-Upload (die Unterschriften stecken dann
|
||||
/// eingebettet im PDF in DOCUframe). Idempotent (fehlende Datei = ok).
|
||||
async fn delete_for_delivery(&self, delivery_id: Uuid) -> Result<(), ApplicationError>;
|
||||
}
|
||||
Reference in New Issue
Block a user