//! 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, // Kopf pub belegart_id: i64, /// Belegart-Kurzcode (z. B. „VL5"), falls vom Sync befüllt. pub belegart_code: Option, /// Belegart-Klartext (z. B. „Lieferschein EH"). pub belegart_name: Option, pub belegnummer: String, pub state: String, pub tour_date: NaiveDate, pub driver_personalnummer: i64, pub driver_name: String, pub car_plate: Option, pub payment_method: Option, // Kunde + Adresse pub customer_number: i64, pub customer_name: String, pub address: String, pub desired_time: Option, pub special_agreements: Option, pub prepaid_amount: f64, pub current_credit_cents: i64, pub contacts: Vec, pub items: Vec, pub services: Vec, pub notes: Vec, pub completion: Option, pub scan_audit: Vec, pub credit_audit: Vec, pub attachments: Vec, // Bild-Bytes (vom Use Case aus dem lokalen Speicher nachgeladen): pub customer_signature_png: Option>, pub driver_signature_png: Option>, } #[derive(Debug, Clone)] pub struct ReportContact { pub name: String, pub detail: Option, } #[derive(Debug, Clone)] pub struct ReportItem { pub belegzeilen_nr: i32, pub komponenten_artikel_nr: Option, pub parent_artikel_nr: Option, 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, pub warehouse_name: Option, } 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, pub numeric_value: Option, } #[derive(Debug, Clone)] pub struct ReportNote { pub created_at: DateTime, pub author_personalnummer: i64, pub text: Option, pub image_attachment: Option, pub is_amount_credit_note: bool, } #[derive(Debug, Clone)] pub struct ReportCompletion { pub completed_at: DateTime, 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, } #[derive(Debug, Clone)] pub struct ReportScanAudit { pub server_recorded_at: DateTime, pub client_scanned_at: DateTime, pub action: String, pub delta: i32, pub resulting_quantity: i32, pub resulting_status: String, pub reason: Option, pub manual: bool, pub credit_delta: Option, pub actor_personalnummer: i64, pub belegzeilen_nr: i32, pub komponenten_artikel_nr: Option, pub article_name: Option, } #[derive(Debug, Clone)] pub struct ReportCreditAudit { pub recorded_at: DateTime, pub action: String, pub amount_cents: i64, pub reason: Option, pub author_personalnummer: i64, } #[derive(Debug, Clone)] pub struct ReportAttachment { pub filename: Option, /// Speicher-Referenz (lokaler relativer Pfad) — zum Nachladen der Bytes. pub reference: String, pub mime_type: String, pub size_bytes: i64, pub width: Option, pub height: Option, pub uploaded_at: DateTime, pub uploaded_by: i64, /// Vom Use Case aus dem lokalen Speicher nachgeladen (fürs Einbetten). pub bytes: Option>, }