Files
Holzleitner---Backend--aktu…/crates/application/src/dto/delivery_report.rs
Dennis Nemec 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

150 lines
4.5 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! DTO-Aggregat für den PDF-Lieferreport.
//!
//! Bündelt **alle** Informationen zu einer Lieferung inkl. der beiden
//! Audit-Trails (`scan_audit`, `delivery_credit_audit`). Wird vom
//! `DeliveryReportRepository` (DB) befüllt; die Bild-Bytes (Unterschriften,
//! Anhänge) hängt der Use Case nachträglich aus dem lokalen Speicher an,
//! damit der Renderer rein (ohne IO) bleibt.
use chrono::{DateTime, NaiveDate, Utc};
#[derive(Debug, Clone)]
pub struct DeliveryReportData {
pub generated_at: DateTime<Utc>,
// Kopf
pub belegart_id: i64,
/// Belegart-Kurzcode (z. B. „VL5"), falls vom Sync befüllt.
pub belegart_code: Option<String>,
/// Belegart-Klartext (z. B. „Lieferschein EH").
pub belegart_name: Option<String>,
pub belegnummer: String,
pub state: String,
pub tour_date: NaiveDate,
pub driver_personalnummer: i64,
pub driver_name: String,
pub car_plate: Option<String>,
pub payment_method: Option<String>,
// Kunde + Adresse
pub customer_number: i64,
pub customer_name: String,
pub address: String,
pub desired_time: Option<String>,
pub special_agreements: Option<String>,
pub prepaid_amount: f64,
pub current_credit_cents: i64,
pub contacts: Vec<ReportContact>,
pub items: Vec<ReportItem>,
pub services: Vec<ReportService>,
pub notes: Vec<ReportNote>,
pub completion: Option<ReportCompletion>,
pub scan_audit: Vec<ReportScanAudit>,
pub credit_audit: Vec<ReportCreditAudit>,
pub attachments: Vec<ReportAttachment>,
// Bild-Bytes (vom Use Case aus dem lokalen Speicher nachgeladen):
pub customer_signature_png: Option<Vec<u8>>,
pub driver_signature_png: Option<Vec<u8>>,
}
#[derive(Debug, Clone)]
pub struct ReportContact {
pub name: String,
pub detail: Option<String>,
}
#[derive(Debug, Clone)]
pub struct ReportItem {
pub belegzeilen_nr: i32,
pub komponenten_artikel_nr: Option<String>,
pub parent_artikel_nr: Option<String>,
pub article_number: String,
pub name: String,
pub required_quantity: i32,
pub credited_quantity: i32,
pub scanned_quantity: i32,
pub scan_status: String,
pub unit_price: f64,
pub warehouse_code: Option<String>,
pub warehouse_name: Option<String>,
}
impl ReportItem {
pub fn is_component(&self) -> bool {
self.komponenten_artikel_nr.is_some()
}
/// Tatsächlich ausgeliefert = Soll Gutschrift.
pub fn delivered(&self) -> i32 {
(self.required_quantity - self.credited_quantity).max(0)
}
}
#[derive(Debug, Clone)]
pub struct ReportService {
pub name: String,
pub bool_value: Option<bool>,
pub numeric_value: Option<i32>,
}
#[derive(Debug, Clone)]
pub struct ReportNote {
pub created_at: DateTime<Utc>,
pub author_personalnummer: i64,
pub text: Option<String>,
pub image_attachment: Option<String>,
pub is_amount_credit_note: bool,
}
#[derive(Debug, Clone)]
pub struct ReportCompletion {
pub completed_at: DateTime<Utc>,
pub completed_by_personalnummer: i64,
pub receipt_confirmed: bool,
pub notes_acknowledged: bool,
pub customer_signature_path: String,
pub driver_signature_path: String,
/// Fahrer hat das Inkasso (Bar/EC) bestätigt.
pub payment_collected: bool,
/// Snapshot des kassierten offenen Betrags in Cent (None = kein Inkasso).
pub collected_amount_cents: Option<i64>,
}
#[derive(Debug, Clone)]
pub struct ReportScanAudit {
pub server_recorded_at: DateTime<Utc>,
pub client_scanned_at: DateTime<Utc>,
pub action: String,
pub delta: i32,
pub resulting_quantity: i32,
pub resulting_status: String,
pub reason: Option<String>,
pub manual: bool,
pub credit_delta: Option<i32>,
pub actor_personalnummer: i64,
pub belegzeilen_nr: i32,
pub komponenten_artikel_nr: Option<String>,
pub article_name: Option<String>,
}
#[derive(Debug, Clone)]
pub struct ReportCreditAudit {
pub recorded_at: DateTime<Utc>,
pub action: String,
pub amount_cents: i64,
pub reason: Option<String>,
pub author_personalnummer: i64,
}
#[derive(Debug, Clone)]
pub struct ReportAttachment {
pub filename: Option<String>,
/// Speicher-Referenz (lokaler relativer Pfad) — zum Nachladen der Bytes.
pub reference: String,
pub mime_type: String,
pub size_bytes: i64,
pub width: Option<i32>,
pub height: Option<i32>,
pub uploaded_at: DateTime<Utc>,
pub uploaded_by: i64,
/// Vom Use Case aus dem lokalen Speicher nachgeladen (fürs Einbetten).
pub bytes: Option<Vec<u8>>,
}