//! Request-Body für `POST /sync/tour`. //! //! Diese Struktur ist die Kontaktfläche zum ERP. Sie beschreibt eine //! Tagestour eines Fahrers inklusive aller Lieferungen und Positionen. //! Identität: //! * Tour: `(driver_personalnummer, tour_date)` — Upsert. //! * Delivery: `(belegart_id, belegnummer)` — Upsert. //! * DeliveryItem: `(delivery, belegzeilen_nr, komponenten_artikel_nr)`. //! //! Bewusst keine UUIDs vom Sender erwartet — die ERP-Welt arbeitet mit //! ihren eigenen business-stabilen Keys, wir generieren UUIDs intern. use chrono::NaiveDate; use serde::{Deserialize, Serialize}; use holzleitner_domain::{Address, ContactKind, ContactRole}; #[derive(Debug, Clone, Deserialize, Serialize)] #[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub struct SyncTourRequest { pub driver_personalnummer: i64, pub tour_date: NaiveDate, pub deliveries: Vec, } #[derive(Debug, Clone, Deserialize, Serialize)] #[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub struct SyncDelivery { pub belegart_id: i64, /// Belegart-Kurzcode (z. B. „VL5"), aus `Belegarten.Belegart` (getrimmt). #[serde(default)] pub belegart_code: Option, /// Belegart-Klartext (z. B. „Lieferschein EH"), aus `Belegarten.Bezeichnung`. #[serde(default)] pub belegart_name: Option, pub belegnummer: String, pub erp_customer_id: i64, pub customer_name: String, pub customer_address: Address, /// Snapshot der Lieferadresse (kann von der Stammadresse abweichen). pub delivery_address: Address, /// 1-basiert, definiert die initiale Reihenfolge in der App. pub sort_order: i32, pub desired_time: Option, pub special_agreements: Option, /// Bei Bestellung schon bezahlter Betrag in EUR. Default `0.0`, /// wenn der Kunde alles bei Lieferung zahlt. Der ERP-Sync liefert /// den Wert mit. #[serde(default)] pub prepaid_amount: f64, /// Für den Restbetrag gewählte Zahlungsart — Referenz per /// `code` (z. B. `"cash"`, `"invoice"`). Das ERP kennt seine /// Standard-Codes, der Sync-Code resolvet sie zur UUID. Wenn /// `None`, fällt der Backend-Code auf `"cash"` zurück. #[serde(default)] pub payment_method_code: Option, pub items: Vec, /// Alle vom ERP an diesem Beleg hängenden Kontakt-Adressen (Beleg-/ /// Liefer-/Rechnungsadresse, Ansprechpartner, Kundenstamm). Leere /// Quellen (kein einziger ausgefüllter Kanal *und* kein Name) lässt /// der Sync weg. #[serde(default)] pub contact_sources: Vec, } /// Eine Adress-Rolle eines Belegs mit Namensblock und allen ausgefüllten /// Telefon-/Mobil-/E-Mail-/Web-Einträgen. #[derive(Debug, Clone, Deserialize, Serialize)] #[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub struct SyncContactSource { pub role: ContactRole, #[serde(default)] pub anrede: Option, #[serde(default)] pub titel: Option, #[serde(default)] pub name1: Option, #[serde(default)] pub name2: Option, #[serde(default)] pub name3: Option, #[serde(default)] pub abteilung: Option, #[serde(default)] pub funktion: Option, #[serde(default)] pub channels: Vec, } #[derive(Debug, Clone, Deserialize, Serialize)] #[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub struct SyncContactChannel { pub kind: ContactKind, /// 1-basiert: spiegelt ERP-Spaltenposition (Telefon → 1, Telefon2 → 2, …). pub position: i16, pub value: String, } #[derive(Debug, Clone, Deserialize, Serialize)] #[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))] #[serde(rename_all = "camelCase")] pub struct SyncDeliveryItem { pub belegzeilen_nr: i32, /// Komponenten-Artikelnummer bei aufgelösten Stücklisten, sonst leer. /// Trägt die **eigene** Nummer der Komponente (eindeutig je Belegzeile). pub komponenten_artikel_nr: Option, /// Artikelnummer des **Oberartikels**, zu dem diese Komponente gehört. /// `None` bei Oberartikeln/regulären Zeilen. Erlaubt der App, Komponenten /// unter ihrem Oberartikel einzurücken. #[serde(default)] pub parent_artikel_nr: Option, pub article_number: String, pub article_name: String, /// Default-Lager-Code für den Artikel (Anlage neuer Artikel). pub article_default_warehouse_code: Option, pub article_scannable: bool, pub warehouse_code: String, pub warehouse_name: String, pub required_quantity: i32, /// Stückpreis (brutto, EUR). Default `0.0`. Liefert der ERP-Sync mit; /// die App rechnet daraus den Warenwert. #[serde(default)] pub unit_price: f64, }