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:
149
crates/application/src/dto/delivery_report.rs
Normal file
149
crates/application/src/dto/delivery_report.rs
Normal file
@ -0,0 +1,149 @@
|
||||
//! 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>>,
|
||||
}
|
||||
Reference in New Issue
Block a user