67 lines
2.5 KiB
Rust
67 lines
2.5 KiB
Rust
//! 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<String>,
|
|
}
|
|
|
|
/// 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<ShipmentResult> {
|
|
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"];
|