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>
74 lines
2.6 KiB
Rust
74 lines
2.6 KiB
Rust
use chrono::{DateTime, Utc};
|
|
use serde::{Deserialize, Serialize};
|
|
use uuid::Uuid;
|
|
|
|
use super::delivery::ScanStatus;
|
|
|
|
/// Aktion-Typen im Scan-Audit-Log.
|
|
///
|
|
/// * `Scan` / `Unscan` verändern die `scanned_quantity` (+1 / -1).
|
|
/// * `Hold` / `Unhold` ändern nur den Status, keine Menge.
|
|
/// * `Remove` markiert die Position als entfernt (Status `Removed`,
|
|
/// z. B. weil der Kunde sie nicht annimmt).
|
|
/// * `Unremove` hebt ein `Remove` wieder auf — die Position landet
|
|
/// zurück in `InProgress` (oder `Done`, falls die `scanned_quantity`
|
|
/// schon `required_quantity` erreicht hatte). Der ursprüngliche
|
|
/// `Remove`-Audit-Eintrag bleibt unangetastet; das `Unremove` erzeugt
|
|
/// einen eigenen Audit-Eintrag — die Historie bleibt vollständig.
|
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
|
|
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
|
|
#[serde(rename_all = "snake_case")]
|
|
pub enum AuditAction {
|
|
Scan,
|
|
Unscan,
|
|
Hold,
|
|
Unhold,
|
|
Remove,
|
|
Unremove,
|
|
}
|
|
|
|
/// Append-only Audit-Log-Eintrag: jedes Ereignis am Scan-Zustand einer
|
|
/// Position bekommt eine eigene Zeile. Nie geupdated, nie gelöscht.
|
|
///
|
|
/// Beleg-Bezug wird denormalisiert mitgeführt, damit der Audit-Trail
|
|
/// auch dann auflösbar bleibt, wenn das zugehörige `DeliveryItem`
|
|
/// irgendwann archiviert oder bereinigt wird.
|
|
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
|
|
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
|
|
#[serde(rename_all = "camelCase")]
|
|
pub struct ScanAuditEntry {
|
|
pub id: Uuid,
|
|
/// Vom Client vergebene UUID — Idempotenz-Schlüssel beim Retry.
|
|
pub client_scan_id: Uuid,
|
|
pub delivery_item_id: Uuid,
|
|
|
|
pub action: AuditAction,
|
|
|
|
/// Signed Δ in `scanned_quantity`: +1 bei SCAN, -1 bei UNSCAN, 0 sonst.
|
|
pub delta: i32,
|
|
|
|
/// Snapshot der `scanned_quantity` direkt NACH dieser Aktion.
|
|
/// Vermeidet teure Aggregat-Queries bei Reports.
|
|
pub resulting_quantity: i32,
|
|
|
|
/// Status der Position NACH dieser Aktion.
|
|
pub resulting_status: ScanStatus,
|
|
|
|
/// Grund bei HOLD / REMOVE (jeweils Pflicht).
|
|
pub reason: Option<String>,
|
|
|
|
/// Akteur — Personalnummer aus dem JWT.
|
|
pub actor_personalnummer: i64,
|
|
/// Akteur-Fahrzeug, sofern bekannt (cars werden später verwaltet).
|
|
pub actor_car_id: Option<Uuid>,
|
|
|
|
pub client_scanned_at: DateTime<Utc>,
|
|
pub server_recorded_at: DateTime<Utc>,
|
|
|
|
// ── Denormalisierter ERP-Beleg-Bezug (Archiv-stabil) ───────────────
|
|
pub erp_belegart_id: i64,
|
|
pub erp_belegnummer: String,
|
|
pub erp_belegzeilen_nr: i32,
|
|
pub erp_komponenten_artikel_nr: Option<String>,
|
|
}
|