{
    "openapi": "3.1.0",
    "info": {
        "title": "Holzleitner Backend API",
        "description": "Backend f\u00fcr die Holzleitner-Lieferservice-App \u2014 Tour, Beladung, Ausf\u00fchrung.",
        "contact": {
            "name": "Holzleitner GmbH"
        },
        "license": {
            "name": "Proprietary",
            "identifier": "Proprietary"
        },
        "version": "0.1.0"
    },
    "paths": {
        "/accounts/{personalnummer}": {
            "get": {
                "tags": [
                    "accounts"
                ],
                "summary": "Liest den Account zu einer Personalnummer.",
                "operationId": "get_account",
                "parameters": [
                    {
                        "name": "personalnummer",
                        "in": "path",
                        "description": "Personalnummer des Accounts",
                        "required": true,
                        "schema": {
                            "type": "integer",
                            "format": "int64"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Account gefunden",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/Account"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Authentifizierung fehlgeschlagen"
                    },
                    "404": {
                        "description": "Kein Account zu dieser Personalnummer"
                    }
                },
                "security": [
                    {
                        "bearer_auth": []
                    }
                ]
            }
        },
        "/deliveries/{delivery_id}/assigned-car": {
            "put": {
                "tags": [
                    "deliveries"
                ],
                "summary": "Setzt das `assigned_car_id` einer Lieferung. `carId: null` l\u00f6st\ndie Zuordnung wieder. Der Use Case stellt sicher, dass das Fahrzeug\nzum angemeldeten Account geh\u00f6rt.",
                "operationId": "assign_car",
                "parameters": [
                    {
                        "name": "delivery_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "format": "uuid"
                        }
                    }
                ],
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/AssignCarRequest"
                            }
                        }
                    },
                    "required": true
                },
                "responses": {
                    "200": {
                        "description": "Fahrzeug zugewiesen / entfernt",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/DeliveryResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Fahrzeug geh\u00f6rt nicht zum Account"
                    },
                    "401": {
                        "description": "Authentifizierung fehlgeschlagen"
                    },
                    "404": {
                        "description": "Lieferung nicht gefunden"
                    }
                },
                "security": [
                    {
                        "bearer_auth": []
                    }
                ]
            }
        },
        "/deliveries/{delivery_id}/cancel": {
            "post": {
                "tags": [
                    "deliveries"
                ],
                "summary": "Setzt die Lieferung auf `canceled` \u2014 endg\u00fcltig. Erlaubt aus\n`active` und `held`.",
                "operationId": "cancel",
                "parameters": [
                    {
                        "name": "delivery_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "format": "uuid"
                        }
                    }
                ],
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/CancelDeliveryRequest"
                            }
                        }
                    },
                    "required": true
                },
                "responses": {
                    "200": {
                        "description": "Lieferung storniert",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/DeliveryResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Invalider Status\u00fcbergang oder leerer Reason"
                    },
                    "401": {
                        "description": "Authentifizierung fehlgeschlagen"
                    },
                    "404": {
                        "description": "Lieferung nicht gefunden"
                    }
                },
                "security": [
                    {
                        "bearer_auth": []
                    }
                ]
            }
        },
        "/deliveries/{delivery_id}/complete": {
            "post": {
                "tags": [
                    "deliveries"
                ],
                "summary": "Schlie\u00dft die Lieferung ab \u2014 `state = completed`. Nur aus `active`.",
                "operationId": "complete",
                "parameters": [
                    {
                        "name": "delivery_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "format": "uuid"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Lieferung abgeschlossen",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/DeliveryResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Invalider Status\u00fcbergang"
                    },
                    "401": {
                        "description": "Authentifizierung fehlgeschlagen"
                    },
                    "404": {
                        "description": "Lieferung nicht gefunden"
                    }
                },
                "security": [
                    {
                        "bearer_auth": []
                    }
                ]
            }
        },
        "/deliveries/{delivery_id}/hold": {
            "post": {
                "tags": [
                    "deliveries"
                ],
                "summary": "Setzt die Lieferung auf `held`. Nur aus `active` zul\u00e4ssig.",
                "operationId": "hold",
                "parameters": [
                    {
                        "name": "delivery_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "format": "uuid"
                        }
                    }
                ],
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/HoldDeliveryRequest"
                            }
                        }
                    },
                    "required": true
                },
                "responses": {
                    "200": {
                        "description": "Lieferung geholdet",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/DeliveryResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Invalider Status\u00fcbergang oder leerer Reason"
                    },
                    "401": {
                        "description": "Authentifizierung fehlgeschlagen"
                    },
                    "404": {
                        "description": "Lieferung nicht gefunden"
                    }
                },
                "security": [
                    {
                        "bearer_auth": []
                    }
                ]
            }
        },
        "/deliveries/{delivery_id}/notes": {
            "post": {
                "tags": [
                    "deliveries"
                ],
                "summary": "Legt eine neue Notiz an einer Lieferung an. Mindestens eines von\n`text` und `imageAttachment` muss inhaltlich gef\u00fcllt sein\n(Leerstrings werden serverseitig getrimmt und als leer behandelt).",
                "operationId": "create_note",
                "parameters": [
                    {
                        "name": "delivery_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "format": "uuid"
                        }
                    }
                ],
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/CreateDeliveryNoteRequest"
                            }
                        }
                    },
                    "required": true
                },
                "responses": {
                    "200": {
                        "description": "Notiz angelegt",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/DeliveryNoteResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Notiz ohne Inhalt"
                    },
                    "401": {
                        "description": "Authentifizierung fehlgeschlagen"
                    },
                    "404": {
                        "description": "Lieferung nicht gefunden"
                    }
                },
                "security": [
                    {
                        "bearer_auth": []
                    }
                ]
            }
        },
        "/deliveries/{delivery_id}/resume": {
            "post": {
                "tags": [
                    "deliveries"
                ],
                "summary": "Setzt die Lieferung zur\u00fcck auf `active`. Nur aus `held` zul\u00e4ssig.",
                "operationId": "resume",
                "parameters": [
                    {
                        "name": "delivery_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "format": "uuid"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Lieferung wieder aktiv",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/DeliveryResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Invalider Status\u00fcbergang"
                    },
                    "401": {
                        "description": "Authentifizierung fehlgeschlagen"
                    },
                    "404": {
                        "description": "Lieferung nicht gefunden"
                    }
                },
                "security": [
                    {
                        "bearer_auth": []
                    }
                ]
            }
        },
        "/health": {
            "get": {
                "tags": [
                    "health"
                ],
                "summary": "Health-Endpoint f\u00fcr Load-Balancer und Container-Probes. Bewusst\nkein Auth \u2014 eine `200 ok`-Antwort darf nicht von der Auth abh\u00e4ngen.",
                "operationId": "health",
                "responses": {
                    "200": {
                        "description": "Service ist erreichbar",
                        "content": {
                            "text/plain": {
                                "schema": {
                                    "type": "string"
                                }
                            }
                        }
                    }
                },
                "security": []
            }
        },
        "/me/cars": {
            "get": {
                "tags": [
                    "cars"
                ],
                "summary": "Listet die Fahrzeuge des angemeldeten Fahrers.",
                "operationId": "list_my_cars",
                "parameters": [
                    {
                        "name": "includeInactive",
                        "in": "query",
                        "description": "Wenn true, werden inaktive Fahrzeuge mitgeliefert (default: false)",
                        "required": false,
                        "schema": {
                            "type": "boolean"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Fahrzeuge des Fahrers",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/CarsList"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Authentifizierung fehlgeschlagen"
                    }
                },
                "security": [
                    {
                        "bearer_auth": []
                    }
                ]
            },
            "post": {
                "tags": [
                    "cars"
                ],
                "summary": "Legt ein neues Fahrzeug f\u00fcr den angemeldeten Fahrer an.",
                "operationId": "create_my_car",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/CreateCarRequest"
                            }
                        }
                    },
                    "required": true
                },
                "responses": {
                    "200": {
                        "description": "Fahrzeug angelegt",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/CarResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Validierungsfehler (z. B. doppeltes Kennzeichen)"
                    },
                    "401": {
                        "description": "Authentifizierung fehlgeschlagen"
                    }
                },
                "security": [
                    {
                        "bearer_auth": []
                    }
                ]
            }
        },
        "/me/cars/{car_id}": {
            "patch": {
                "tags": [
                    "cars"
                ],
                "summary": "Aktualisiert ein Fahrzeug (Kennzeichen \u00e4ndern / deaktivieren).",
                "operationId": "update_my_car",
                "parameters": [
                    {
                        "name": "car_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "format": "uuid"
                        }
                    }
                ],
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/UpdateCarRequest"
                            }
                        }
                    },
                    "required": true
                },
                "responses": {
                    "200": {
                        "description": "Fahrzeug aktualisiert",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/CarResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Validierungsfehler"
                    },
                    "401": {
                        "description": "Authentifizierung fehlgeschlagen"
                    },
                    "404": {
                        "description": "Fahrzeug nicht gefunden oder geh\u00f6rt nicht zu diesem Account"
                    }
                },
                "security": [
                    {
                        "bearer_auth": []
                    }
                ]
            }
        },
        "/me/tours/today": {
            "get": {
                "tags": [
                    "tours"
                ],
                "summary": "Listet heutige Touren des angemeldeten Fahrers (Filter aus dem JWT).",
                "operationId": "list_my_tours_today",
                "responses": {
                    "200": {
                        "description": "Liste der heutigen Touren",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/TourSummaryList"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Authentifizierung fehlgeschlagen"
                    }
                },
                "security": [
                    {
                        "bearer_auth": []
                    }
                ]
            }
        },
        "/scans": {
            "post": {
                "tags": [
                    "scans"
                ],
                "summary": "Wendet eine Liste von Scan-Events idempotent an.",
                "description": "Pro Event ein eigenes Resultat. Status `applied` schreibt einen\nfrischen Audit-Eintrag, `duplicate` liefert den aktuellen Stand am\nServer, `rejected` enth\u00e4lt die Begr\u00fcndung. Reihenfolge der `results`\nentspricht der Reihenfolge der `scans` im Request.",
                "operationId": "apply_scans",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/ApplyScansRequest"
                            }
                        }
                    },
                    "required": true
                },
                "responses": {
                    "200": {
                        "description": "Bulk-Ergebnis pro Event",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/ApplyScansResponse"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Authentifizierung fehlgeschlagen"
                    }
                },
                "security": [
                    {
                        "bearer_auth": []
                    }
                ]
            }
        },
        "/sync/tour": {
            "post": {
                "tags": [
                    "sync"
                ],
                "summary": "Sync-Endpoint f\u00fcr das ERP: legt eine Tagestour samt Lieferungen und\nPositionen idempotent an. Identit\u00e4t pro Tour\n`(driver_personalnummer, tour_date)`, pro Lieferung\n`(belegart_id, belegnummer)`.",
                "operationId": "sync_tour",
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/SyncTourRequest"
                            }
                        }
                    },
                    "required": true
                },
                "responses": {
                    "200": {
                        "description": "Tour gespeichert",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/SyncTourResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Validierungsfehler im Sync-Payload"
                    },
                    "401": {
                        "description": "Authentifizierung fehlgeschlagen"
                    }
                },
                "security": [
                    {
                        "bearer_auth": []
                    }
                ]
            }
        },
        "/tours/{tour_id}": {
            "get": {
                "tags": [
                    "tours"
                ],
                "summary": "L\u00e4dt eine Tour mit allen Lieferungen, Positionen und referenzierten\nStammdaten \u2014 die App nutzt das als einzigen gro\u00dfen Read.",
                "operationId": "get_tour",
                "parameters": [
                    {
                        "name": "tour_id",
                        "in": "path",
                        "description": "Eindeutige Tour-Id (UUID)",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "format": "uuid"
                        }
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Tour-Aggregat gefunden",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/TourDetails"
                                }
                            }
                        }
                    },
                    "401": {
                        "description": "Authentifizierung fehlgeschlagen"
                    },
                    "404": {
                        "description": "Keine Tour mit dieser Id"
                    }
                },
                "security": [
                    {
                        "bearer_auth": []
                    }
                ]
            }
        },
        "/tours/{tour_id}/delivery-order": {
            "put": {
                "tags": [
                    "tours"
                ],
                "summary": "Schreibt die Sortier-Reihenfolge aller Lieferungen einer Tour neu.\nDer Client schickt die **vollst\u00e4ndige** neue Reihenfolge; fehlende\noder fremde Lieferungs-Ids werden mit `400 validation` abgelehnt.",
                "operationId": "set_delivery_order",
                "parameters": [
                    {
                        "name": "tour_id",
                        "in": "path",
                        "required": true,
                        "schema": {
                            "type": "string",
                            "format": "uuid"
                        }
                    }
                ],
                "requestBody": {
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/SetDeliveryOrderRequest"
                            }
                        }
                    },
                    "required": true
                },
                "responses": {
                    "200": {
                        "description": "Neue Reihenfolge gespeichert",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/SetDeliveryOrderResponse"
                                }
                            }
                        }
                    },
                    "400": {
                        "description": "Mengen-Mismatch oder Duplikate"
                    },
                    "401": {
                        "description": "Authentifizierung fehlgeschlagen"
                    },
                    "404": {
                        "description": "Tour nicht gefunden"
                    }
                },
                "security": [
                    {
                        "bearer_auth": []
                    }
                ]
            }
        }
    },
    "components": {
        "schemas": {
            "Account": {
                "type": "object",
                "description": "Account eines Liefer-Unternehmens oder Einzel-Lieferfahrers.\n\nDie Personalnummer ist sowohl Prim\u00e4rschl\u00fcssel als auch Login-ID. Sie\nstammt aus dem ERP-Stamm \u2014 entweder ein Unternehmen (juristische\nPerson, eigener Personalnummern-Kreis) oder eine nat\u00fcrliche Person.\n\nMehrere physische Fahrer k\u00f6nnen denselben Account benutzen; das Modell\nunterscheidet sie nicht, sondern loggt die Aktivit\u00e4t auf [`crate::domain::Car`]-\nEbene (siehe Audit-Log).",
                "required": [
                    "personalnummer",
                    "name",
                    "active"
                ],
                "properties": {
                    "active": {
                        "type": "boolean"
                    },
                    "name": {
                        "type": "string"
                    },
                    "personalnummer": {
                        "type": "integer",
                        "format": "int64"
                    }
                }
            },
            "Address": {
                "type": "object",
                "description": "Postanschrift \u2014 wird sowohl als aktuelle Kundenanschrift in [`Customer`]\nals auch als unver\u00e4nderlicher Snapshot in [`crate::domain::Delivery`]\nverwendet (`delivery_address_snapshot`).\n\nBewusst als Value Object modelliert: gleiche Adresse = gleicher Wert.\nStrikte Equality erleichtert Sync-Diffs zwischen ERP und Backend.\n\n[`Customer`]: crate::domain::Customer",
                "required": [
                    "street",
                    "houseNumber",
                    "postalCode",
                    "city",
                    "country"
                ],
                "properties": {
                    "city": {
                        "type": "string"
                    },
                    "country": {
                        "type": "string"
                    },
                    "houseNumber": {
                        "type": "string"
                    },
                    "postalCode": {
                        "type": "string"
                    },
                    "street": {
                        "type": "string"
                    }
                }
            },
            "ApplyScansRequest": {
                "type": "object",
                "required": [
                    "scans"
                ],
                "properties": {
                    "scans": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/ScanEvent"
                        }
                    }
                }
            },
            "ApplyScansResponse": {
                "type": "object",
                "required": [
                    "results"
                ],
                "properties": {
                    "results": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/ScanResult"
                        }
                    }
                }
            },
            "Article": {
                "type": "object",
                "description": "Artikel. ERP-Mirror; die `article_number` ist die business-stabile\nArtikelnummer aus dem ERP-Stamm und dient gleichzeitig als Br\u00fccke.\n\n`scannable = false` markiert nicht-physische Positionen wie\nDienstleistungen, Versandpauschalen o.\u00e4. \u2014 sie tauchen zwar als\n`DeliveryItem` auf, blockieren aber den Beladen-Fortschritt nicht.",
                "required": [
                    "id",
                    "articleNumber",
                    "name",
                    "scannable"
                ],
                "properties": {
                    "articleNumber": {
                        "type": "string"
                    },
                    "defaultWarehouseId": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "uuid"
                    },
                    "id": {
                        "type": "string",
                        "format": "uuid"
                    },
                    "name": {
                        "type": "string"
                    },
                    "scannable": {
                        "type": "boolean"
                    }
                }
            },
            "AssignCarRequest": {
                "type": "object",
                "description": "Setzt das `assigned_car_id` einer Lieferung. `None` (`carId: null`)\nentfernt die Zuordnung.",
                "properties": {
                    "carId": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "uuid"
                    }
                }
            },
            "AuditAction": {
                "type": "string",
                "description": "Aktion-Typen im Scan-Audit-Log.\n\n* `Scan` / `Unscan` ver\u00e4ndern die `scanned_quantity` (+1 / -1).\n* `Hold` / `Unhold` \u00e4ndern nur den Status, keine Menge.\n* `Remove` markiert die Position als entfernt (Status `Removed`,\n  z. B. weil der Kunde sie nicht annimmt).",
                "enum": [
                    "scan",
                    "unscan",
                    "hold",
                    "unhold",
                    "remove"
                ]
            },
            "CancelDeliveryRequest": {
                "type": "object",
                "required": [
                    "reason"
                ],
                "properties": {
                    "reason": {
                        "type": "string"
                    }
                }
            },
            "Car": {
                "type": "object",
                "description": "Fahrzeug eines [`crate::domain::Account`]. Wird in der App selbst\ngepflegt \u2014 kein ERP-Spiegel. Eindeutig per UUID.\n\nIm Audit-Log ist der `Car` der \u201eAkteur\": die Personalnummer-Ebene\n(Account) ist gr\u00f6ber und unterscheidet nicht zwischen mehreren\ngleichzeitig aktiven Fahrern desselben Subunternehmens.",
                "required": [
                    "id",
                    "accountId",
                    "plate",
                    "active"
                ],
                "properties": {
                    "accountId": {
                        "type": "integer",
                        "format": "int64",
                        "description": "Verweis auf [`crate::domain::Account::personalnummer`]."
                    },
                    "active": {
                        "type": "boolean"
                    },
                    "id": {
                        "type": "string",
                        "format": "uuid"
                    },
                    "plate": {
                        "type": "string"
                    }
                }
            },
            "CarResponse": {
                "type": "object",
                "required": [
                    "car"
                ],
                "properties": {
                    "car": {
                        "$ref": "#/components/schemas/Car"
                    }
                }
            },
            "CarsList": {
                "type": "object",
                "required": [
                    "cars"
                ],
                "properties": {
                    "cars": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/Car"
                        }
                    }
                }
            },
            "CreateCarRequest": {
                "type": "object",
                "required": [
                    "plate"
                ],
                "properties": {
                    "plate": {
                        "type": "string"
                    }
                }
            },
            "CreateDeliveryNoteRequest": {
                "type": "object",
                "properties": {
                    "authorCarId": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "uuid",
                        "description": "Fahrzeug, das die Notiz erzeugt hat. Muss zum angemeldeten\nAccount geh\u00f6ren. `None` ist erlaubt."
                    },
                    "imageAttachment": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Object-Storage-Key oder URL eines vorab hochgeladenen Bildes."
                    },
                    "text": {
                        "type": [
                            "string",
                            "null"
                        ]
                    }
                }
            },
            "Customer": {
                "type": "object",
                "description": "Kunde. ERP-Mirror: die Stammdaten geh\u00f6ren dem ERP, wir spiegeln sie\nf\u00fcr die App. Die `erp_customer_id` ist die Br\u00fccke zur\u00fcck (in der\nRegel die `Kunde.row_id` aus ERPframe).\n\nDie `Customer.address` ist die *aktuelle* Anschrift. F\u00fcr historische\nStabilit\u00e4t f\u00fchrt [`crate::domain::Delivery`] zus\u00e4tzlich einen\n`delivery_address_snapshot` \u2014 Adress-\u00c4nderungen wirken nicht\nr\u00fcckwirkend auf bereits zugestellte oder geplante Lieferungen.",
                "required": [
                    "id",
                    "erpCustomerId",
                    "name",
                    "address"
                ],
                "properties": {
                    "address": {
                        "$ref": "#/components/schemas/Address"
                    },
                    "erpCustomerId": {
                        "type": "integer",
                        "format": "int64"
                    },
                    "id": {
                        "type": "string",
                        "format": "uuid"
                    },
                    "name": {
                        "type": "string"
                    }
                }
            },
            "CustomerContact": {
                "type": "object",
                "description": "Ansprechpartner eines Kunden. Ein Kunde kann mehrere Kontaktpersonen\nhaben (z. B. Empfang vor Ort + Gesch\u00e4ftsf\u00fchrung). Eine Lieferung w\u00e4hlt\n0..N davon als aktive Kontakte aus (siehe\n`Delivery::contact_person_ids`).",
                "required": [
                    "id",
                    "customerId",
                    "name"
                ],
                "properties": {
                    "customerId": {
                        "type": "string",
                        "format": "uuid"
                    },
                    "email": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "id": {
                        "type": "string",
                        "format": "uuid"
                    },
                    "name": {
                        "type": "string"
                    },
                    "phone": {
                        "type": [
                            "string",
                            "null"
                        ]
                    }
                }
            },
            "Delivery": {
                "type": "object",
                "description": "Eine einzelne Lieferung an einen Kunden. Aggregat-Wurzel f\u00fcr die\nLiefer-Items, Notizen und das ggf. zugeordnete Fahrzeug.",
                "required": [
                    "id",
                    "tourId",
                    "erpBelegartId",
                    "erpBelegnummer",
                    "customerId",
                    "deliveryAddressSnapshot",
                    "contactPersonIds",
                    "state"
                ],
                "properties": {
                    "assignedCarId": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "uuid",
                        "description": "Fahrzeug-Zuordnung, gesetzt in der Ausw\u00e4hlen-Phase.\nBei Ein-Auto-Teams beim Sync automatisch gef\u00fcllt."
                    },
                    "contactPersonIds": {
                        "type": "array",
                        "items": {
                            "type": "string",
                            "format": "uuid"
                        },
                        "description": "Ausgew\u00e4hlte Ansprechpartner f\u00fcr genau diese Lieferung (Auswahl\naus `Customer.contacts`). Kann leer sein."
                    },
                    "customerId": {
                        "type": "string",
                        "format": "uuid"
                    },
                    "deliveryAddressSnapshot": {
                        "$ref": "#/components/schemas/Address",
                        "description": "Eingefrorene Liefer-Adresse zum Zeitpunkt des Tour-Syncs.\nSch\u00fctzt vor r\u00fcckwirkenden Kunden-Adress\u00e4nderungen."
                    },
                    "desiredTime": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Wunsch-Lieferzeit als Freitext (z. B. \"vormittags\", \"ab 14:00\")."
                    },
                    "erpBelegartId": {
                        "type": "integer",
                        "format": "int64",
                        "description": "ERP-Beleg-Bezug: business-stabiles Paar `(Belegart, Belegnummer)`.\n\u00dcberlebt den Belegkopf-Archiv\u00fcbergang."
                    },
                    "erpBelegnummer": {
                        "type": "string"
                    },
                    "id": {
                        "type": "string",
                        "format": "uuid"
                    },
                    "specialAgreements": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Sondervereinbarungen (z. B. \u201eT\u00fcrklingel defekt, hintenrum klopfen\")."
                    },
                    "state": {
                        "$ref": "#/components/schemas/DeliveryState"
                    },
                    "stateReason": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Begr\u00fcndung bei `state == Held` oder `state == Canceled`. Beim\nResume / Complete wieder `None`."
                    },
                    "tourId": {
                        "type": "string",
                        "format": "uuid"
                    }
                }
            },
            "DeliveryItem": {
                "type": "object",
                "description": "Einzelposition einer Lieferung. Vereint regul\u00e4re Belegzeilen und\nSt\u00fccklisten-Komponenten zu einer flachen Liste \u2014 die St\u00fccklisten-\nHierarchie ist ein ERP-Konstrukt und wird beim Sync aufgel\u00f6st.\n\n\u00dcber die Felder `belegzeilen_nr` und `komponenten_artikel_nr` bleibt\ndie ERP-Herkunft aufl\u00f6sbar.",
                "required": [
                    "id",
                    "deliveryId",
                    "articleId",
                    "requiredQuantity",
                    "warehouseId",
                    "belegzeilenNr",
                    "scanState"
                ],
                "properties": {
                    "articleId": {
                        "type": "string",
                        "format": "uuid"
                    },
                    "belegzeilenNr": {
                        "type": "integer",
                        "format": "int32",
                        "description": "ERP-Belegzeilen-Nr (Position innerhalb des Belegs)."
                    },
                    "deliveryId": {
                        "type": "string",
                        "format": "uuid"
                    },
                    "id": {
                        "type": "string",
                        "format": "uuid"
                    },
                    "komponentenArtikelNr": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Bei Items aus einer St\u00fcckliste: Artikelnummer der Komponente.\nBei regul\u00e4ren Belegzeilen: `None`."
                    },
                    "requiredQuantity": {
                        "type": "integer",
                        "format": "int32"
                    },
                    "scanState": {
                        "$ref": "#/components/schemas/ScanState"
                    },
                    "warehouseId": {
                        "type": "string",
                        "format": "uuid"
                    }
                }
            },
            "DeliveryNote": {
                "type": "object",
                "description": "Notiz an einer Lieferung \u2014 frei eingegeben durch den Fahrer.\n\nMindestens eines von `text` oder `image_attachment` muss gesetzt\nsein. Die Constraint sitzt sowohl im DB-Schema (CHECK) als auch\nin der Application-Schicht.",
                "required": [
                    "id",
                    "deliveryId",
                    "authorPersonalnummer",
                    "createdAt"
                ],
                "properties": {
                    "authorCarId": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "uuid",
                        "description": "Fahrzeug, falls bekannt \u2014 nullable bis das Backend Cars verwaltet."
                    },
                    "authorPersonalnummer": {
                        "type": "integer",
                        "format": "int64",
                        "description": "Personalnummer des Akteurs (aus dem JWT). Pflicht."
                    },
                    "createdAt": {
                        "type": "string",
                        "format": "date-time"
                    },
                    "deliveryId": {
                        "type": "string",
                        "format": "uuid"
                    },
                    "id": {
                        "type": "string",
                        "format": "uuid"
                    },
                    "imageAttachment": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Referenz auf einen Bild-Anhang (z. B. Object-Storage-Key/URL)."
                    },
                    "text": {
                        "type": [
                            "string",
                            "null"
                        ]
                    }
                }
            },
            "DeliveryNoteResponse": {
                "type": "object",
                "required": [
                    "note"
                ],
                "properties": {
                    "note": {
                        "$ref": "#/components/schemas/DeliveryNote"
                    }
                }
            },
            "DeliveryOrderEntry": {
                "type": "object",
                "required": [
                    "deliveryId",
                    "sortOrder"
                ],
                "properties": {
                    "deliveryId": {
                        "type": "string",
                        "format": "uuid"
                    },
                    "sortOrder": {
                        "type": "integer",
                        "format": "int32"
                    }
                }
            },
            "DeliveryResponse": {
                "type": "object",
                "required": [
                    "delivery"
                ],
                "properties": {
                    "delivery": {
                        "$ref": "#/components/schemas/Delivery"
                    }
                }
            },
            "DeliveryState": {
                "type": "string",
                "description": "Lebenszyklus einer Lieferung.\n\n`Held` ist f\u00fcr \u201eheute nicht zustellbar, aber nicht endg\u00fcltig abgesagt\"\nreserviert; `Canceled` ist endg\u00fcltig. `Completed` setzt der\nAbschluss-Flow am Ende der Auslieferung.",
                "enum": [
                    "active",
                    "held",
                    "canceled",
                    "completed"
                ]
            },
            "DeliveryWithItems": {
                "allOf": [
                    {
                        "$ref": "#/components/schemas/Delivery"
                    },
                    {
                        "type": "object",
                        "required": [
                            "sortOrder",
                            "items"
                        ],
                        "properties": {
                            "items": {
                                "type": "array",
                                "items": {
                                    "$ref": "#/components/schemas/DeliveryItem"
                                }
                            },
                            "sortOrder": {
                                "type": "integer",
                                "format": "int32",
                                "description": "Sortier-Reihenfolge innerhalb der Tour (1-basiert)."
                            }
                        }
                    }
                ]
            },
            "HoldDeliveryRequest": {
                "type": "object",
                "required": [
                    "reason"
                ],
                "properties": {
                    "reason": {
                        "type": "string"
                    }
                }
            },
            "ScanEvent": {
                "type": "object",
                "required": [
                    "clientScanId",
                    "deliveryItemId",
                    "action",
                    "clientScannedAt"
                ],
                "properties": {
                    "action": {
                        "$ref": "#/components/schemas/AuditAction"
                    },
                    "actorCarId": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "uuid",
                        "description": "Fahrzeug, in dem der Scan gemacht wurde. Muss zum\nangemeldeten Account geh\u00f6ren. `None` ist erlaubt, schw\u00e4cht\naber den Audit-Trail."
                    },
                    "clientScanId": {
                        "type": "string",
                        "format": "uuid"
                    },
                    "clientScannedAt": {
                        "type": "string",
                        "format": "date-time"
                    },
                    "deliveryItemId": {
                        "type": "string",
                        "format": "uuid"
                    },
                    "reason": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Pflicht bei `Hold` und `Remove`. Sonst ignoriert."
                    }
                }
            },
            "ScanResult": {
                "type": "object",
                "required": [
                    "clientScanId",
                    "status"
                ],
                "properties": {
                    "clientScanId": {
                        "type": "string",
                        "format": "uuid"
                    },
                    "deliveryItemId": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "format": "uuid",
                        "description": "Aktueller `scan_state` der Position nach der Verarbeitung \u2014\ngenau dann gesetzt, wenn der Server den Stand kennen konnte\n(`Applied` oder `Duplicate`). Erlaubt der App, die UI ohne\nRe-Fetch zu aktualisieren."
                    },
                    "newScanState": {
                        "oneOf": [
                            {
                                "type": "null"
                            },
                            {
                                "$ref": "#/components/schemas/ScanState"
                            }
                        ]
                    },
                    "reason": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Bei `Rejected`: Begr\u00fcndung. Bei `Applied`/`Duplicate`: `None`."
                    },
                    "status": {
                        "$ref": "#/components/schemas/ScanResultStatus"
                    }
                }
            },
            "ScanResultStatus": {
                "type": "string",
                "enum": [
                    "applied",
                    "duplicate",
                    "rejected"
                ]
            },
            "ScanState": {
                "type": "object",
                "description": "Eingebetteter Scan-Zustand pro [`DeliveryItem`]. Wird durch\n`ScanAuditEntry`-Events fortgeschrieben \u2014 das Audit-Log ist die\nWahrheit \u00fcber das WIE und WANN, dieses Embedded-VO ist die schnelle\nWahrheit \u00fcber das WIEVIEL.",
                "required": [
                    "scannedQuantity",
                    "status",
                    "lastUpdatedAt"
                ],
                "properties": {
                    "heldReason": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Grund bei `status == Held` oder `status == Removed`."
                    },
                    "lastUpdatedAt": {
                        "type": "string",
                        "format": "date-time"
                    },
                    "scannedQuantity": {
                        "type": "integer",
                        "format": "int32"
                    },
                    "status": {
                        "$ref": "#/components/schemas/ScanStatus"
                    }
                }
            },
            "ScanStatus": {
                "type": "string",
                "description": "Status einer einzelnen Scan-Position innerhalb eines Items.",
                "enum": [
                    "in_progress",
                    "done",
                    "held",
                    "removed"
                ]
            },
            "SetDeliveryOrderRequest": {
                "type": "object",
                "required": [
                    "deliveryIds"
                ],
                "properties": {
                    "deliveryIds": {
                        "type": "array",
                        "items": {
                            "type": "string",
                            "format": "uuid"
                        },
                        "description": "Reihenfolge: Position im Array (0-basiert) wird zu `sort_order`\n(1-basiert) gemappt."
                    }
                }
            },
            "SetDeliveryOrderResponse": {
                "type": "object",
                "required": [
                    "tourId",
                    "order"
                ],
                "properties": {
                    "order": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/DeliveryOrderEntry"
                        }
                    },
                    "tourId": {
                        "type": "string",
                        "format": "uuid"
                    }
                }
            },
            "SyncDelivery": {
                "type": "object",
                "required": [
                    "belegartId",
                    "belegnummer",
                    "erpCustomerId",
                    "customerName",
                    "customerAddress",
                    "deliveryAddress",
                    "sortOrder",
                    "items"
                ],
                "properties": {
                    "belegartId": {
                        "type": "integer",
                        "format": "int64"
                    },
                    "belegnummer": {
                        "type": "string"
                    },
                    "customerAddress": {
                        "$ref": "#/components/schemas/Address"
                    },
                    "customerName": {
                        "type": "string"
                    },
                    "deliveryAddress": {
                        "$ref": "#/components/schemas/Address",
                        "description": "Snapshot der Lieferadresse (kann von der Stammadresse abweichen)."
                    },
                    "desiredTime": {
                        "type": [
                            "string",
                            "null"
                        ]
                    },
                    "erpCustomerId": {
                        "type": "integer",
                        "format": "int64"
                    },
                    "items": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/SyncDeliveryItem"
                        }
                    },
                    "sortOrder": {
                        "type": "integer",
                        "format": "int32",
                        "description": "1-basiert, definiert die initiale Reihenfolge in der App."
                    },
                    "specialAgreements": {
                        "type": [
                            "string",
                            "null"
                        ]
                    }
                }
            },
            "SyncDeliveryItem": {
                "type": "object",
                "required": [
                    "belegzeilenNr",
                    "articleNumber",
                    "articleName",
                    "articleScannable",
                    "warehouseCode",
                    "warehouseName",
                    "requiredQuantity"
                ],
                "properties": {
                    "articleDefaultWarehouseCode": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Default-Lager-Code f\u00fcr den Artikel (Anlage neuer Artikel)."
                    },
                    "articleName": {
                        "type": "string"
                    },
                    "articleNumber": {
                        "type": "string"
                    },
                    "articleScannable": {
                        "type": "boolean"
                    },
                    "belegzeilenNr": {
                        "type": "integer",
                        "format": "int32"
                    },
                    "komponentenArtikelNr": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Komponenten-Artikelnummer bei aufgel\u00f6sten St\u00fccklisten, sonst leer."
                    },
                    "requiredQuantity": {
                        "type": "integer",
                        "format": "int32"
                    },
                    "warehouseCode": {
                        "type": "string"
                    },
                    "warehouseName": {
                        "type": "string"
                    }
                }
            },
            "SyncTourRequest": {
                "type": "object",
                "required": [
                    "driverPersonalnummer",
                    "tourDate",
                    "deliveries"
                ],
                "properties": {
                    "deliveries": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/SyncDelivery"
                        }
                    },
                    "driverPersonalnummer": {
                        "type": "integer",
                        "format": "int64"
                    },
                    "tourDate": {
                        "type": "string",
                        "format": "date"
                    }
                }
            },
            "SyncTourResponse": {
                "type": "object",
                "description": "Antwort-H\u00fclle f\u00fcr `POST /sync/tour`.",
                "required": [
                    "tourId"
                ],
                "properties": {
                    "tourId": {
                        "type": "string",
                        "format": "uuid"
                    }
                }
            },
            "Tour": {
                "type": "object",
                "description": "Tour eines Tages, pro [`crate::domain::Account`]. Aggregat-Wurzel\nf\u00fcr die Lieferungen dieses Tages \u2014 die einzelnen [`crate::domain::Delivery`]\nreferenzieren ihre Tour per FK.\n\nDer Sync vom ERP l\u00e4uft in der Regel einmal am Vortag und f\u00fcllt eine\nneue Tour-Zeile inklusive Delivery- und DeliveryItem-Strukturen.",
                "required": [
                    "id",
                    "accountId",
                    "date",
                    "syncedAt"
                ],
                "properties": {
                    "accountId": {
                        "type": "integer",
                        "format": "int64"
                    },
                    "date": {
                        "type": "string",
                        "format": "date"
                    },
                    "id": {
                        "type": "string",
                        "format": "uuid"
                    },
                    "syncedAt": {
                        "type": "string",
                        "format": "date-time",
                        "description": "Zeitpunkt des letzten ERP-Sync \u2014 f\u00fcr Drift-Erkennung."
                    }
                }
            },
            "TourDetails": {
                "type": "object",
                "required": [
                    "tour",
                    "deliveries",
                    "customers",
                    "customerContacts",
                    "articles",
                    "warehouses",
                    "notes"
                ],
                "properties": {
                    "articles": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/Article"
                        }
                    },
                    "customerContacts": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/CustomerContact"
                        }
                    },
                    "customers": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/Customer"
                        }
                    },
                    "deliveries": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/DeliveryWithItems"
                        }
                    },
                    "notes": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/DeliveryNote"
                        },
                        "description": "Alle Notizen aller Lieferungen dieser Tour, in einer Liste.\nDie App joint clientseitig per `delivery_id`. Reihenfolge:\npro Lieferung aufsteigend nach `created_at`."
                    },
                    "tour": {
                        "$ref": "#/components/schemas/Tour"
                    },
                    "warehouses": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/Warehouse"
                        }
                    }
                }
            },
            "TourSummary": {
                "type": "object",
                "required": [
                    "tourId",
                    "tourDate",
                    "deliveryCount"
                ],
                "properties": {
                    "deliveryCount": {
                        "type": "integer",
                        "format": "int64"
                    },
                    "tourDate": {
                        "type": "string",
                        "format": "date"
                    },
                    "tourId": {
                        "type": "string",
                        "format": "uuid"
                    }
                }
            },
            "TourSummaryList": {
                "type": "object",
                "description": "Antwort-H\u00fclle f\u00fcr `GET /me/tours/today`. Eigenes Struct, weil\nutoipa f\u00fcr `Vec<T>` als Top-Level-Response keinen sauberen\nSchemanamen vergibt \u2014 und ein Wrapper macht die Erweiterbarkeit\n(z. B. Paginierung in Zukunft) zur Nicht-Breaking-Change.",
                "required": [
                    "tours"
                ],
                "properties": {
                    "tours": {
                        "type": "array",
                        "items": {
                            "$ref": "#/components/schemas/TourSummary"
                        }
                    }
                }
            },
            "UpdateCarRequest": {
                "type": "object",
                "properties": {
                    "active": {
                        "type": [
                            "boolean",
                            "null"
                        ],
                        "description": "Wenn gesetzt: aktiv/inaktiv. Inaktive Fahrzeuge tauchen in\n`GET /me/cars?activeOnly=true` (default) nicht auf."
                    },
                    "plate": {
                        "type": [
                            "string",
                            "null"
                        ],
                        "description": "Wenn gesetzt: neues Kennzeichen."
                    }
                }
            },
            "Warehouse": {
                "type": "object",
                "description": "Lager. ERP-Mirror; `code` ist die ERP-Lager-Nr (z. B. `\"0\"` f\u00fcr das\nStandardlager). Das `is_standard`-Flag ist der schnelle Filter f\u00fcr\ndie Beladen-Logik (\u201enur Standardlager-Artikel z\u00e4hlen f\u00fcr Fertig\").",
                "required": [
                    "id",
                    "code",
                    "name",
                    "isStandard"
                ],
                "properties": {
                    "code": {
                        "type": "string"
                    },
                    "id": {
                        "type": "string",
                        "format": "uuid"
                    },
                    "isStandard": {
                        "type": "boolean"
                    },
                    "name": {
                        "type": "string"
                    }
                }
            }
        },
        "securitySchemes": {
            "bearer_auth": {
                "type": "http",
                "scheme": "bearer",
                "bearerFormat": "JWT"
            }
        }
    },
    "security": [
        {
            "bearer_auth": []
        }
    ],
    "tags": [
        {
            "name": "health",
            "description": "Health- und Status-Endpoints"
        },
        {
            "name": "accounts",
            "description": "Account-Stammdaten"
        },
        {
            "name": "tours",
            "description": "Touren der Fahrer"
        },
        {
            "name": "sync",
            "description": "ERP-Sync-Endpunkte"
        },
        {
            "name": "scans",
            "description": "Scan-Events (Beladung & Auslieferung)"
        },
        {
            "name": "deliveries",
            "description": "Delivery-Lifecycle (hold / resume / cancel / complete)"
        },
        {
            "name": "cars",
            "description": "Fahrzeug-Stammdaten pro Fahrer"
        }
    ]
}
