Skip to main content

commerce_types/
transport.rs

1use chrono::{DateTime, Utc};
2use schemars::JsonSchema;
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5
6use crate::core::*;
7
8/// Data model for an incoming transportation request
9///
10/// This is identical to [`TransportationRqState`], but has no events, the rate is not set (as this is requested),
11/// and the pickup and delivery timing are only a request (as nothing is planned yet).
12#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
13pub struct TransportationRfq {
14    /// Your unique ID (tender/booking)
15    pub request_id: String,
16
17    /// Mode and service flavor (e.g., FTL/LTL/Express)
18    pub mode: TransportMode,
19    pub service_level: Option<String>,
20
21    /// Equipment needs (container/truck/trailer etc.)
22    pub equipment: Option<Equipment>,
23
24    /// "Ship-From" / origin
25    pub consignor: Address,
26    /// "Ship-To" / final destination
27    pub consignee: Address,
28
29    /// Optional intermediate stops / milk run
30    pub stops: Vec<LegStop>,
31
32    /// Shipment synopsis
33    pub cargo: CargoSummary,
34
35    /// High-level pickup/delivery lifecycle timings
36    pub requested_pickup: TimeWindow,
37    pub requested_delivery: TimeWindow,
38
39    /// Extras beyond “drive from A to B” (liftgate, hazardous, temperature control, etc.)
40    pub accessorials: Vec<Accessorial>,
41
42    /// Business references (PO, Delivery, Booking, BOL, SSCC, etc.)
43    pub references: HashMap<String, String>,
44}
45
46/// The state of a transport service request/tender/booking (A→B or multi-stop)
47#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
48pub struct TransportationRqState {
49    /// Your unique ID (tender/booking)
50    pub request_id: String,
51
52    /// Current lifecycle state
53    pub status: TransportStatus,
54
55    /// Mode and service flavor (e.g., FTL/LTL/Express)
56    pub mode: TransportMode,
57    pub service_level: Option<String>,
58
59    /// Equipment needs (container/truck/trailer etc.)
60    pub equipment: Option<Equipment>,
61
62    /// "Ship-From" / origin
63    pub consignor: Address,
64    /// "Ship-To" / final destination
65    pub consignee: Address,
66
67    /// Optional intermediate stops / milk run
68    pub stops: Vec<LegStop>,
69
70    /// Shipment synopsis
71    pub cargo: CargoSummary,
72
73    /// High-level pickup/delivery lifecycle timings
74    pub pickup: StepTiming,
75    pub delivery: StepTiming,
76
77    /// Extras beyond “drive from A to B” (liftgate, hazardous, temperature control, etc.)
78    pub accessorials: Vec<Accessorial>,
79
80    /// Business references (PO, Delivery, Booking, BOL, SSCC, etc.)
81    pub references: HashMap<String, String>,
82
83    /// Quoted/awarded rate (flat, per hour/km/container — via UnitRate)
84    pub rate: Option<UnitRate>,
85
86    /// Execution updates (optional stream of events)
87    pub events: Vec<TransportEvent>,
88}
89
90impl From<TransportationRfq> for TransportationRqState {
91    fn from(value: TransportationRfq) -> Self {
92        TransportationRqState {
93            request_id: value.request_id,
94            status: TransportStatus::RfqSent,
95            mode: value.mode,
96            service_level: value.service_level,
97            equipment: value.equipment,
98            consignor: value.consignor,
99            consignee: value.consignee,
100            stops: value.stops,
101            cargo: value.cargo,
102            pickup: StepTiming::requested(value.requested_pickup),
103            delivery: StepTiming::requested(value.requested_delivery),
104            accessorials: value.accessorials,
105            references: value.references,
106            rate: None,
107            events: Vec::new(),
108        }
109    }
110}
111
112/// Defines where you are in the request→execution flow
113#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
114pub enum TransportStatus {
115    /// Built, but not sent
116    Draft,
117    /// RFQ/Tender sent to providers
118    RfqSent,
119    /// Provider placed his bid
120    BidSent,
121    /// Provider selected
122    Awarded,
123    /// transport order/booking confirmed
124    Booked,
125    /// pickup done and en route
126    InTransit,
127    /// delivered (actuals available)
128    Completed,
129    /// Request / Transport cancelled
130    Cancelled,
131}
132
133#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, PartialEq, Eq, Copy)]
134pub enum TransportMode {
135    Road,
136    Air,
137    Ocean,
138    Rail,
139    Multimodal,
140}
141
142/// Requested vs planned vs estimated vs actual for one step (pickup or delivery)
143#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
144pub struct StepTiming {
145    /// What the shipper/transport user asked for
146    pub requested: Option<TimeWindow>,
147    /// What the carrier committed to after award/booking
148    pub planned: Option<TimeWindow>,
149    /// Rolling ETA during execution (single best estimate)
150    pub estimated: Option<DateTime<Utc>>,
151    /// Final actual timestamp
152    pub actual: Option<DateTime<Utc>>,
153}
154
155impl StepTiming {
156    pub fn requested(tw: TimeWindow) -> Self {
157        StepTiming {
158            requested: Some(tw),
159            planned: None,
160            estimated: None,
161            actual: None,
162        }
163    }
164
165    pub fn set_plan(&mut self, tw: TimeWindow) {
166        self.planned = Some(tw);
167    }
168
169    pub fn set_estimate(&mut self, date: DateTime<Utc>) {
170        self.estimated = Some(date);
171    }
172
173    pub fn set_actual(&mut self, date: DateTime<Utc>) {
174        self.actual = Some(date);
175    }
176}
177
178/// A time window (site availability / appointment window)
179#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
180pub struct TimeWindow {
181    pub earliest_utc: DateTime<Utc>,
182    pub latest_utc: DateTime<Utc>,
183}
184
185/// An intermediate stop (for multi-pick/drop). Each stop can model both arrival & departure.
186#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
187pub struct LegStop {
188    pub sequence: u32,
189    pub location: Address,
190    /// Arrival lifecycle timings at this stop
191    pub arrival: Option<StepTiming>,
192    /// Departure lifecycle timings at this stop
193    pub departure: Option<StepTiming>,
194    /// Optional notes / instructions specific to this stop
195    pub notes: Option<String>,
196}
197
198/// What you’re moving (high-level)
199#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
200pub struct CargoSummary {
201    /// Number of pieces ( pieces being cartons / pallets / containers / etc. )
202    pub pieces: u32,
203    /// Weight in kilogram
204    pub gross_weight_kg: f32,
205    /// Volume in cubic meters
206    pub volume_m3: Option<f32>,
207    /// Weather or not the cargo contains dangerous goods (ADR/IMDG/etc.)
208    pub dangerous_goods: bool,
209    /// Optional short description
210    pub commodity_description: Option<String>,
211    /// Optional standardized codes (e.g., HS code), free-form map
212    pub codes: Option<HashMap<String, String>>,
213}
214
215/// Equipment / container / trailer requirements
216#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
217pub struct Equipment {
218    /// Examples: "Box Truck 7.5t", "13.6m tautliner", "40HC", "20DV", "Reefer Trailer"
219    pub type_code: String,
220    /// Temperature range if controlled transport is needed (min, max)
221    pub temperature_c: Option<(f32, f32)>,
222    /// Free slots for axle/weight/class, door type, etc.
223    pub extrinsic: Option<HashMap<String, String>>,
224}
225
226/// Accessorial services (used to add cost / constraints on the delivery)
227#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
228pub enum Accessorial {
229    /// Liftgate
230    TailLift,
231    InsidePickup,
232    InsideDelivery,
233    /// Requires time-slot booking
234    Appointment,
235    /// Hazardous material handling
236    Hazardous,
237    /// Temperature managed transport
238    TempControl,
239    /// Brokerage / Clearance service
240    Customs,
241    /// Non-Commercial or limited access
242    Residential,
243    /// Billable waiting time
244    Detention,
245    /// Special handling / installation
246    WhiteGlove,
247    /// Any other option, represented as string
248    Other(String),
249}
250
251/// Execution/visibility event (optional stream)
252#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
253pub struct TransportEvent {
254    pub code: TransportEventCode,
255    pub timestamp: DateTime<Utc>,
256    /// Who reported it (carrier/TSP, telematics, port, visibility provider…)
257    pub source: Option<String>,
258    pub note: Option<String>,
259}
260
261#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
262pub enum TransportEventCode {
263    PickupPlanned,
264    PickupEtaUpdated,
265    PickupActual,
266    DepartureActual,
267    ArrivalEtaUpdated,
268    ArrivalActual,
269    DeliveryPlanned,
270    DeliveryEtaUpdated,
271    DeliveryActual,
272    ProofOfDeliveryAvailable,
273    Exception,
274}