//! Outbound carrier integrations for creating shipments. //! //! Shipments are **never created automatically**. An admin reviews an order and, //! once the goods are physically ready, explicitly triggers //! [`create_shipment`] from the order page. Only then does the eshop call the //! carrier's API. `orders::place` (checkout) does not touch any of this. //! //! Each delivery option (`shipping_methods.carrier`) maps to one carrier here. //! "none" means the option is fulfilled manually and has no API. pub mod dhl; pub mod dpd; pub mod packeta; use loco_rs::prelude::*; /// Everything a carrier needs to register a parcel, snapshotted from an order. pub struct ShipmentRequest<'a> { pub order_number: &'a str, pub recipient_name: &'a str, pub email: &'a str, pub phone: Option<&'a str>, pub address: Option<&'a str>, pub city: Option<&'a str>, pub zip: Option<&'a str>, pub country: Option<&'a str>, /// Carrier pickup-point / locker id, when the method requires one. pub pickup_point_id: Option<&'a str>, /// Cash-on-delivery amount in cents; `0` when payment is not COD. pub cod_cents: i64, pub currency: &'a str, /// Total order value in cents (for insurance / customs declarations). pub value_cents: i64, pub weight_grams: i32, } /// What a carrier returns once the parcel is registered. pub struct ShipmentResult { /// Carrier-internal shipment/packet id. pub shipment_id: String, /// Public tracking number / barcode shown to the customer. pub tracking_number: String, /// Direct link to the shipping label PDF, if the carrier returns one. pub label_url: Option, } /// Dispatch to the carrier named by `shipping_methods.carrier`. Returns an error /// for `"none"` (manual fulfilment) or an unknown carrier. pub async fn create_shipment( ctx: &AppContext, carrier: &str, req: ShipmentRequest<'_>, ) -> Result { match carrier { "packeta" => packeta::create_shipment(ctx, req).await, "dpd" => dpd::create_shipment(ctx, req).await, "dhl" => dhl::create_shipment(ctx, req).await, "none" | "" => Err(Error::BadRequest( "this delivery option is fulfilled manually (no carrier API)".to_string(), )), other => Err(Error::BadRequest(format!("unknown carrier '{other}'"))), } } /// The carrier values offered in the admin UI. `none` is the manual default. pub const CARRIERS: [&str; 4] = ["none", "packeta", "dpd", "dhl"];