//! Port für Fahrzeug-Stammdaten. //! //! Alle Lese- und Schreibzugriffe gehen über `account_id` (= //! Personalnummer aus dem JWT). Es gibt **keine** Methode, die //! Fahrzeuge ohne Account-Filter zurückgibt — so ist die Isolation //! zwischen Subunternehmer-Accounts strukturell garantiert. use async_trait::async_trait; use uuid::Uuid; use holzleitner_domain::Car; use crate::error::ApplicationError; #[async_trait] pub trait CarRepository: Send + Sync { /// Liste der Fahrzeuge eines Accounts. `include_inactive = false` /// blendet deaktivierte Fahrzeuge aus (Default für die App). async fn find_by_account( &self, personalnummer: i64, include_inactive: bool, ) -> Result, ApplicationError>; /// Sucht ein Fahrzeug, das einem Account gehört. `None` wenn es /// das Fahrzeug nicht gibt **oder** zu einem anderen Account /// gehört — beides sieht für den Caller gleich aus, das ist /// gewollt (kein Account-Probing). async fn find_by_id_for_account( &self, car_id: Uuid, personalnummer: i64, ) -> Result, ApplicationError>; async fn create( &self, personalnummer: i64, plate: &str, ) -> Result; /// Optional-Patch. `None` heißt "unverändert". async fn update( &self, car_id: Uuid, personalnummer: i64, plate: Option<&str>, active: Option, ) -> Result; /// Validiert, dass alle übergebenen IDs zum Account gehören. Nutzt /// der Use-Case beim Bulk-Scan, um die `actor_car_id`-Werte /// einmalig vor dem Verarbeiten zu prüfen. async fn assert_owned_by_account( &self, car_ids: &[Uuid], personalnummer: i64, ) -> Result<(), ApplicationError>; }